Hibernate sequence nextVal resolved but not used (Oracle) - java

Hibernate is not assigning objects ids from the Oracle sequence as expected. Here's what I see in the Hibernate debug logs.
DEBUG o.h.SQL:92 - select MY_SEQ.nextval from dual
DEBUG o.h.i.e.SequenceStructure:102 - Sequence value obtained: 22643
DEBUG o.h.r.j.i.ResourceRegistryStandardImpl:73 - HHH000387: ResultSet's statement was not registered
DEBUG o.h.e.i.AbstractSaveEventListener:118 - Generated identifier: 22594, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
The first "sequence value obtained" is correct, 22643 comes right from MY_SEQ.nextVal as it says. But then the "generated identifier" that gets used is 22594. What gives?
I've tried tweaking the generator strategy to no avail.
#Id
#SequenceGenerator(name = "generator", sequenceName = "MY_SEQ")
#GeneratedValue(generator = "generator", strategy = GenerationType.SEQUENCE)
#Column(name = "MY_ID", nullable = false, precision = 6, scale = 0)
private Integer id;
I can include my Spring Hibernate context configuration if that'll help. I don't see anything in there that looks obviously relevant.
Hibernate with Oracle sequence doesn't use it is very probably related, but that deals with gaps while the id I get is less than the sequence value obtained.
PS: Other tickets discuss sequence generator strategies that optimize efficiency. A single record of this data is inserted once a month or so, and only from this class. So, efficiency isn't a concern here.
Update 1
I am able to recreate this in HSQLDB in Oracle emulation mode too. So it's surely a Hibernate issue.
Update 2
The offset is always exactly 49. The example above correctly fetched 22643 from the sequence, but then resolved 22594 as the next value.
22643-22594=49
In another example, the next sequence value was actually 4, and Hibernate gave me -45.
4-(-45)=49
Update 3
Subsequent inserts do not call the Oracle sequence's nextVal. I suspect JPA/Hibernate is trying to fetch ids in bulk upfront for efficiency.
DEBUG o.h.SQL:92 - select MY_SEQ.nextval from dual
DEBUG o.h.i.e.SequenceStructure:102 - Sequence value obtained: 22643
DEBUG o.h.r.j.i.ResourceRegistryStandardImpl:73 - HHH000387: ResultSet's statement was not registered
DEBUG o.h.e.i.AbstractSaveEventListener:118 - Generated identifier: 22594, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
...
DEBUG o.h.e.i.AbstractSaveEventListener:118 - Generated identifier: 22595, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator

As I mentioned in my 3rd update, JPA was "fetching 50 ids" from the sequence up front and counting through them in memory for efficiency.
This behavior is specified by javax.persistence.SequenceGenerator.allocationSize which defaults to 50.
(Optional) The amount to increment by when allocating sequence numbers from the sequence.
This isn't at all intuitive to me, or others, since my Oracle database sequence is supposed to define this behavior and 50 isn't a standard default there.
The quick and dirty solution was to specify allocationSize=1:
#SequenceGenerator(name = "generator", sequenceName = "MY_SEQ",
allocationSize = 1)
Now the Oracle sequence is incremented for every insert.

Related

Hibernate Generation Type for SQL Server and Others

I wrote program that uses different databases like sql server, oracle etc. My problem is that I can't handle GenerationType and insert correct row into table. Using GenerationType.AUTO and hibernate.id.new_generator_mappings := false in sql server, my program is able to insert new row into table, but ID is always null, same problem is when GenerationType is IDENTITY.
I tried to add auto-incrementation only for sql server, but Liquibase yells at me that it's not supported for mssql. When I use Sequences for Oracle as well SQL Server my program is trying to get "next value" from generator but it cannot and do infinite loop. Even if I set default value for ID it won't increment this value.
Thats my code :
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "name")
#SequenceGenerator(name = "name", sequenceName = "SEQ", allocationSize = 1)
private Long id;
I would like to be able to add auto-incrementing indices into table and it should work for SQL Server databases and I don't want to use Table strategy for generation because it needs additional table in db.
Problem solved. I add condition in Liquibase xml file that checks whether db is mssql type and if it's true script drops ID column and adds it with IDENTITY(1,1) option.
The only problem is that now I have to switch aforementioned "hibernate.id.new_generator_mappings" setting.

