EJB3事务传播

时间:2020-03-06 14:28:17  来源:igfitidea点击:

我有一个像这样的无状态豆:

@Stateless
public class MyStatelessBean implements MyStatelessLocal, MyStatelessRemote {
    @PersistenceContext(unitName="myPC")
    private EntityManager mgr;

    @TransationAttribute(TransactionAttributeType.SUPPORTED)
    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.process(obj);
        }
    }

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

然后,通常的用法是客户端将调用processObjects(...),该对象实际上并未与实体管理器进行交互。它完成需要做的事情,并为每个要处理的对象分别调用process(...)。 process(...)的持续时间相对较短,但是processObjects(...)可能需要很长时间才能完成所有操作。因此,我不希望它保持公开交易。我确实需要单个流程(...)操作才能在自己的事务中进行操作。这应该是每个呼叫的新事务。最后,我想让该选项保持打开状态,以便客户端直接调用process(...)。

我尝试了多种不同的事务类型:从不,不支持,受支持(在processObjects上)和必需的,需要新的(在进程上),但是每次调用merge()时,我都会得到TransactionRequiredException。

通过将方法分成两个不同的bean,我能够使其工作:

@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessBean2 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }
}

@Stateless
public class MyStatelessBean2 implements MyStatelessLocal2, MyStatelessRemote2 {
    @PersistenceContext(unitName="myPC")
    private EntityManager mgr;

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

但我仍然好奇是否有可能在一堂课中做到这一点。在我看来,即使给单个方法指定了更具体的注释,事务管理器也只能在Bean级别上运行。因此,如果我以一种防止事务开始在同一实例内调用其他方法的方式标记一个方法,那么无论如何对其进行标记,也将不会创建事务。

我正在使用JBoss Application Server 4.2.1.GA,但欢迎/首选非特定答案。

解决方案

我认为与方法processObjects上的@TransationAttribute(TransactionAttributeType.Never)有关。

TransactionAttributeType.Never

http://docs.sun.com/app/docs/doc/819-3669/6n5sg7cm3?a=view

If the client is running within a
  transaction and invokes the enterprise
  bean’s method, the container throws a
  RemoteException. If the client is not
  associated with a transaction, the
  container does not start a new
  transaction before running the method.

我假设我们是客户端代码中的processObjects方法的客户端。因为可能客户端未与事务关联,所以首先使用TransactionAttributeType.Never进行方法调用是不愉快的。然后,从完全具有TransactionAttributeType.Required批注的processObjects中调用处理方法,而不是bean方法调用,并且不执行事务策略。当我们调用合并时,我们会得到异常,因为我们仍未与事务关联。

尝试对两个bean方法都使用TransactionAttributeType.Required来查看它是否有效。

马特(Matt),我所得出的结论与结论完全一样。

仅在跨越Bean边界时才考虑TransactionAttributeTypes。当在同一个bean中调用方法时,TransactionAttributeTypes无效,无论这些方法上放置什么类型。

据我所知,EJB持久性规范中没有任何内容指定在这种情况下的行为。

我在Jboss中也经历过。我还将在Glassfish中尝试一下,让我们知道结果。

我认为问题是每个bean都包装在控制事务行为的代理中。当我们从一个bean调用到另一个bean时,我们正在通过该bean的代理,并且代理可以更改事务行为。

但是,当bean自己调用具有不同事务属性的方法时,该调用不会通过代理进行,因此行为不会改变。

实现它的另一种方法实际上是将两个方法都放在同一个bean上,并对其自身有一个@EJB引用!像这样的东西:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {
    @EJB
    private MyStatelessLocal1 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }

    @TransationAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void process(Object obj) {
        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();
    }
}

这样,我们实际上"强制"了可以通过ejb代理堆栈访问的process()方法,因此使@TransactionAttribute有效,并且仍然只保留一个类。 !

马特,我们提出的问题是一个非常经典的问题,我认为Herval / Pascal的自参照解决方案很简洁。这里没有提到更通用的解决方案。

EJB"用户"事务就是这种情况。由于我们在会话Bean中,因此可以从会话上下文中获取用户事务。这是代码在用户事务中的外观:

// supposing processObjects defined on MyStatelessRemote1 and process defined on MyStatelessLocal1
@Stateless
@TransationAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class MyStatelessBean1 implements MyStatelessLocal1, MyStatelessRemote1 {

    @Resource
    private SessionContext ctx;

    @EJB
    private MyStatelessLocal1 myBean2;

    public void processObjects(List<Object> objs) {
        // this method just processes the data; no need for a transaction
        for(Object obj : objs) {
            this.myBean2.process(obj);
        }
    }

    public void process(Object obj) {

        UserTransaction tx = ctx.getUserTransaction();

        tx.begin();

        // do some work with obj that must be in the scope of a transaction

        this.mgr.merge(obj);
        // ...
        this.mgr.merge(obj);
        // ...
        this.mgr.flush();

        tx.commit();
    }
}

如果有人遇到这一天:

为了避免JBoss中的循环依赖(例如允许自我引用),请使用注释" IgnoreDependency",例如:

@IgnoreDependency
@EJB MySelf selfRef;