EJB3事务传播

时间:2008-09-19 23:43:09

标签: transactions java-ee ejb-3.0

我有一个像无状态的bean:

@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(...),它实际上不与实体管理器交互。它完成了它需要做的事情,并为每个要处理的对象单独调用进程(...)。进程的持续时间(...)相对较短,但是processObjects(...)可能需要很长时间来运行所有内容。因此我不希望它维持一个开放的交易。我需要单独的进程(...)操作才能在自己的事务中运行。这应该是每次通话的新交易。最后,我想保持选项打开,让客户端直接调用进程(...)。

我尝试了许多不同的事务类型:从不,不支持,支持(在processObjects上)和必需,需要new(在进程上)但每次调用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,但欢迎/首选非特定答案。

8 个答案:

答案 0 :(得分:24)

另一种方法是在同一个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 - 并且仍然只保留一个类。呼!

答案 1 :(得分:4)

Matt,你问的问题是一个非常经典的问题,我认为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();
    }
}

答案 2 :(得分:2)

我认为事情是每个bean都包含在一个控制事务行为的代理中。当您从一个bean调用另一个bean时,您将通过该bean的代理进行操作,并且代理可以更改事务行为。

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

答案 3 :(得分:1)

马特,因为它的价值,我得出与你完全相同的结论。

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

据我所知,EJB Persistence Spec中没有任何内容可以指定在这些情况下应该采取的行为。

我在Jboss也经历过这个。我还将尝试使用Glassfish,让您知道结果。

答案 4 :(得分:1)

如果有人偶然遇到这一天:

避免循环依赖(例如允许自引用)在JBoss中使用注释'IgnoreDependency',例如:

@IgnoreDependency @EJB MySelf本人参考;

答案 5 :(得分:1)

我还没有尝试过(我即将),但是通过@EJB注释注入自引用的替代方法是SessionContext.getBusinessObject()方法。这将是另一种方法,以避免循环引用吹嘘你的可能性 - 尽管至少对于无状态豆注射确实有效。

我正在开发一个大型系统,其中采用了两种技术(可能是由不同的开发人员),但我不确定哪种方法是“正确的”。

答案 6 :(得分:0)

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

<强> TransactionAttributeType.Never

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

  

如果客户端在a内运行   事务并调用企业   bean的方法,容器抛出一个   RemoteException的。如果客户不是   与交易相关联的   容器不会启动新的   运行方法之前的事务。

我假设您是客户端代码中的方法 processObjects 的客户端。因为可能您的客户端与事务无关,所以首先使用 TransactionAttributeType.Never 进行方法调用很开心。然后从 processObjects 调用 process 方法,尽管 TransactionAttributeType.Required 注释不是bean方法调用且未强制执行事务策略。当您致电合并时,您会收到异常,因为您仍未与交易相关联。

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

答案 7 :(得分:0)

我提到了Kevin提到的这些循环依赖问题。但是,建议的注释@IgnoreDependency是一个特定于jboss的注释,例如Glassfish中没有对应的注释。

由于它不能与默认的EJB引用一起使用,我觉得这个解决方案有点不舒服。

因此,我给了bluecarbon的解决方案一个机会,从而“手动”开始内部交易。

除此之外,我认为没有解决办法,只能在另一个bean中实现内部进程(),这也很难看,因为我们只是想干扰我们的类模型以获得这些技术细节。