How force hibernate to sync sequences?

I try to prepare an integration test with test data. I read insert queries from an external file and execute them as native queries. After the insertions I execute select setval('vlan_id_seq', 2000, true );. Here is the entity ID definition:
#Id
#Column(name = "id", unique = true, nullable = false)
#GeneratedValue(strategy = IDENTITY)
private Integer id;
When I try tor persist a new entry, I got a Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "vlan_pkey"
Detail: Key (id)=(1) already exists. exception. The ID of the sequence is 2000. The column definition is done by the serial macro and is id integer NOT NULL DEFAULT nextval('vlan_id_seq'::regclass).
I executed the native queries in a user transaction, so all test entries are stored in the postgresql data base, but it seems that hibernate not sync the sequence. The entityManager.flush(); also didn't force a sequence synchronisation. It seems that hibernate did not use sequences with #GeneratedValue(strategy = IDENTITY). I use a XA-Datasource and wildfly 13.
I tested now an other initialisation method. I defined a SQL data script (I generated the script with Jailer) in the persitence.xml (javax.persistence.sql-load-script-source) and end the script with select pg_catalog.setval('vlan_id_seq', (SELECT max(id) FROM vlan), true );. I set a breakpoint before the first persist command, check the sequence in the postgresql db, the sequence has the max id value 16. Now persisting works and the entry has the id 17. The scripts are executed before the entity manager is started and hibernate read the the updated sequences while starting. But this solution did not answer my question.
Is there a possibility that hibernate reread the sequences to use the nextval value?
if the strategy is Identity this means hibernate will create a sequence table and fetch the IDs from it, by using native sql you are just inserting your own values without updating that table so you have TWO solutions
Insert using hibernate itself which will be fairly easy, in your
integration test inject your DAOs and let hibernate do the insertion
for you which is recommended so you do not need to rehandle what
hibernate already handled
Update the sequence table whenever you do the insert by increment the
value which I do not recommend.

Oracle sequence returns different value when issued from java hibernate vs sql developer

I am running into this really weird issue where an oracle sequence returns very different values when issued from java/Hibernate vs SqlDeveloper tool.
#SequenceGenerator(name = "PARAM_ID_SEQUENCE", initialValue = 10, allocationSize = 30, sequenceName = "PARAM_ID_SEQUENCE")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PARAM_ID_SEQUENCE")
#Column(name = "PARAMETER_ID")
The above returns 1177248345. I turned the loglevel to trace to see the actual query being issued and it is: select SPS_PARAM_ID_SEQUENCE.nextval from dual.
Now I issue the very same query against the same sequence from sql developer tool and it returns: 39241615 which is 2 orders of magnitude lesser! Also, this is the accurate value I would expect.
CREATED 03-OCT-14
LAST_DDL_TIME 25-JAN-18
SEQUENCE_OWNER API
SEQUENCE_NAME PARAM_ID_SEQUENCE
MIN_VALUE 1
MAX_VALUE 9999999999999999999999999999
INCREMENT_BY 1
CYCLE_FLAG N
ORDER_FLAG N
CACHE_SIZE 20
LAST_NUMBER 39241624
PARTITION_COUNT
SESSION_FLAG N
KEEP_VALUE N
I confirmed that both are issuing requests on the same environment against the same database schema etc, but for some reason Hibernate gets a much higher value. Has anyone faced a similar issue. Is hibernate "padding" the sequence somehow?

Should hibernate use unique sequences for each table?

