Unique constraint violation on insertion after server restart - oracle

I am running a JBoss EAP 7 server with an Oracle 11g DB, and Hibernate for JPA. I have noticed something weird. When I first create the database and start the server, everything works fine. I send requests from the client and the server persists the data in the DB.
If I restart the server and try to do the same, I get a unique constraint violation exception for every request:
java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SCHEMA.SYS_C0010299) violated
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:447)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:396)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:951)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:513)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:227)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:531)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:208)
at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1046)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1336)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3613)
at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3694)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1354)
at org.jboss.jca.adapters.jdbc.WrappedPreparedStatement.executeUpdate(WrappedPreparedStatement.java:537)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.executeUpdate(ResultSetReturnImpl.java:204)
I checked the constraint in sqlplus with the query below. (I ran the query as the system user, not the same user as the server, if that matters).
SELECT A.TABLE_NAME,C.TABLE_NAME,COLUMN_NAME FROM ALL_CONS_COLUMNS A
JOIN ALL_CONSTRAINTS C ON A.CONSTRAINT_NAME = C.CONSTRAINT_NAME
WHERE C.CONSTRAINT_NAME = 'SYS_C0010299';
It seems to happen on the primary key of one of my tables. That primary key is generated with a sequence.
#Id
#Column(name="ID_COL")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "SEQ_NAME_GEN")
#SequenceGenerator(name = "SEQ_NAME_GEN", sequenceName = "SEQ_NAME")
private Long id;
If I create a fresh new DB, the application again works fine at first, until I restart the server. Why does this happen?
This is the relation of the entity class to another entity class:
// other class
#OneToMany(cascade=CascadeType.ALL, mappedBy="otherClass")
#MapKey(name = "mapKey")
private Map<MapKey, ConstraintViolationEntityClass>
map;
// problematic class (ConstraintViolationEntityClass)
#Column(name = "MAP_KEY")
#Enumerated(EnumType.ORDINAL)
private EnumType enumType;
#ManyToOne
#JoinColumn(name = "OTHER_CLASS_ID", nullable = false)
private OtherClass otherClass;
And this is the SQL code I used to create the table for the ConstraintViolationEntityClass:
create table schema.ConstraintViolationEntityTable (
id_col number(10) not null primary key,
map_key number(2) not null,
other_class_id number(10) not null,
constraint other_class_fk foreign key (other_class_id) references schema.other_class(id)
);
This is my persistence.xml:
<persistence-unit name="unit1" transaction-type="JTA">
<jta-data-source>java:jboss/OracleDS</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties>
<property name="hibernate.transaction.jta.platform" value="org.hibernate.service.jta.platform.internal.JBossStandAloneJtaPlatform"/>
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.hbm2ddl.auto" value="validate" />
</properties>
</persistence-unit>
For some reason, some of the primary keys of the rows inserted by the successful requests are negative. And checking dba_sequences, the last_number of the sequence is 43, even though the table only has 24 rows in it (12 rows added per client request)

As stated in the answer that PeterM linked to, the default allocation size for sequence generators is 50, which is the root cause of the problem, since you defined the sequence with an increment of 1. I'll just comment on the negative values issue.
An allocation size of 50 (set in SequenceGenerator.allocationSize) means that Hibernate will:
create the sequence with INCREMENT BY 50 (if you let it)
grab the next value n from the sequence
start allocating ids from n-50 till n
repeat the two steps above once it runs out of numbers
Since you've made the sequence increment by 1, it's easy to see where the negative values come from (and why constraint violations will follow). If you tried inserting more than 50 rows, you'd run into constraint violations without having to restart the server.

Related

Hibernate insert with id generation on insert trigger with sequence

Here my problem, I have a sequence in my oracle database and a trigger on insert to fetch the next value from the sequence and put it as id. With a tool like sql developer, it works perfectly.
My id is defined at this
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MY_SEQUENCE")
#SequenceGenerator(sequenceName = "MY_SEQUENCE", allocationSize = 1, name = "MY_SEQUENCE")
private BigInteger jobId;
The problem is hibernate firstly read the next value of the sequence, set it as the id and then persist it. Then my database update my id with the next value of the sequence but that new id isn't "updated" in my code after my .save(entity).
I read that I should use the GenerationType.IDENTITY but I would like to do batch inserts and I also read that with IDENTITY the batch inserts is not possible.
If possible, I would like to keep my trigger so like that hibernate doesn't have to call the database each time I insert and be able to do batch inserts.
Edit: I'll probably need to insert near a million of rows

