我正在使用Glassfish v4和EJB应用程序与JPA2.1(EclipseLink)。我遇到了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日志。 如果有人见过这样的话,请告诉我。
感谢。
答案 0 :(得分:0)
我认为您正在使用EclipseLink的缓存。
您应该禁用此实体的缓存:
@Cacheable(false)
@Entity
public class Example {
...
}
答案 1 :(得分:0)
EJB Singleton仅适用于单个jvm,在多个节点中
在环境中,您必须依赖数据库的锁定。
请勿使用REQUIRES_NEW
,仅在必要时使用它。如果存在外部事务,则REQUIRES_NEW
事务中的回滚事件不会影响外部事务。因为他们是不同的交易。应用程序停止外部事务并开始并提交或回滚内部事务,然后恢复外部事务。