I have several entities using AUTO key generation strategy with Hibernate and postgres.
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
This will result in a hibernate_sequence generated, and each entity will make use of that sequence when assigning keys.
Now I have a table that has lots of cache data (like 100k entries), and some user tables. As both use strategy AUTO, they both get their keys from the same hibernate sequence. As a result, even if I only have 10 users, they will all have an id of 6-7 digits long, like 123123.
I wonder if, in general, one should introduce a custom sequence for each table? Or shouldn't I care about the id generation that much?
I have recently solved this problem for my project. I use the Enhanced sequence generator (which is the default for sequence-style generators) and set the prefer_sequence_per_entity parameter to true.
Contents of my package-info.java:
#GenericGenerator(
name = "optimized-sequence",
strategy = "enhanced-sequence",
parameters = {
#Parameter(name="prefer_sequence_per_entity", value="true"),
#Parameter(name="optimizer", value="hilo"),
#Parameter(name="increment_size", value="50")})
package org.example.model;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
On the usage side you just need
#Id #GeneratedValue(generator="optimized-sequence")
public long id;
I prefer having separate sequences because occasionally I'll drop a table and recreate it, and I want the ID's starting from one.
You can use serial datatype for your useid , or use PostgreSQL sequence.
LIKE :
digoal=# create table tt(id serial, info text);
CREATE TABLE
digoal=# insert into tt (info) values ('test'),('test');
INSERT 0 2
digoal=# select * from tt;
id | info
----+------
1 | test
2 | test
(2 rows)
OR
digoal=# create table tt1(id int, info text);
CREATE TABLE
digoal=# create sequence seq_tt1;
CREATE SEQUENCE
digoal=# alter table tt1 alter column id set default nextval('seq_tt1'::regclass);
ALTER TABLE
digoal=# insert into tt1 (info) values ('test'),('test');
INSERT 0 2
digoal=# select * from tt1;
id | info
----+------
1 | test
2 | test
(2 rows)
Try with Sequence
First create sequence in postgres
CREATE SEQUENCE YOUR_ENTITY_SEQ;
In entity, use generation strategy as SEQUENCE and for next time gen value allocation set allocationSize as necessary
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "YOUR_ENTITY_SEQ")
#SequenceGenerator(name = "YOUR_ENTITY_SEQ", sequenceName = "YOUR_ENTITY_SEQ", allocationSize = 1)
private Long id;
This is just the preferred way for Hibernate to manage primary keys. It will generate the appropiate SQL idiom depending on your underlying database (a sequence for Oracle, and identity key field for DB2, etc.)
However, you can perfectly define composite keys if you feel they are more appropiate for your business. Someone gave a great explanation on this here in Stackoverflow:
How to map a composite key with Hibernate?

Hibernate throws PK violation error

I've been using hibernate with jboss 4.2.3 and everything was working, now i migrated the code to Jboss 7.1.1 and suddenly i start getting :
Caused by: org.hibernate.exception.ConstraintViolationException: ORA-00001: unique constraint (OBLICORE.PK_ACE_WORKERS_QUEUE_STATS_ID) violated
Also the generated ID's are negative.
The entity that fails is defined as such:
#Id
#SequenceGenerator(name = "SEQ_ACE_WORKERS_QUEUE_STATS_ID", sequenceName = "SEQ_ACE_WORKERS_QUEUE_STATS_ID", allocationSize = 500)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ACE_WORKERS_QUEUE_STATS_ID")
#Column(name = "ID")
private long Id;
I checked the sequence in Oracle and it seems O.K (Like i said, it worked before with jboss 4.2 and nothing changed on the DB side since the migration).
I tried writing Hibernate query logs but couldn't locate that query and I've also logged the specific call that persist this class and saw that it only get called once.
Check this question: hibernate oracle sequence produces large gap
It must be Hibernate's sequence generator which is defaulting to the Hi/Lo algorithm, and the returned values overflowing. You can try using a hibernate-specific annotation to default to the older behaviour GenericGenerator(name="blah", strategy="sequence"), or set allocationSize=1.
If you are relying on your sequence incrementing by some value larger than 1, you'll have to use a different generator. Or maybe it's enough to set hibernate.id.new_generator_mappings to false, but that is in the scope of a new question.
When we changed Hibernate to use the new generator, I used the following script to fix the sequences:
DECLARE
v NUMBER;
BEGIN
FOR r IN (select sequence_name from user_sequences) LOOP
EXECUTE IMMEDIATE 'ALTER SEQUENCE '|| r.sequence_name ||' INCREMENT BY 50';
EXECUTE IMMEDIATE 'SELECT '|| r.sequence_name ||' .NEXTVAL FROM DUAL' INTO v;
END LOOP;
END;
/
If your allocationSize is 500, you should change the "INCREMENT BY 50" to "INCREMENT BY 500".
If the generated value of id is not so critical in your project try to use #GeneratedValue(strategy = GenerationType.AUTO)
this strategy will generate id avtomatically by incrementing last by one. Hope it will useful for you.

Categories