I'm working on a project where we recently have changed our persistence provider from using OpenJPA to use EclipseLink. This is a big and old application where we also do SQL inserts from other processes that are currently not feasible to migrate to JPA in the time being.
We use @TableGenerator to reference a table where we keep track of the ids to use on inserts.
When we used OpenJPA, we noticed that it first selects next id from the table, then it updates table to preallocate next id's. This is exactly the same way the old SQL process gets and preallocates next id's.
When we switched to EclipseLink, we noticed the opposite behavior, it Updates the table to preallocate next id's, and then begin doing inserts. This is causing us a java.sql.SQLIntegrityConstraintViolationException because the last preallocated id has been used by the non-JPA processes to insert a new record, so when the JPA process reaches that id, the database gives an error, claiming that we are trying to do an insert with an id already used.
Is there a way to tell EclipseLink to handle pre-allocation the way OpenJPA does?
Here are some samples of the pre-allocation strategy from OpenJPA vs EclipseLink, for these examples I have set the allocationSize to 5
OpenJPA
TEST TRACE [main] openjpa.jdbc.SQL - <t 22760146, conn 3658896> executing prepstmnt 9137209 SELECT NEXT_ID FROM ABC.table_ids WHERE TABLE_ID = ? FOR UPDATE [params=(String) 1034] [reused=0]
TEST TRACE [main] openjpa.jdbc.SQL - <t 22760146, conn 3658896> [94 ms] spent
TEST TRACE [main] openjpa.jdbc.SQL - <t 22760146, conn 3658896> executing prepstmnt 23999306 UPDATE ABC.table_ids SET NEXT_ID = ? WHERE TABLE_ID = ? AND NEXT_ID = ? [params=(long) 55, (String) 10, (long) 50] [reused=0]
TEST TRACE [main] openjpa.jdbc.SQL - <t 22760146, conn 3658896> [93 ms] spent
EclipseLink:
[EL Fine]: 2013-01-23 14:08:35.875--ClientSession(6215763)--Connection(10098848)--Thread(Thread[main,5,main])--UPDATE table_ids SET next_id = next_id + ? WHERE table_id = ?
bind => [5, 10]
[EL Fine]: 2013-01-23 14:08:36.0--ClientSession(6215763)--Connection(10098848)--Thread(Thread[main,5,main])--SELECT next_id FROM table_ids WHERE table_id = ?
bind => [10]
Thanks in advance!
The issue isn't the order of the update versus select, but the interpretation of having the current value or not. EclipseLink assumes it gets the current value, where as OpenJPA appears to not.
Ideally you would be able to change the non-JPA usages to make the same assumption. If you cannot, you could write your own custom Sequence object in EclipseLink.
To do this create a subclass of TableSequence and override the buildSelectQuery() method to add a "+ 1" (or "- 1") to the SQL to account for the difference in assumption.
You can then add your custom sequence using a SessionCustomizer.
Please also log a bug in EclipseLink to have a compatiblity option added for OpenJPA sequencing.