Hibernate Jpa - constraint violation exception on Primary Key(Sequence)

I use Hibernate JPA in my application. I have a Table that has a Primary key(Sequence). Service inserts records to that table.
Version: Oracle 12c
Dialect: org.hibernate.dialect.Oracle10gDialect
Issue :
We face problem(Unique constraint violation on SEQUENCE Key) during the load testing.
Questions :
This issue is not occurring all the time. But only during load test. Can someone please check and help to use thread safe generator?
Is it DB side sequence definition issue or at Java side?
DB Sequence :
CREATE SEQUENCE MY_SEQ
START WITH 1
INCREMENT BY 1
NOMINVALUE
NOMAXVALUE
CACHE 30
NOORDER;
CREATE TABLE MY_TABLE (
MY_PRIMARY_KEY INT default MY_SEQ.nextval NOT NULL,
VALUE_COL VARCHAR2(10) NULL
);
Entity :
public class MyTableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "MY_PRIMARY_KEY")
#GenericGenerator(
        name = "mySequenceGenerator",
        strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
        parameters = {
                #Parameter(name = "sequence_name", value = "SEQUENCE MY_SEQ"),
                #Parameter(name = "increment_size", value = "1")
        }
)
#GeneratedValue(generator = "mySequenceGenerator")
private long myPrimaryKey;
#Column(name = "VALUE")
private String value;
}
Oracle 10 Dialect
For Oracle10gDialect use this configuration
#Id
#Column(name = "MY_PRIMARY_KEY")
#GeneratedValue(strategy=GenerationType.AUTO)
Long myPrimaryKey;
Hibernate creates a table and a sequence:
create table MY_TABLE (
MY_PRIMARY_KEY number(19,0) not null,
VALUE varchar2(255 char),
primary key (MY_PRIMARY_KEY))
create sequence hibernate_sequence
While storing it first gets the new sequence ID and than passes it in the INSERT statement
select hibernate_sequence.nextval from dual
insert into MY_TABLE (VALUE, MY_PRIMARY_KEY) values (?, ?)
Oracle 12 Dialect
If you use Oracle 12 that natively supports IDENTITY column it is prefered to upgrade to Oracle12cDialect (note that this requires Hibernate 5.3)
Set the strategy to GenerationType.IDENTITY
#Id
#Column(name = "MY_PRIMARY_KEY", updatable = false, nullable = false)
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long myPrimaryKey;
The following table is created - the important part is generated as identity which provides the unique velues.
Note that no explicite sequence is required to be created, it is managed internally .
create table MY_TABLE (
MY_PRIMARY_KEY number(19,0) generated as identity,
VALUE varchar2(255 char),
primary key (MY_PRIMARY_KEY))
While storing no ID is passed in the INSERT, it is assigned by Oracle and returned to the session
insert into MY_TABLE (VALUE) values (?) RETURNING MY_PRIMARY_KEY INTO ?
Note that in contrary to the Oracle 10 you save one round trip to the database.
Change long to Long
Because if you use long, it (by default) is 0. No generator is allowed to change existing values!
https://docs.oracle.com/javaee/7/api/javax/persistence/GeneratedValue.html

Spring Data/JPA PostgreSQL SequenceGenerator and duplicate primary keys on concurrent access to the same database

In my Spring Data/JPA project I use ProstgreSQL database.
I use a following mapping for my JPA entity PK on cards table:
#Id
#SequenceGenerator(name = "cards_id_seq", sequenceName = "cards_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "cards_id_seq")
private Long id;
Everything works fine until some other applications or persons do not insert manually new records into this table. Once it happens my application fails with the following error:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "cards_pkey"
Detail: Key (id)=(42) already exists.
when tries to insert records because of out of sync between PostgreSQL sequence object and actual PK IDs in the database.
What am I doing wrong and how to solve this situation in order to assign the correct IDs to new records inserted via my application ?

Primary Key Violation on JPA save method

