Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JBoss EAP 6.x with Hibernate Oracle Sequence Duplicate Value on Primary Key

I have defined a number of Hibernate Entities using pure JPA annotations. These use an predefined Oracle Sequence on my database to automatically generate primary key values.

@Id
@SequenceGenerator(name = "USERS_ID_GENERATOR", sequenceName = "MY_SEQ")
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USERS_ID_GENERATOR")
@Column(name = "U_ID", updatable = false, unique = true, nullable = false, precision = 19)
private Long id;

When this is deployed to JBoss EAP 6.1 all works well initially however after a short period Hibernate starts generating duplicate keys on inserts (ORA-00001 errors).

I don't care about id ordering or gaps, but can't tolerate duplicate keys... What is going on here?

like image 778
shonky linux user Avatar asked Jul 24 '13 05:07

shonky linux user


2 Answers

This is not well documented, many of the solutions on here and other sites relate to older versions of hibernate where the HiLo sequenceGenerator was the default. But after investigation I found the underlying cause is that JBoss EAP 6 sets

hibernate.id.new_generator_mappings=true 

by default, which uses an org.org.hibernate.id.enhanced.SequenceStyleGenerator instead of the older version.

The Hibernate SequenceStyleGenerator default increment is 1 (check the code!), however JPA overrides the increment value in this generator to 50. This means that the Generator looks at the sequence nextval and keeps a cache of 50 ids to use, starting from nextval - 49. When these are exhausted the generator reads the next sequence from oracle, and repeats the process. So once the first series of ids are exhausted we start seeing duplicate keys.

So the resolution is:

1) Either define your Oracle sequence(s) with an increment value of 50 to match the JPA default

CREATE SEQUENCE MY_SEQ
START WITH 50
MAXVALUE 9999999999999999999
INCREMENT BY 50
NOCYCLE;

or

2) Add allocationSize=1 to the @SequenceGenerator annotation - this forces the SequenceGenerator to go back to read the next value from the oracle sequence for each ID it requires (with a potential performance impact)

 @SequenceGenerator(name = "USERS_ID_GENERATOR", sequenceName = "MY_SEQ", allocationSize = 1)

, or

3) define the Oracle sequence INCREMENT BY some other value, and ensure the allocationSize matches.

Answered my own question in the hope of helping others that strike this issue.

like image 130
shonky linux user Avatar answered Oct 29 '22 20:10

shonky linux user


Your answers are correct; just some more details.

Some posts propose to turn off hibernate.id.new_generator_mappings=false.

But according to https://docs.jboss.org/author/display/AS71/JPA+Reference+Guide#JPAReferenceGuide-Persistenceunitproperties

There is a difference between GenerationType.AUTO and GenerationType.SEQUENCE

if you choose AUTO, you'll select the hibernate native. and if you choose SEQUENCE, you'll match hilo algorithm for sequence allocation that is absolutely not the same as SequenceStyleGenerator. This will be NOT compatible if you switch hibernate.id.new_generator_mappings=true / false.

So the answer 1) is definitely the correct one / following the current Hibernate/Jboss recommendations.

...and the answer setting allocationSize=1 to all entities is not a good solution. See http://itdevworld.wordpress.com/2009/12/20/hibernate-sequencegenerator-with-allocationsize1-leads-to-huge-contention/

like image 39
skay Avatar answered Oct 29 '22 22:10

skay