I read that JPA caches SQL instructions to improve performance:
JPA providers like Hibernate can cache the SQL instructions they are supposed to send to the database, often until you actually commit the transaction. For example, you call em.persist(), Hibernate remembers it has to make a database INSERT, but does not actually execute the instruction until you commit the transaction.
I have a Java EE 6 application deployed to a Glassfish cluster with two instances. In the application there is a race condition where two Singletons do some expensive queries and then cache the results in a database table. They're doing the same work and trying to write the same record, so I sometimes get an exception:
java.sql.SQLIntegrityConstraintViolationException: ORA-00001: unique constraint (SOMESCHEMA.SOMETABLE_PK) violated
I decided the easiest way to deal with this would be to catch and ignore the exception:
// In a EJB with container-managed transactions.
public Entity getExpensiveEntity(int entityId) {
Entity entity = entityManager.find(Entity.class, entityId);
if (entity == null) {
try {
result = expensiveQueries();
entityManager.persist(result);
entityManager.flush();
} catch (SQLIntegrityConstraintViolationException ex) {
// The other instance already created the result, so get it.
result = jpa.find(result.getId());
}
}
return result;
}
I think the call to flush
is necessary because otherwise the SQLIntegrityConstraintViolationException won't occur until the transaction ends somewhere up the EJB call stack, past catching and ignoring. Am I correct, is this a valid use case for flush
? Is there a better way to handle this?