3

我有一个 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)。但是在数据库中设置状态的事务是在释放锁之后提交的,并且下一个调用者可能会再次获取相同的对象。

我怎么能防止这种情况?

4

5 回答 5

2

If you are using Wildfly: This is a bug. https://issues.jboss.org/browse/WFLY-4844 describes your problem which will be fixed in Wildfly 10. There the problem is described as a timer problem which might be the same as yours.

My workaround is to seperate the code that does the work into another bean which is called by the outer (timer) bean. The outer bean method is annotated to not start a transaction (@TransactionAttribute(TransactionAttributeType.NEVER)), so the transaction is started and safely finished in the second new bean.

于 2016-02-12T05:42:05.977 回答
1

You could use SELECT FOR UPDATE to serialize the access of the rows.

With JPA 2 use the LockModeType: http://docs.oracle.com/javaee/6/api/javax/persistence/LockModeType.html

q.setLockMode(LockModeType.PESSIMISTIC_WRITE)
于 2013-10-17T13:53:32.637 回答
0

在 JPA 中没有办法做到这一点(所以,以可移植的方式)。您的选择可能是:

  1. 一些 JPA 实现允许在每个查询的基础上设置隔离级别(例如OpenJPA),有些则不允许(Hibernate)。但即使在 OpenJPA 中,这个提示也需要在特定的数据库驱动程序中实现,否则没有效果)。
  2. 运行本机查询 - 有关详细信息,请参阅您的数据库文档。

作为旁注,我应该说 JPA(和一般的 Java EE)在设计时并没有考虑到批量数据库操作——而是针对在大多数情况下不重叠的数据项的多个并发查询。

于 2013-10-01T15:24:20.613 回答
0

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

I know that this way we have two really tight coupled beans, but this should solve your problem.

于 2013-10-01T20:37:35.940 回答
0

You're using container-managed concurrency (the default). In JavaEE 7 (not sure about older ones, but likely yes) the transaction is guaranteed to commit before the method exits, hence before lock is released. From the JavaEE 7 tutorials:

"Typically, the container begins a transaction immediately before an enterprise bean method starts and commits the transaction just before the method exits. Each method can be associated with a single transaction. Nested or multiple transactions are not allowed within a method." https://docs.oracle.com/javaee/7/tutorial/doc/transactions003.htm#BNCIJ

If you're experiencing another behavior, check for any cache that might be active (@Cacheable). You may watch another interesting question here: https://stackoverflow.com/questions/26790667/timeout-and-container-managed-concurrency-in-singleton

By the way, LockType(WRITE) is also default, you don't need to explicit it. Hence, getAllObjects is also LockType(WRITE).

于 2014-11-12T02:54:20.750 回答