A имеет объект JPA, который имеет поле метки времени и отличается полем комплексного идентификатора.Мне нужно обновить отметку времени в уже сохраненной сущности, в противном случае создать и сохранить новую сущность с текущей отметкой времени.
Как оказалось, задача не так проста, как кажется на первый взгляд,Проблема состоит в том, что в параллельной среде я получаю неприятное исключение «Уникальный индекс или нарушение первичного ключа».Вот мой код:
// Load existing entity, if any.
Entity e = entityManager.find(Entity.class, id);
if (e == null) {
// Could not find entity with the specified id in the database, so create new one.
e = entityManager.merge(new Entity(id));
}
// Set current time...
e.setTimestamp(new Date());
// ...and finally save entity.
entityManager.flush();
Обратите внимание, что в этом примере идентификатор сущности не генерируется при вставке, он известен заранее.
Когда два или более потоков запускают этот блок кодапараллельно они могут одновременно получать null
из entityManager.find(Entity.class, id)
вызова метода, поэтому они будут пытаться сохранить две или более сущностей одновременно, причем один и тот же идентификатор приведет к ошибке.
Я думаю, что тамЕсть несколько решений этой проблемы.
- Конечно, я мог бы синхронизировать этот блок кода с глобальной блокировкой, чтобы предотвратить одновременный доступ к базе данных, но будет ли это наиболее эффективным способом?
- Некоторые базы данных поддерживают очень удобный оператор
MERGE
, который обновляет существующую или создает новую строку, если ее нет.Но я сомневаюсь, что OpenJPA (реализация JPA по моему выбору) поддерживает это. - Если JPA не поддерживает SQL MERGE, я всегда могу вернуться к старому JDBC и делать с базой все, что захочу.Но я не хочу оставлять удобный API и связываться с волосатой комбинацией JDBC + SQL.
- Существует волшебная хитрость, чтобы исправить это, используя только стандартный JPA API, но я пока не знаю.
Пожалуйста, помогите.