有一个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实现)是否支持它。请帮忙。
答案 0 :(得分:1)
您指的是JPA事务的事务隔离。即当交易访问其他交易资源时,交易的行为是什么。
根据this article:
READ_COMMITTED是使用[..] EJB3 JPA的预期默认事务隔离级别
这意味着 - 是的,您将遇到上述代码的问题。
但JPA不支持自定义隔离级别。
This thread更广泛地讨论了这个主题。根据您使用的是Spring还是EJB,我认为您可以使用正确的事务策略。