I'm facing an annoying problem, which I think it's really stupid.
I'm using an H2 database with JPA and Hibernate.
This is my save method on ScheduleService.class:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public int save(Schedule sched) {
try {
scheduleRepository.save(sched);
} catch(Exception e) {...}
}
The sentence "scheduleRepository.save(sched)" is throwing the following exception:
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["UK_4R97H1MMVBOG9TD05OH9FKL16_INDEX_5 ON PUBLIC.SCHEDULE(CHANNEL, PROGRAMME, START, END) VALUES ( /* key:65574 */ null, TIMESTAMP '2015-09-15 17:00:00.0', null, TIMESTAMP '2015-09-15 16:40:00.0', 3, 13)"; SQL statement:
insert into schedule (channel, end, programme, published, start, idSched) values (?, ?, ?, ?, ?, ?) [23505-175]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
And printing to the output console the following message:
Unique index or primary key violation: "UK_4R97H1MMVBOG9TD05OH9FKL16_INDEX_5 ON PUBLIC.SCHEDULE(CHANNEL, PROGRAMME, START, END) VALUES ( /* key:65574 */ null, TIMESTAMP '2015-09-15 17:00:00.0', null, TIMESTAMP '2015-09-15 16:40:00.0', 3, 13)"; SQL statement:
insert into schedule (channel, end, programme, published, start, idSched) values (?, ?, ?, ?, ?, ?) [23505-175]
I've removed my #UniqueConstraint annotation in Schedule entity to make sure it is not a unique constraint problem.
The error persists, so it must be caused by a primary key violation.
This is how I define the primary key of Schedule entity:
#Id
#Column(name = "idSched", nullable = false)
#GeneratedValue(strategy = GenerationType.TABLE)
protected Long idSched;
It should be assigning a new unique idSched field for every row is inserted in the table, right?
What could be the problem which is causing the exception?
Thank you.
UPDATE:
Based on Bohuslav suggestion, I've changed the primary key declaration as this:
#Id
#TableGenerator(
name="scheduleGen",
table="schedule_sequence_table",
pkColumnValue="idSched",
allocationSize=1)
#GeneratedValue(strategy = GenerationType.TABLE, generator="scheduleGen")
#Column(name = "idSched", nullable = false)
protected Long idSched;
For the moment it seems to work fine.
UPDATE 2: Based on Sreenath comment:
I'm generating the tables only once (they're persisted as files on disk). Actually there was a unique key constraint on schedule table. However, since it was throwing that exception, I removed the constraint and removed database files to make sure the problem was not in the unique constraint but in the primary key instead. Even without the unique constraint and a brand new database, the exception still remained, so then I came with the #TableGenerator solution. Apparently that solved the problem.
But then I put my unique constraint again, removed database files so tables can be regenerated, and started and stopped the application a few times.
Now the problem has come again.
This is my unique constraint:
#Table(uniqueConstraints={#UniqueConstraint(columnNames = {"channel", "programme", "start", "end"})})
#Entity(name = "schedule")
public class Schedule { ...}
Of course, before I save an entity, I check if there is any other entity with the same unique members as the one I'm adding:
Schedule schedIn = schedRep.findOneByChannelAndProgrammeAndStartAndEnd(
sched.getChannel(), sched.getProgramme(), sched.getStart(), sched.getEnd());
// Check if there exists any schedule with the same {channel, programme, start, end} values
if (schedIn == null) {
// If not, then save it
schedRep.save(sched);
}
The method schedRep.findOneByChannelAndProgrammeAndStartAndEnd() is automatically generated by JPA. So that's weird, that query cannot find any schedule with the same {channel, programme, start, end} values, but when I try to save them, the unique constraint is violated.
Where could be the problem then?

Cannot retrieve the id of the last inserted row in Hibernate using Oracle

