Объекта 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. Т.е. как ведут себя транзакции, когда они обращаются к ресурсам других транзакций.
Согласно этой статье :
READ_COMMITTED - ожидаемый уровень изоляции транзакции по умолчанию для использования [..] EJB3 JPA
Это означает, что - да, у вас будут проблемы с приведенным выше кодом .
Но JPA не поддерживает настраиваемые уровни изоляции.
В этой ветке эта тема обсуждается более подробно. В зависимости от того, используете ли вы Spring или EJB, я думаю, вы можете использовать правильную стратегию транзакций.