捕获 EJBTransactionRolledbackException
Catching EJBTransactionRolledbackException
我对 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 块的消息 ("Exception caught"),但在此之前会生成大量异常日志 (link)。
我不太明白发生了什么 - 这个异常是否被捕获?如何在这种简单的场景中防止控制台中出现所有这些错误消息?
我可以建议你两个解决方案:
使用 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);
}
}
}
委托/门面模式:
您将 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");
}
}
}
请看这个解释:A clear explanation of system exception vs application exception
你必须明白处理异常和处理事务是同时发生的两件不同的事情。 系统异常无条件触发事务回滚。当你看到一个 ConstraintViolationException
,这是一个系统异常,因为它扩展了 RuntimeException
,它不仅仅是包装和重新抛出。一路上发生了一件坏事 - 您的交易已中止。
所以,如果异常 (ConstraintViolationException
) 被捕获,那么回答第一个问题——是的,它被容器捕获了。事务被中止并抛出一个新的异常以通知应用程序代码。
您可以禁止记录这些消息,但这样您就不会知道数据持久性失败。
我对 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 块的消息 ("Exception caught"),但在此之前会生成大量异常日志 (link)。 我不太明白发生了什么 - 这个异常是否被捕获?如何在这种简单的场景中防止控制台中出现所有这些错误消息?
我可以建议你两个解决方案:
使用 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); } } }
委托/门面模式:
您将 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"); } } }
请看这个解释:A clear explanation of system exception vs application exception
你必须明白处理异常和处理事务是同时发生的两件不同的事情。 系统异常无条件触发事务回滚。当你看到一个 ConstraintViolationException
,这是一个系统异常,因为它扩展了 RuntimeException
,它不仅仅是包装和重新抛出。一路上发生了一件坏事 - 您的交易已中止。
所以,如果异常 (ConstraintViolationException
) 被捕获,那么回答第一个问题——是的,它被容器捕获了。事务被中止并抛出一个新的异常以通知应用程序代码。
您可以禁止记录这些消息,但这样您就不会知道数据持久性失败。