【发布时间】:2018-04-09 02:40:03
【问题描述】:
我正在使用带有 JPA2.1(EclipseLink) 的 Glassfish v4 和 EJB 应用程序。我遇到了 JTA 的并发问题。基本上,我正在尝试通过给定名称查找实体,然后使用新的 nextVal 更新列。 所以一个 EJB 无状态 Bean
@PersistenceContext(unitName = "db")
private EntityManager em;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public List<Long> getWithWriteLock(final String name, final int size) {
TypedQuery<Sequence> q = em.createNamedQuery("Example.findByName", Example.class);
q.setParameter("name", name);
q.setLockMode(LockModeType.PESSIMISTIC_WRITE);
Example example = q.getSingleResult();
final Long result = example.getNextval().longValue();
final Long nextVal = result + size;
final List<Long> resultList = LongStream.range(result, nextVal).boxed().collect(Collectors.toList());
example.setNextval(BigInteger.valueOf(nextVal));
em.merge(example);
em.flush(); //forced flush
return resultList;
}
应用程序部署在多个节点上,此方法通过带有 WRITE 锁的 EJB Singleton 进行评估。我们看到的是返回的列表是重复的随机时间。当不同的线程调用此方法时甚至超过一分钟时,它甚至会发生。我们尝试过显式刷新、合并甚至验证确实使用 SELECT ... FOR UPDATE 语句的 SQL 日志。 如果有人看到过这样的事情,请告诉我。
谢谢。
【问题讨论】:
-
“flush()”到底在做什么?不应该是 em.flush() 吗?另外,当您稍后使用 em.merge 时,为什么要使用 hw2EntityManager 实例来创建查询 - 它们应该是相同的 EntityManager 或者您的读取发生在不同的上下文中。
-
是的,它的 em.flush()。我更新了我的示例 sn-p。 em 上下文也一样。
-
您必须检查日志,因为如果一个事务回滚,可能会发生这种情况。 JPA 日志记录应该显示您更新对象所使用的值,因此允许您跟踪哪些其他线程放入了这些相同的值,并给出处理重复的原因的线索。
-
谢谢@Chris。所以我们已经确认没有异常,并且这个方法在新事务中,所以即使调用者事务被回滚,这仍然会通过。 SQL 日志说我们做了一个正确的 SELCT For update,然后只正确地将 nextVal 更新为 'X' 一次。但是问题似乎是 2 个线程同时调用相同的方法,并且它们都同时获取数据的副本,最终 List
结果是相同的。我们从带有 WRITE 锁的 Singleton 调用此方法。有什么想法吗? -
悲观锁依赖于数据库工作,看起来你的允许第二个查询返回而不是等待。
标签: hibernate concurrency ejb eclipselink jta