【问题标题】:EJB3 Transaction PropagationEJB3 事务传播
【发布时间】:2008-09-19 23:43:08
【问题描述】:

我有一个类似的无状态 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(...),它实际上并不与实体管理器交互。它做它需要做的事情,并为每个要处理的对象单独调用 process(...) 。 process(...) 的持续时间相对较短,但 processObjects(...) 可能需要很长时间才能运行所有内容。因此我不希望它保持一个开放的交易。我确实需要单独的 process(...) 操作在他们自己的事务中操作。这应该是每次调用的新事务。最后,我想保持选项打开,让客户端直接调用 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,但欢迎/首选非特定答案。

【问题讨论】:

    标签: transactions jakarta-ee ejb-3.0


    【解决方案1】:

    实现它的另一种方法实际上是在同一个 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();
        }
    }
    

    这样,您实际上“强制”process() 方法通过代理的 ejb 堆栈访问,因此使 @TransactionAttribute 生效 - 并且仍然只保留一个类。呸!

    【讨论】:

    • 只是想我会顺便过来分享一下,自从问了这个问题以来,这个问题已经出现了很多次(顺便说一下,我的老板说他曾经通过谷歌找到这个问题,同时试图自己解决这个问题.) 我们已多次使用此解决方案,再次感谢。
    • 我认为这是一个很好的问题。如果您曾经必须在 EJB2 世界中做任何事情,您会期望这些语义,但如果您只在 EJB3 中工作过,它们可能看起来有点陌生。另一种看待事物的方式是,您需要确保您调用的方法是通过 EJB 接口调用的。
    【解决方案2】:

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

    【讨论】:

    • 我不喜欢这个解决方案。它破坏了 EJB 容器管理的 TX 的优势之一,即减少了手动 TX 管理和相关的样板代码。在这种情况下没有必要这样做。只需按照接受的答案,或者,您可以像这样使用会话上下文: ctx.getBusinessObject(MyStatelessLocal1.class).process(obj);和给自己注射一样的效果。
    【解决方案3】:

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

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

    【讨论】:

    • 这样,bean 可以调用自己来获得该行为,就好像它来自另一个 bean。
    【解决方案4】:

    马特,不管怎样,我得出的结论和你完全一样。

    TransactionAttributeTypes 仅在跨越 Bean 边界时才被考虑。当在同一个bean中调用方法时,TransactionAttributeTypes没有任何效果,不管方法上放什么Types。

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

    我在 Jboss 也遇到过这种情况。我也会在 Glassfish 中试一试,然后告诉你结果。

    【讨论】:

      【解决方案5】:

      万一有人遇到这种情况:

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

      @IgnoreDependency @EJB MySelf 我自己Ref;

      【讨论】:

      • 这只是 JBoss 的解决方法。从 AS6 开始,它就消失了。
      【解决方案6】:

      我还没有尝试过(我即将尝试),但是通过@EJB 注释注入自引用的替代方法是SessionContext.getBusinessObject() 方法。这将是避免循环引用对您造成影响的另一种方法 - 尽管至少对于无状态 bean 注入似乎确实有效。

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

      【讨论】:

        【解决方案7】:

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

        TransactionAttributeType.Never

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

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

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

        尝试对这两种 bean 方法使用 TransactionAttributeType.Required 看看它是否有效。

        【讨论】:

        • 据我所知,你错过了重点。 Matt 不希望 processObjects 在事务中运行(如果他在 processObjects 上设置 TransactionAttributeType.Required 就会出现这种情况),他希望 process 方法有许多单独的事务。
        【解决方案8】:

        我遇到了 Kevin 提到的这些循环依赖问题。然而,建议的注解 @IgnoreDependency 是一个 jboss 特定的注解,并且在例如 Glassfish 中没有对应的注解。

        由于它不适用于默认的 EJB 引用,我对这个解决方案感到有点不舒服。

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

        除此之外,我认为没有解决方案,只能在另一个 bean 中实现内部 process(),这也很丑陋,因为我们只是想扰乱我们的类模型以获得这些技术细节。

        【讨论】:

        • @IgnoreDependency 不再适用于 JBoss,它是一种 hack 解决方法,从 AS6 开始不再需要。 Glassfish 从来没有遇到过这个问题,也不需要这样的解决方法。
        猜你喜欢
        • 2011-01-22
        • 2017-11-22
        • 2014-06-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-11
        • 2018-01-15
        • 2014-09-03
        相关资源
        最近更新 更多