异常在已捕获后传播
我遇到了最奇怪的事情,但我不明白为什么。描述这一点的最佳方法是提供一个简单的示例:
@Service
@Transactional
public class Foo{
public ModelAndView delete(@ModelAttribute("abc") Long id) {
ModelAndView mav = new ModelAndView();
try {
getDaoService().delete(id); //Calls Bar.delete()
} catch (final Exception e) {
// Add a custom error message to the mav for the user to see
mav.getModelMap().addAttribute(blah, blah);
}
return mav;
}
}
@Service
@Transactional
public class Bar {
public void delete(final E entity) throws HibernateException {
if (null != entity) {
try {
sessionFactory.getCurrentSession().delete(entity);
} finally {
sessionFactory.getCurrentSession().flush();
}
}
}
}
在这种特殊情况下,我试图删除一个违反约束的对象 (ORA-02292)。我预计删除会因此而失败。当删除失败时,我希望向用户显示适当的自定义消息。
调用失败并在屏幕上显示以下内容,而不是向用户显示自定义消息:
org.springframework.transaction.UnexpectedRollbackException:事务 已回滚,因为它已被标记为仅回滚
当我使用调试器时,我可以看到错误已被正确捕获,并且 ModelAndView 对象内部包含自定义消息。所以,我不知道为什么在捕获并处理异常后仍然抛出异常。有谁知道为什么会发生这种情况?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在@Transactional 注释上,您可以使用
noRollbackForClassName
属性来声明是否由于给定异常而回滚事务。你可以做类似这样的事情。但是,请注意,仅仅说
noRollbackForClassName = "java.lang.Exception"
就意味着它不会回滚任何异常(或其子类),因此这不是一个好的做法。您应该做的是,首先找出实际抛出的异常(可能是通过打印
e.getClass().getName()
),然后将该类名称设置为 noRollbackForClassName 值。明智的原因是,发生这种情况是因为如果在尝试删除()时抛出一些异常,当前事务会自动标记为仅回滚,并且如果尝试提交,您看到的异常将被抛出。传递此问题的方法是明确声明此特定异常不应导致回滚。
On the
@Transactional
annotation, you can state whether or not to roll back your transaction due to a given exception using thenoRollbackForClassName
attribute. You can do it similar to this.However, note that just saying
noRollbackForClassName = "java.lang.Exception"
would mean it will not rollback for any Exception (or its subclasses), hence its not a good practice.What you should do is, figure out what exception is actually thrown first (may be by printing out the
e.getClass().getName()
), then set that class name as the noRollbackForClassName value.Reason wise, this is happening because if some exception is thrown while attempting to delete(), the current transaction is automatically marked as roll back only, and if it is attempted to be committed, the exception you see will be thrown. The way to get passed this is to explicitly state that this certain exception should not cause a roll back.
问题是因为一旦抛出异常,Spring 会在内部将 tx 标记为仅回滚。这与 Java 异常处理完全分开。您有多种选择:
RuntimeException
的异常; Spring仅在类型为RuntimeException
时回滚tx(请参阅本页,第 10.5.3 节)。 HibernateException 扩展了 RuntimeException,所以这就是你的原因重新获取回滚标记。noRollbackForClassName
样式 venushka。但出于上述原因,请谨慎使用。The issue is because once an exception is thrown, Spring internally marks the tx as rollback-only. This is completely separate from Java exception handling. You have several options:
RuntimeException
; Spring only rolls back tx's when its a typeRuntimeException
(see this page, section 10.5.3). HibernateException extends RuntimeException, so that's why you're getting the rollback marker.@Transactional(propagation=Propagation.REQUIRES_NEW)
. Then each call will run in its own tx and will not affect the overall tx.noRollbackForClassName
style venushka mentioned. But use with caution, for the reason mentioned.异常在 Bar#delete 中抛出,并在 Foo#delete 中捕获。 Bar#delete 上有一个 @Transactional 注解,在捕获异常之前会交叉该注解。该内部事务正在参与外部事务,因此整个事务被标记为回滚。
为了避免这种情况,您可以删除 Bar#delete 的 @Transactional 注释。该方法已在其他事务的范围内被调用。
The Exception is being thrown in Bar#delete and is caught in Foo#delete. There is a @Transactional annotation on Bar#delete which is crossed before the exception is caught. This inner transaction is participating in the outer transaction and so the entire transaction is marked for rollback.
To avoid this you could remove the @Transactional annotation for Bar#delete. This method is already called within the scope of the other transaction.
将属性“globalRollbackOnParticipationFailure”添加到 hibernateTransactionManager bean 定义中,如下所示。
Add property "globalRollbackOnParticipationFailure" to the hibernateTransactionManager bean definition as follows.