26

我有一个无状态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,但欢迎/首选非特定答案。

4

8 回答 8

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();
    }
}

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

于 2009-02-04T15:46:23.633 回答
4

马特,你问的问题是一个非常经典的问题,我认为 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();
    }
}
于 2010-04-17T22:06:24.760 回答
2

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

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

于 2008-12-04T16:32:11.030 回答
1

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

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

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

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

于 2008-12-04T16:15:50.130 回答
1

如果有一天有人遇到这个:

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

@IgnoreDependency @EJB MySelf 我自己Ref;

于 2010-05-07T19:09:07.247 回答
1

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

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

于 2012-04-11T15:25:12.737 回答
0

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

TransactionAttributeType.Never

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

如果客户端在事务中运行并调用企业 bean 的方法,则容器会抛出 RemoteException。如果客户端未与事务关联,则容器在运行该方法之前不会启动新事务。

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

尝试对这两种 bean 方法使用TransactionAttributeType.Required以查看它是否可以解决问题。

于 2008-09-20T00:01:37.807 回答
0

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

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

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

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

于 2011-09-26T14:31:46.483 回答