2010-02-25 35 views
9

创建新的或更新现有实体A具有一个具有时间戳字段并由复杂标识符字段进行区分的JPA实体。我需要的是在已存储的实体中更新时间戳,否则使用当前时间戳创建并存储新实体。使用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(); 

请注意,在这个例子中实体标识符没有在刀片上产生,它预先知道。

当两个或更多个线程的运行的并行代码块,它们可以同时从entityManager.find(Entity.class, id)方法调用得到null,所以它们将尝试在同一时间保存两个或更多实体,具有相同标识符导致错误。

我认为这个问题很少有解决方案。

  1. 当然,我会这样的代码块与全球锁以防止对数据库的并发访问同步,但它是最有效的方法是什么?
  2. 某些数据库支持非常方便的MERGE语句,该语句更新现有的或创建新行(如果不存在)。但我怀疑OpenJPA(我选择的JPA实现)是否支持它。
  3. 事件如果JPA不支持SQL MERGE,我总是可以回退到普通的旧JDBC并对数据库做任何我想做的事情。但是我不想留下舒适的API,并且不愿意使用大量的JDBC + SQL组合。
  4. 只有使用标准的JPA API才能修复它,但我还不知道它。

请帮忙。

回答

1

您指的是JPA事务的事务隔离。即当他们访问其他交易的资源时,交易的行为是什么?

根据this article

READ_COMMITTED是使用[..] EJB3 JPA

这意味着预期的默认事务隔离级别 - 是的,你将不得不与上面的代码问题。

但JPA不支持自定义隔离级别。

This thread更广泛地讨论了这个话题。根据您使用Spring还是EJB,我认为您可以使用正确的事务策略。

+0

两条链路都断开 – Sergey 2018-01-24 05:17:23