I have 2 transactions come into my application that want to update a subset of the same entities. However some of those entities are not in the database, so both threads create the same entity and try to commit it. Causing a database Duplicate entry for primary key. How do I handle this?
eg Transaction 1 wants to update entities A,B,C,D,E,F Transaction 2 wants to update entities D,E,F,G,H,I My Database only has records for entities A,C,E,G and I
So Transaction 1 and 2 attempt to create entity D. Which the second thread fails to commit as D is the primary key and now already exists.
I've tried using pessimistic_write, but this doesn't help records that don't exist yet.
I've tried wrapping my persist() call in a try catch but it appears that the exception is thrown internally to the JPA and my application never sees it.
Most google searches return examples about locking existing records, and if they happen to mention concurrent inserts it seems to be beyond the scope of the article.
Some articles say I should be using a database generated unique id, this doesn't work in my instance. Or I should be using a version in my entity, but again my entity model improves its accuracy based on its previous values, the values of the entities are not isolated but rely on previous transactions.
I've thought about pre-populating the database but there is probably over a billion possible valid entity keys, although I'm only working on a set of about 20,000 keys but I don't know what they are until a transaction comes in.
This is my persist code with try catch wrapper, I never see the message "INSERT Persistence collision," come out of my application, just the JPA stack trace.
if (cellmu == null)
{
CellMUEntity cellmuNew = new CellMUEntity();
cellmuNew.setId(cellOuter);
cellmuNew.setDistribution(mus);
try {
em.persist(cellmuNew);
} catch (Exception e) {
System.out.println("INSERT Persistence collision, " +e.getClass().getName() +" read and refine (" + cellOuter+")" );
cellmuNew = em.find(CellMUEntity.class,cellOuter,LockModeType.PESSIMISTIC_WRITE);
cellmuNew.setDistribution(cellmuNew.getDistribution().refine(mus));
em.merge(cellmuNew);
}
}
here is part of the stack trace from the jpa...
[2019-05-01T21:07:07.654+1000] [glassfish 4.1] [WARNING] [jts.unexpected_error_occurred_in_after_completion] [javax.enterprise.system.core.transaction.com.sun.jts.jta] [tid: _ThreadID=70 _ThreadName=p: thread-pool-1; w: 2] [timeMillis: 1556708827654] [levelValue: 900] [[
JTS5054: Unexpected error occurred in after completion
Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.1.v20150605-31e8258): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry '6ad63bd4' for key 'PRIMARY'
Error Code: 1062
Call: INSERT INTO mucell (cellid, mu_high, mu_low) VALUES (?, ?, ?)
bind => [3 parameters bound]
Query: InsertObjectQuery((face=3, pos=ad63bd400000000, level=13):[0.0, 47499.492305012])
at org.eclipse.persistence.exceptions.DatabaseException.sqlException(DatabaseException.java:331)