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'sallocationSizewith your sequence'sincrementBy. The default value forallocationSizeis 50, which means that after every 50th insert, the hibernate will generateselect 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:
allocationSize) without asking DB what is the next value for the sequence. Generated ID will be from 1 to 50.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 nextvalon sequence, it simply doescurrentValue + incrementBy. For your sequence, it'll be 1, 11, 21, 31, etc.If you enable SQL logs, you'll see following:
repository.save(entity)the first time would generaterepository.save(entity)would generate onlyallocationSizenumber of inserts you would again see: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
allocationSizeto get the best results.Note: one of the comments suggested to use
allocationSize = 1which is very bad and will have a huge impact on performance. For Hibernate that would mean that it needs to issueselect nextvalevery 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
initialValueofSequenceGeneratorandstartValueof sequence in sync as well.allocationSizeandinitialValueare 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: