Singleton-EJB并发与容器管理的事务

时间:2013-10-01 10:15:51

标签: java java-ee concurrency transactions singleton

我有一个Singleton-EJB,它从具有特定状态的数据库中读取所有对象。然后我用这些对象做一些事情并将状态设置为其他东西:

@Singleton
public class MyEJB {

    @PersistenceContext(unitName = "MyPu")
    private EntityManager em;

    @Lock(LockType.WRITE)
    public void doSomeStuffAndClose() {
        List<MyObj> objects = getAllOpenObjects();
        for (MyObj obj : objects) {
            // do some stuff here...
            obj.setClosed(true);
        }
    }

    private List<MyObj> getAllOpenObjects() {
        TypedQuery<MyObj> q = em.createQuery("select o from MyObj o "
            + "where o.closed = false", MyObj.class);
        return q.getResultList();
    }
}

现在,如果我想确保无法快速调用我的方法,我会添加注释@Lock(LockType.WRITE)。但是,在锁定释放后提交设置数据库中状态的事务,并且下一个调用者可能会再次抓取相同的对象。

我怎么能阻止这个?

5 个答案:

答案 0 :(得分:2)

如果您使用Wildfly:这是一个错误。 https://issues.jboss.org/browse/WFLY-4844描述了您将在Wildfly 10中修复的问题。问题被描述为计时器问题,可能与您的问题相同。

我的解决方法是将执行该工作的代码分离到另一个由外部(计时器)bean调用的bean中。外部bean方法注释为不启动事务(@TransactionAttribute(TransactionAttributeType.NEVER)),因此事务在第二个新bean中启动并安全地完成。

答案 1 :(得分:1)

您可以使用SELECT FOR UPDATE来序列化对行的访问。

使用JPA 2时,请使用LockModeType: http://docs.oracle.com/javaee/6/api/javax/persistence/LockModeType.html

q.setLockMode(LockModeType.PESSIMISTIC_WRITE)

答案 2 :(得分:0)

在JPA中无法做到这一点(因此,以便携式方式)。您的选择可能是:

  1. 某些JPA实现允许基于每个查询设置隔离级别(例如OpenJPA),有些则不允许(Hibernate)。但即使在OpenJPA中,这个提示也需要在特定的数据库驱动程序中实现,否则它没有效果。)
  2. 运行本机查询 - 有关详细信息,请参阅数据库文档。
  3. 作为旁注,我应该说JPA(以及一般的Java EE)并没有考虑到批量数据库操作 - 而是针对数据项的多个并发查询,在大多数情况下不会重叠。

答案 3 :(得分:0)

您可以使用已实现的SessionSynchronization接口从doSomeStuffAndClose方法调用Stateful Session Bean。与SFSB中的afterCompletion方法相比,您可以通知singleton bean数据已被提交并可以处理另一个请求。

我知道这样我们有两个非常紧密耦合的bean,但这应该可以解决你的问题。

答案 4 :(得分:0)

您正在使用容器管理的并发(默认值)。在JavaEE 7中(不确定旧的,但可能是肯定的),事务保证在方法退出之前提交,因此在释放锁之前。从JavaEE 7教程:

“通常,容器在企业bean方法启动之前立即开始事务,并在方法退出之前提交事务。每个方法都可以与单个事务关联。在方法中不允许嵌套或多个事务。” https://docs.oracle.com/javaee/7/tutorial/doc/transactions003.htm#BNCIJ

如果您遇到其他行为,请检查可能处于活动状态的任何缓存(@Cacheable)。您可以在此处观看另一个有趣的问题:https://stackoverflow.com/questions/26790667/timeout-and-container-managed-concurrency-in-singleton

顺便说一下,LockType(WRITE)也是默认的,你不需要显式它。因此,getAllObjects也是LockType(WRITE)。