异常在已捕获后传播

发布于 2024-12-11 13:10:26 字数 1194 浏览 0 评论 0 原文

我遇到了最奇怪的事情,但我不明白为什么。描述这一点的最佳方法是提供一个简单的示例:

@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 对象内部包含自定义消息。所以,我不知道为什么在捕获并处理异常后仍然抛出异常。有谁知道为什么会发生这种情况?

I'm having the strangest thing happening and I can't figure out why. The best way to describe this is to provide a simplistic example:

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

In this particular case, I am trying to delete an object which has a constraint violation (ORA-02292). I expect the delete to fail because of this. When the delete fails, I wish to show the user an appropriate custom message.

Instead of being able to show the user a custom message, the call fails and displays the following to the screen:

org.springframework.transaction.UnexpectedRollbackException: Transaction
rolled back because it has been marked as rollback-only

When I use a debugger, I can see that the error is appropriately caught and that the ModelAndView object has the custom message inside of it. So, I have no clue why an exception is still being thrown after it has been caught and dealt with. Does anyone have insight into why this is happening?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

慢慢从新开始 2024-12-18 13:10:26

在@Transactional 注释上,您可以使用noRollbackForClassName 属性来声明是否由于给定异常而回滚事务。你可以做类似这样的事情。

@Service
@Transactional(noRollbackForClassName = "java.lang.Exception")
public class YourClass {
    ...
}

但是,请注意,仅仅说 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 the noRollbackForClassName attribute. You can do it similar to this.

@Service
@Transactional(noRollbackForClassName = "java.lang.Exception")
public class YourClass {
    ...
}

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.

冰魂雪魄 2024-12-18 13:10:26

问题是因为一旦抛出异常,Spring 会在内部将 tx 标记为仅回滚。这与 Java 异常处理完全分开。您有多种选择:

  • 确保您预期的异常不会引发扩展 RuntimeException 的异常; Spring仅在类型为RuntimeException时回滚tx(请参阅本页,第 10.5.3 节)。 HibernateException 扩展了 RuntimeException,所以这就是你的原因重新获取回滚标记。
  • 通过将事务方法移至其自己的类并使用 @Transactional(propagation=Propagation.REQUIRES_NEW) 进行注释,在自己的事务中运行每个交易。那么每个调用都会在自己的 tx 中运行,不会影响整体 tx。
  • 使用提到的 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:

  • Make sure your expected exception does not throw exceptions which extend RuntimeException; Spring only rolls back tx's when its a type RuntimeException (see this page, section 10.5.3). HibernateException extends RuntimeException, so that's why you're getting the rollback marker.
  • Run each tx in its own transaction by moving the transactional method to its own class and annotating it using @Transactional(propagation=Propagation.REQUIRES_NEW). Then each call will run in its own tx and will not affect the overall tx.
  • Use the noRollbackForClassName style venushka mentioned. But use with caution, for the reason mentioned.
一个人的旅程 2024-12-18 13:10:26

异常在 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.

︶ ̄淡然 2024-12-18 13:10:26

将属性“globalRollbackOnParticipationFailure”添加到 hibernateTransactionManager bean 定义中,如下所示。

<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="hibernateSessionFactory" />
    **<property name="globalRollbackOnParticipationFailure" value="false" />**
</bean>

Add property "globalRollbackOnParticipationFailure" to the hibernateTransactionManager bean definition as follows.

<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="hibernateSessionFactory" />
    **<property name="globalRollbackOnParticipationFailure" value="false" />**
</bean>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文