JPA sequence skipping values - java

I am using Open JPA 2.0, WebSphere V8 and database is DB2 V10.
Created sequence using below syntax
CREATE SEQUENCE "MYSCHEMA"."SEQ_TABLEA" AS INTEGER START WITH 1
INCREMENT BY 1 MINVALUE 1000 MAXVALUE 2147483647 NO CYCLE CACHE 100
ORDER;
My Entity class definition uses sequence as below
#Entity
#Table(name="MYSCHEMA.SEQ_TABLEA")
public class MyEntity implements Serializable {
#Id
#SequenceGenerator(name="TABLEA_ID_GENERATOR", sequenceName="MYSCHEMA.SEQ_TABLEA")
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="TABLEA_GENERATOR")
#Column(name="ID")
private Integer myId;
..
After multiple inserts to TABLEA over 3 to 4 days, I find that sequence number has skipped many values. Inserts with sequence created are like 1,2,3,100,101,102,103,104,105,200,201,202,300,301,302,303,304,305,306,307,308,309,310,400,.. and so on
Any view on what is going wrong?
I did not find any error while inserting, neither the DB was bounced during this period.

Try setting the allocation size on the #SequenceGenerator annotation. This specifies the amount to increment when allocating sequence numbers.
#SequenceGenerator(name="TABLEA_ID_GENERATOR",
sequenceName="MYSCHEMA.SEQ_TABLEA", allocationSize=1)
Java API
Good Article on Sequences in JPA

This is pretty much normal behavior. The Sequencer will pre-fetch/pre-resevre an interval of values (and save the interval params in the db), but then for some reason he'll not end up actually using (saving entities) for all of those values. Later when it gets to the last value of said interval, it will create a new one, without caring/re-checking if all values from previous interval have been used up or not.

Related

Hibernate GeneratorSequence fails after 50 entries

