I have several mapped objects in my JPA / Hibernate application. On the network I receive packets that represent updates to these objects, or may in fact represent new objects entirely.
I'd like to write a method like
<T> T getOrCreate(Class<T> klass, Object primaryKey)
that returns an object of the provided class if one exists in the database with pk primaryKey, and otherwise creates a new object of that class, persists it and returns it.
The very next thing I'll do with the object will be to update all its fields, within a transaction.
Is there an idiomatic way to do this in JPA, or is there a better way to solve my problem?
Я бы хотел написать такой метод, как
T getOrCreate(Class 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, синхронизация не поможет. И без блокировки таблицы (что довольно ужасно) я действительно не понимаю, как вы могли бы решить эту проблему.
В таком случае я задаюсь вопросом, не лучше ли было бы сначала систематически вставлять и обрабатывать возможное исключение для выполнения последующего выбора (в новой транзакции).
Вероятно, вам следует добавить некоторые подробности относительно упомянутых ограничений (многопоточность? распределенная среда?).