【问题标题】:JavaEE EJB Can't catch TransactionRolledbackExceptionJavaEE EJB 无法捕获 TransactionRolledbackException
【发布时间】:2013-07-18 12:45:50
【问题描述】:

我正在构建一个小型 JavaEE 项目并有一个存储库类:

@Stateless
public class UserRepository extends AbstractRepository<User> {

    @PersistenceContext
    private EntityManager em;

    public User getByName(String username) {
       CriteriaBuilder criteriaBuilder = ...

       return em.createQuery(query).getSingleResult();
    }
}

我有使用此功能的服务类:

@Stateless
public class UserService extends AbstractService<User> {
    public User getByName(String username) {
        try {
            return userRepository.getByName(username);
        } catch (Throwable e) {
            // says: "Caught: EJBTransactionRolledbackException"
            System.out.println("Caught: " + e.getClass());
            return null;
        }
    }
}

我希望catch (Throwable e) 子句能够捕获此方法中可能发生的任何事情,但显然我错了。我的日志中仍然有一个TransactionRolledbackException。我读了Why can't I catch EJB exceptions?,但这个问题本身并没有真正的答案。

为什么TransactionRolledbackException 仍然被抛出,我该如何防止这种情况发生?另外,它与实际被捕获的EJBTransactionRolledbackException 有什么关系?

我还尝试在 getByName 方法中捕获异常并抛出自定义异常,但没有成功。

【问题讨论】:

    标签: jakarta-ee exception jpa ejb


    【解决方案1】:

    根据您的源代码
    UserService = EJB 客户端
    UserRepository = EJB 被客户端调用/被调用的 EJB
    由于没有指定 Transaction 属性,它默认为 REQUEIRED,因此事务上下文正在从 UserService 传播到 UserRepository,即所有内容都在单个事务中执行。

    从 EJB 3.1 规范第 386 页摘录

    方法条件:Bean方法运行在调用者的上下文中 交易[注A]。这种情况可能发生在必需、强制、 并支持属性。

    你的条件满足这个

    方法异常:所有其他异常和错误

    这基本上意味着抛出系统异常,这是你的情况(应用程序异常是 EJB 中的另一个,太多解释了)

    最重要的部分是跟随容器动作

    容器的动作
    记录异常或错误 [注意 B]。
    将事务标记为回滚。
    丢弃实例 [注 C]。
    向客户端抛出 javax.ejb.EJBTransactionRolledbackException。 [注D]

    因此,根据您的源代码 UserService,客户端将收到 EJBTransactionRolledbackException 请阅读规范以获得更好的理解。

    我希望这能回答你的问题。

    另外,如果你想捕获异常,请添加 try-catch 子句 em.createQuery(query).getSingleResult(); 在 UserRepository 类中。这也将删除您获得的异常跟踪日志。

    【讨论】:

    • 当我在getSingleResult 捕获异常时,我有两个选择: 抛出另一个异常(例如自定义异常),它最终的行为与将被捕获的行为完全相同,但日志是发送垃圾邮件或什么都不做,在这种情况下,服务根本不会注意到任何错误。
    【解决方案2】:

    当您从另一个 EJB 方法调用时,默认情况下它将加入同一个事务。所以回滚只会发生在最外层的方法中。您可以将外部方法更改为使用 BMT 而不是 CMT,然后您将控制事务提交并处理错误。

    如果您希望内部调用独立,也可以使用新事务。

    【讨论】:

    • 我担心我不明白这一点。添加TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 并没有改变任何东西,你认为你可以举个例子吗?
    【解决方案3】:

    这是因为 EJB 的默认行为是使用 (http://docs.oracle.com/javaee/6/api/javax/ejb/TransactionAttributeType.html#REQUIRED),它将所有调用加入到单个事务中。好吧,您可以使用带有 http://docs.oracle.com/javaee/6/api/javax/ejb/TransactionAttributeType.html#REQUIRES_NEW 的 TransactionAttribute 强制创建另一个事务。最好您非常谨慎地决定您将在哪里开始、加入或开始/新事务(例如,您也可以强制拥有当前事务或使其不绑定到任何事务(这有助于提高性能)) .

    问候, 栾

    【讨论】:

    • 正如另一个答案中已经提到的,将TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) 添加到内部方法不会改变任何内容。这是放错地方了吗?另外:为什么我不能捕捉到异常?
    【解决方案4】:

    在 Sameer Mali 的帮助下,我发现了事务被回滚的原因。然后可以通过抛出具有@ApplicationException 注释的异常来避免跟踪日志。此外,异常可能不会从Throwable 继承,但必须从Exception 继承(无论如何这都是合理的)。代码最终是这样的:

    public User getByName(String username) {
       CriteriaBuilder criteriaBuilder = ...
    
       try {
           return em.createQuery(query).getSingleResult();
       } catch (NoSuchResultException e){ // Note: this is a System Exception
           throw new NoSuchUserException(username);
       }
    }
    

    @ApplicationException
    public class NoSuchUserException {
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 2017-11-04
      • 2015-03-31
      • 1970-01-01
      • 1970-01-01
      • 2017-11-13
      • 1970-01-01
      • 1970-01-01
      • 2011-05-25
      • 1970-01-01
      相关资源
      最近更新 更多