捕获EJBTransactionRolledbackException

时间:2016-02-04 21:34:32

标签: java java-ee ejb

我对理解EJBTransactionRolledbackException有疑问。

我有实体:

@Entity
public class MyEntity {

    @Id
    @GeneratedValue
    private Long id;
    @Size(max=5)
    private String name;
//...
}
由于CMT的简易性,

和存储库是SLSB:

@Stateless
public class ExampleRepository {
    @PersistenceContext
    private EntityManager em;

    public void add(MyEntity me) {
        em.persist(me);
    }
}

现在我测试了Servlet,当我模拟ConstraintViolation(名字太长)时。

@WebServlet("/example")
public class ExampleServlet extends HttpServlet {
    @Inject
    private ExampleRepository repo;

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        MyEntity me = new MyEntity();
        me.setName("TooLongName");
        try {
            repo.add(me);
        } catch(EJBTransactionRolledbackException e) {
            System.out.println("Exception caught");
        }
    }
}

我知道在这种情况下,EJB容器会包装ConstraintViolationException,因此我会捕获EJBTransactionRolledbackException。问题是在控制台中我可以看到来自catch块的消息(“异常捕获”)但在此之前产生了大量的异常日志(link)。 我不太明白发生了什么 - 这个例外是否被捕获?如何在如此简单的场景中阻止控制台中的所有这些错误消息?

2 个答案:

答案 0 :(得分:2)

请查看以下解释:A clear explanation of system exception vs application exception

您必须了解处理异常和处理事务是两个不同的事情发生在一起。 系统异常无条件触发事务回滚。当您看到ConstraintViolationException,这是一个系统异常,因为它扩展了RuntimeException,它不仅仅是包装和重新投掷。在此过程中发生了一件坏事 - 您的交易已经中止。

所以,回答第一个问题,如果异常(ConstraintViolationException)被捕获 - 是的,它被容器捕获了。事务中止并抛出一个新的异常以通知应用程序代码。

您可以禁止记录这些消息,但是您不会知道数据持久性失败。

答案 1 :(得分:0)

我可以建议你两个解决方案:

  1. 使用bean管理的事务:

    @Stateless
    @TransactionManagement(TransactionManagementType.BEAN)
    public class ExampleRepository {
        @PersistenceContext(synchronization = SynchronizationType.UNSYNCHRONIZED))
        private EntityManager em;
    
        @Resource
        private UserTransaction tx;
    
        public void add(MyEntity me) {
            try {
                tx.begin();
                em.joinTransaction();
                em.persist(me);
                tx.commit();
            } catch (ValidationException ex) {
                throw new AppValidationException(ex);
            }
        }
    }
    
  2. 委托/门面模式:

    按原样保留 ExampleRepository

    @Stateless
    public class ExampleRepository {
        @PersistenceContext
        private EntityManager em;
    
        public void add(MyEntity me) {
            em.persist(me);
        }
    }
    

    创建没有事务的新EJB(使用与初始相同的方法):

    @Stateless
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public class ExampleRepositoryDelegate {
        @EJB
        private ExampleRepository repository;
    
        public void add(MyEntity me) {
            try {
                repository.add(me);
            } catch (ValidationException e) {
                e.printStackTrace();
            }
        }
    }
    

    在servlet中,您使用新的委托bean:

    @WebServlet("/example")
    public class ExampleServlet extends HttpServlet {
        @Inject
        private ExampleRepositoryDelegate repoDelegate;
    
        protected void doGet(HttpServletRequest request, HttpServletResponse response)
                throws ServletException, IOException {
            MyEntity me = new MyEntity();
            me.setName("TooLongName");
            try {
                repoDelegate.add(me);
            } catch(Exception e) {
                System.out.println("Exception caught");
            }
        }
    }