Я бы хотел написать метод, подобный <T> T getOrCreate(Class<T> klass, Object primaryKey)
Это будет нелегко.
Наивным подходом было бы сделать что-то вроде этого (при условии, что метод выполняется внутри транзакции):
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);
}
}
}
Но в параллельной среде этот код может не работать из-за некоторого состояния гонки:
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
И если вы используете несколько JVM, синхронизация не поможет. И без приобретения блокировки таблицы (что довольно ужасно), я не понимаю, как вы могли бы решить эту проблему.
В таком случае мне интересно, не лучше ли систематически сначала вставлять и обрабатывать возможные исключения для выполнения последующего выбора (в новой транзакции).
Вероятно, вам следует добавить некоторые детали относительно упомянутых ограничений (многопоточность? Распределенная среда?).