I'm using Hibernate Tools 3.2.1.GA with the Spring version 3.0.2. I'm trying to retrieve the id of the last inserted row into the Oracle(10g) database as follows.
Session session=NewHibernateUtil.getSessionFactory().getCurrentSession();
session.beginTransaction();
Country c=new Country();
c.setCountryId(new BigDecimal(0));
c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);
session.flush();
System.out.println(c.getCountryId());
session.getTransaction().commit();
This statement System.out.println(c.getCountryId()); is expected to return the currently inserted id after the data is serialized to the database and before the transaction is committed but it doesn't because of the following line in the preceding code snippet (as it presumably appears to me).
c.setCountryId(new BigDecimal(0));
I'm not sure why this statement is required in my case (while inserting). I saw this statement nowhere. Omission of this line causes the following exception to be thrown.
org.hibernate.id.IdentifierGenerationException: ids for this class
must be manually assigned before calling save(): model.Country
Is this statement c.setCountryId(new BigDecimal(0)); really required during insertion? It's a sequence generated primary key in the Oracle database and because of that line, this statement System.out.println(c.getCountryId()); always returns 0 which is actually expected to return the currently inserted id in the current session.
So, how can I get the last generated id in this case? Am I following a wrong way, is there a different way?
EDIT:
CREATE TABLE "COUNTRY"
(
"COUNTRY_ID" NUMBER(35,0) NOT NULL ENABLE,
"COUNTRY_CODE" VARCHAR2(10),
"COUNTRY_NAME" VARCHAR2(50),
"ZONE_ID" NUMBER(35,0),
CONSTRAINT "COUNTRY_PK" PRIMARY KEY ("COUNTRY_ID") ENABLE,
CONSTRAINT "COUNTRY_FK" FOREIGN KEY ("ZONE_ID")
REFERENCES "ZONE" ("ZONE_ID") ON DELETE CASCADE ENABLE
)
/
CREATE OR REPLACE TRIGGER "BI_COUNTRY"
before insert on "COUNTRY"
for each row
begin
select "COUNTRY_SEQ".nextval into :NEW.COUNTRY_ID from dual;
end;
/
ALTER TRIGGER "BI_COUNTRY" ENABLE
/
The exception 'ids for this class must be manually assigned before calling save()' means that you are using the identifier generation strategy of 'Assigned'.
assigned
lets the application assign an identifier to the object before save() is called. This is the default strategy if no element is specified.
If you do not define any strategy, hibernate defaults to 'assigned'. 'assigned' strategy implies that hibernate expects that the application supplies it's own ids.
If you want to use a sequence id generator in Oracle, you can do so with the following configuration -
If you are using xml -
<id name="countryId" type="java.lang.Integer">
<column name="Country_Id" />
<generator class="sequence">
<param name="sequence">Country_Id_Seq</param>
</generator>
</id>
If you are using annotations -
#Id
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="Country_Id_Seq")
#SequenceGenerator(name="Country_Id_Seq", sequenceName="Country_Id_Seq" )
private Integer sequence;
And your code should look like so -
Country c=new Country();
c.setCountryName(request.getParameter("txtCountryName"));
c.setCountryCode(request.getParameter("txtCountryCode"));
Zone z=(Zone) session.get(Zone.class, new BigDecimal(request.getParameter("zoneId")));
c.setZone(z);
session.save(c);
session.flush();
System.out.println(c.getCountryId());
When 'session.save(c)' executes, hibernate makes the following sql call to Oracle, retrieves the id and sets it in Country object.
select Country_Id_Seq.nextVal from dual;
Problem with trigger
Since you are using a trigger to increment the id when a row is inserted, this will cause a problem with hibernate sequence. Hibernate is using the sequence to generate an id and the database is using the trigger to increment the id. This is resulting in the id being incremented twice.
You have a three options to resolve this.
Delete the trigger because it's not necessary.
If you still need the trigger because the table could be updated outside the application, you could update the trigger such that the id is generated only if the id is not set in the insert statement
HIbernate issue with Oracle Trigger for generating id from a sequence
Create a custom id generator that uses the trigger to set the id in the data before it is saved to db. Check out the following link - https://forum.hibernate.org/viewtopic.php?t=973262
If the values into an ID column generated by a sequence, then you should associate that sequence with your ID column in the entity definition so that the attribute is filled in with the ID value by Hibernate during insertion.
Using annotations:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CountryIdSequence")
#SequenceGenerator(name = "CountryIdSequence", sequenceName = "COUNTRY_ID_SEQUENCE")
#Column(name = "COUNTRY_ID")
private BigDecimal countryId;
Using hbm:
<id name="countryId" type="big_decimal">
<column name="COUNTRY_ID" />
<generator class=""sequence">
<param name="sequence">COUNTRY_ID_SEQUENCE</param>
</generator>
</id>
Then, it will be available after the save.
Any changes made to the entity at the database layer are not reflected in the hibernate entity layer until you refresh the object.
session.save(c);
session.flush();
// Refresh the object for columns modified in the DB by IDENTITY / SEQUENCE / Triggers.
session.refresh(c);
System.out.println(c.getCountryId());

Resources