I'd like to write a method like <T> T getOrCreate(Class<T> klass, Object primaryKey)
This won't be easy.
A naive approach would be to do something like this (assuming the method is running inside a transaction):
public <T> T findOrCreate(Class<T> entityClass, Object primaryKey) {
T entity = em.find(entityClass, primaryKey);
if ( entity != null ) {
return entity;
} else {
try {
entity = entityClass.newInstance();
/* use more reflection to set the pk (probably need a base entity) */
return entity;
} catch ( Exception e ) {
throw new RuntimeException(e);
}
}
}
But in a concurrent environment, this code could fail due to some race condition:
T1: BEGIN TX;
T2: BEGIN TX;
T1: SELECT w/ id = 123; //returns null
T2: SELECT w/ id = 123; //returns null
T1: INSERT w/ id = 123;
T1: COMMIT; //row inserted
T2: INSERT w/ name = 123;
T2: COMMIT; //constraint violation
And if you are running multiple JVMs, synchronization won't help. And without acquiring a table lock (which is pretty horrible), I don't really see how you could solve this.
In such case, I wonder if it wouldn't be better to systematically insert first and handle a possible exception to perform a subsequent select (in a new transaction).
You should probably add some details regarding the mentioned constraints (multi-threading? distributed environment?).