I have an entity with the following id configuration:
public class Publication implements Serializable, Identifiable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
}
with this generator (Liquibase syntax):
<createSequence incrementBy="10" sequenceName="sequence_generator" startValue="1" cacheSize="10"/>
and a Spring Data JPA Repository:
#Repository
public interface PublicationRepository extends JpaRepository<Publication, Long>, JpaSpecificationExecutor<Publication> {
// ...
}
Now I have part in my application where I create about 250 new Publication objects without an id and then do publicationRepository.saveAll(). I get the following exception:
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [mypackage.Publication#144651]
I debugged with breakpoints and found that this always happens with the 50th object, where the assigned ID suddenly is set as an ID that is already present in the set of already saved objects – so the generator seems to return the wrong value. For collections with less than 50 objects, it works fine.
What is also strange: The objects IDs created have an increase of 1, while if if execute NEXT VALUE FOR sequence_generator on my database i get IDs in increments of 10.
Am I using the generator wrong?
You need to sync SequenceGenerator's allocationSize with your sequence's incrementBy. The default value for allocationSize is 50, which means that after every 50th insert, the hibernate will generate select nextval('sequence_name)` (or something similar depending on the dialect), to get the next starting value for IDs.
What happens in your case is that:
for the first insert Hibernate fetches next value for the sequence, which is 1. By first insert I mean first insert whenever the service/application is (re)started.
then it performs 50 inserts (default allocationSize) without asking DB what is the next value for the sequence. Generated ID will be from 1 to 50.
51st insert fetches next value for the sequence, which is 11 (startBy + incrementBy). Previously you inserted an entity with ID=11, which is why it fails to insert the new entity (PK constraint violation).
Also, every time you call select nextval on sequence, it simply does currentValue + incrementBy. For your sequence, it'll be 1, 11, 21, 31, etc.
If you enable SQL logs, you'll see following:
Calling repository.save(entity) the first time would generate
select nextval('sequence_name`);
insert into table_name(...) values (...);
Saving second entity with repository.save(entity) would generate only
insert into table_name(...) values (...);
After allocationSize number of inserts you would again see:
select nextval('sequence_name`);
insert into table_name(...) values (...);
Advantage of using sequences is to minimize the number of times Hibernate would need to talk to the DB to get the next ID. Depending on your use-case, you can adjust the allocationSize to get the best results.
Note: one of the comments suggested to use allocationSize = 1 which is very bad and will have a huge impact on performance. For Hibernate that would mean that it needs to issue select nextval every time it performs an insert. In other words, you'll have 2 SQL statements for every insert.
Note 2: Also keep in mind that you need to keep initialValue of SequenceGenerator and startValue of sequence in sync as well. allocationSize and initialValue are the two values used by the sequence generator to calculate the next value.
Note 3: It worth mentioning that depending on the algorithm used to generate sequences (hi-lo, pooled, pooled-lo, etc.), gaps may occur between service/application restarts.
Useful resources:
Hibernate pooled and pooled-lo identifier generators - in case you wish to change the algorithm used by the sequence generator to calculate the next value. There might be the case (e.g. in a concurrent environment) where the two service use the same DB sequence to generate values and their generated values might collide. For cases like that, one strategy is better that the other.

OpenJPA Sequence generator with negative values

Environment: Websphere 8.5, OpenJPA 2.0, DB2 z/OS
There are two tables: one with verified data and another with draft data (staging table) + View that display information from both tables.
To avoid Primary Key clash I've decided that staging table will have negative values as a primary key. It was working in plain SQL, but my approach failed when I tried to define a generator for staging table in Java code
Generator for negative key was defined like this:
CREATE SEQUENCE X AS INTEGER START WITH -1 INCREMENT BY -1
MINVALUE -999999 MAXVALUE 0
On entity side:
#Id
#SequenceGenerator(name="X", sequenceName="X")
#GeneratedValue(strategy=GenerationType.SEQUENCE,generator="X")
#Column(name = "ID")`
First element was created successfully (with value -1), but insertion of second element failed with
THE RANGE OF VALUES FOR THE IDENTITY COLUMN OR SEQUENCE IS EXHAUSTED. SQLCODE=-359, SQLSTATE=23522
Can you help me define #SequenceGenerator? Is it possible under Open JPA 2.0? Maybe sequence definition was wrong (MINVALUE/MAXVALUE)
First, I think your option to do a 'START WITH -99999 INCREMENT BY 1' is the best option. I'm not sure why you feel it is "not pretty". If you do this:
CREATE SEQUENCE SEQ_MYSEQ AS INTEGER START WITH -99999 INCREMENT BY 1 MINVALUE -999999 MAXVALUE 0
You are still range bound between -99999 and 0, right?
As I'll explain below, I think OpenJPA and EclipseLink like to count up. So I think you'll have better luck with this.
That said, let me answer your opening question. I have ran a test on OpenJPA and EclipseLink (since WebSphere uses Ecliplselink in WAS v9 and Liberty). I can not get your scenario to work with EclipseLink, but could get it to work with OpenJPA (but it wasn't pretty). Let me state what I did: this is the SQL I defined my sequence with (just as you listed in the description):
CREATE SEQUENCE SEQ_MYSEQ AS INTEGER START WITH -1 INCREMENT BY -1 MINVALUE -999999 MAXVALUE 0;
I defined my sequence generator in my entity as:
#Id
#SequenceGenerator(name = "IDGENERATOR", sequenceName = "SEQ_MYSEQ", allocationSize = 1, initialValue = -1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "IDGENERATOR")
private int id;
Notice that I'm telling JPA to start at -1, and to use an allocationSize of 1. The JavaDocs state the default is 50. One bummer (but not a show stopper) with allocationSize of 1 is that the JPA provider will go to the database for each sequence value (i.e. local caching will not be used). However, if this is not used, it seems both OpenJPA and EclipseLink want to count up by the allocation size. It is hard coded to count up. That is, either one will ask the DB for the next value, and then count up from there by allocationSize, NOT count down. On OpenJPA, you need to use this property:
Otherwise, by default OpenJPA executes an 'ALTER SEQUENCE' to make certain the INCREMENT BY defined in the SequenceGenerator matches what is in the database. If I don't add this property, I get the same exception about the range exhaustion. Anyway, with this, all works well on OpenJPA. On EclipseLink, I get this exception:
Exception Description: The sequence named [SEQ_MYSEQ] is setup incorrectly. Its increment does not match its pre-allocation size.
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.persist(EntityManagerImpl.java:510)
at hat.test.MySeqTest.main(MySeqTest.java:28)
I didn't dig enough into EclipseLink to figure this out, but I did play around with the Sequence a bit and it seems like EclipseLink doesn't like negative values????
Thanks,
Heath Thomann

nextVal from PostgreSQL sequence in Hibernate fetch sequence multiple times

For business logic we need to update a record with the next value from a sequence defined directly in database and not in Hibernate (because is not always applied on insert / updates)
For this purpose we have a sequence defined in PostgreSQL witch DDL is:
CREATE SEQUENCE public.facturaproveedor_numeracionperiodofiscal_seq
INCREMENT 1 MINVALUE 1
MAXVALUE 9223372036854775807 START 1
CACHE 1;
Then in DAO, when some conditions are true, we get the nextVal via:
Query query = sesion.createSQLQuery("SELECT nextval('facturaproveedor_numeracionperiodofiscal_seq')");
Long siguiente = ((BigInteger) query.uniqueResult()).longValue();
But the values asigned aren't consecutive. Looking the Hibernate output log we see four fetchs to the sequence in the same transaction:
Hibernate: SELECT nextval('facturaproveedor_numeracionperiodofiscal_seq') as num
Hibernate: SELECT nextval('facturaproveedor_numeracionperiodofiscal_seq') as num
Hibernate: SELECT nextval('facturaproveedor_numeracionperiodofiscal_seq') as num
Hibernate: SELECT nextval('facturaproveedor_numeracionperiodofiscal_seq') as num
Why is this happening? Is this for catching purposes? There is a way to disable this? Or this workaround is not correct?
Hibernate won't usually generate standalone nextval calls that look like that, so I won't be too surprised if it's your application doing the multiple fetches. You'll need to collect more tracing information to be sure.
I think you may have a bigger problem though. If you care about sequences skipping values or leaving holes then you're using the wrong tool for the job, you should be using a counter in a table that you UPDATE, probably UPDATE my_id_generator SET id = id + 1 RETURNING id. This locks out concurrent transactions and also ensures that if the transaction rolls back, the update is undone.
Sequences by contrast operate in parallel, which means that it's impossible to roll back a sequence increment when a transaction rolls back (see the PostgreSQL documentation). So they're generally not suitable for accounting purposes like invoice numbering and other things where you require a gapless sequence.
For other readers who don't have the specific requirement to only sometimes generate a value: don't generate the sequence values manually; use a #GeneratedValue annotation.
In Hibernate 3.6 and newer, you should set hibernate.id.new_generator_mappings in your Hibernate properties.
Assuming you're mapping a generated key from a PostgreSQL SERIAL column, use the mapping:
#Id
#SequenceGenerator(name="mytable_id_seq",sequenceName="mytable_id_seq", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="mytable_id_seq")
If you omit the allocationSize then Hibernate assumes it's bizarre default of 50 and fails to check that the sequence actually increments by 50 so it tries to insert already-used IDs and fails.
Hibernate/JPA isn't able to automatically create a value for your non-id-properties. The #GeneratedValue annotation is only used in conjunction with #Id to create auto-numbering
Hibernate caches query results.
Notice the "as num", looks like hibernate is altering the SQL as well.
You should be able to tell Hibernate not to cache results with
query.setCacheable(false);

Generated sequence starts with 1 instead of 1000 which is set in annotation

I would like to ask some help regarding database sequence created by Hibernate.
I have this annotation - the code below - in my entity class in order to have individual sequence for partners table. I expect that the sequence starts with 1000, because I insert test data into my database using import.sql during deploy and I would like to avoid the constraint violation. But when I want to persist data than I got the constraint violation exception and it informs me about the fact the partner_id = 2 already exists. It looks like I missed something.
#Id
#Column(name = "partner_id")
#SequenceGenerator(initialValue=1000,
allocationSize=1,
name = "partner_sequence",
sequenceName="partner_sequence")
#GeneratedValue(generator="partner_sequence")
private Long partnerId;
The generated sequence looks like this:
CREATE SEQUENCE partner_sequence
INCREMENT 1
MINVALUE 1
MAXVALUE 9223372036854775807
START 1
CACHE 1;
ALTER TABLE partner_sequence
OWNER TO postgres;
I use postgres 9.1.
Did I miss something? This is the way how can I approach what I want?
Thanks for any help in advance!
initialValue is supported if hibernate.id.new_generator_mappings=true is specified according to this article. I had the same problem as stated in this post, and I solved it following this recipe. Sequences are generated correctly now.
initialValue and alocattionSize are specific to hilo algorithm that uses sequence. According to this initialValue is not even supported. I don't even see how it could be supported from Java layer since sequence values are generated in the database.
Also see hibernate oracle sequence produces large gap

Hibernate sequence generates non-continous values

I am using a hibernate sequencegenerator to auto-generate unique values for my primary key column.The sample code is given below.
#Entity
#Table(name = "REQUEST")
#javax.persistence.SequenceGenerator(name = "REQ_SEQ", sequenceName = "REQUEST_SEQ")
public class Request {
/**
* Unique id for this request
*/
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "REQ_SEQ")
#Column(name = "REQ_ID")
private long requestId;
//So on
}
Everything works fine except the fact that the generated values are interleaved. For example it inserts values from 5000 to 5015(15 inserts) and then the 16th insert produces the value as 5100.Then it works fine for a few subsequent inserts and again the problem.
I dont have any problem as long as the generated values are unique but just curious to know what could be causing this. FYI, I am using Oracle.
Oracle sequences work that way. They only guarantee uniqueness but they do not guarantee consecutive values as it would hamper parallelism.
What they do internally is more or less this: when you request the next value in the sequence, Oracle precomputes a chunk of values (in your case 5000-5099) and puts it in a sequence cache, and then sets seq.nextval = 5100 on disk. But if, due to activity, the db has to discard your chunk of values from the cache, when seq.nextval is accessed the next time, it will take another chunk 5100-5199. That is, Oracle will not even try to save sequence values that have been put into cache.
The point of this is that the sequence cache is a memory structure that is faster and more parallelizable that the sequence itself, which is an on-disk structure. As we want to scale up, we want to avoid going to disk as much as possible.
You can control chunk size for a given sequence using the CACHE clause in your sequence DDL:
CREATE SEQUENCE seq2
CACHE 50;
From the Oracle doc:
CREATE SEQUENCE customers_seq
START WITH 1000
INCREMENT BY 1
NOCACHE;
The NOCACHE keyword is what you can use if you would like consequential series of IDs. Of course as explained in previous comments, that would limit the sequence throughput and may hinder your application performance.

Categories