【问题标题】:Spring: automatic rollback on checked exceptionsSpring:检查异常的自动回滚
【发布时间】:2017-12-07 12:11:56
【问题描述】:

配置 Spring 以在非 RuntimeExceptions 上回滚的一种方法是在服务类上使用 @Transactional(rollbackFor=...) 注释。这种方法的问题是我们需要为几乎所有看起来真的多余的服务类定义 (rollbackFor=...)。


我的问题:有没有办法为 Spring 事务管理器配置默认行为,以便在发生时回滚非 RuntimeException,而无需在每个 @Transactional 注释上声明它。类似于在 EJB 中的异常类上使用 @ApplicationException(rollback=true) 注释。

【问题讨论】:

标签: java spring transactional


【解决方案1】:

您不能使用 @Transactional 为应用程序级别执行此操作,但您可以:

variant 1 : 扩展@Transactional 注释并将其作为rollbackfor 的默认值。但是只设置你需要的rollbackFor unchecked exceptions。有了这个,你可以控制回滚只在你确定的情况下,并避免复制过去@Transactional(rollbackFor =MyCheckedException.class)

喜欢:

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Transactional(rollbackFor=MyCheckedException.class)
public @interface TransactionalWithRollback {
}

并使用这个注解代替标准的@Transactional。

变体2:您可以从AnnotationTransactionAttributeSource创建扩展并覆盖方法determineTransactionAttribute:

protected TransactionAttribute  determineTransactionAttribute(AnnotatedElement ae)
//Determine the transaction attribute for the given method or class.

TransactionAttribute 见TransactionAttribute api,有方法

boolean rollbackOn(Throwable ex) 我们应该回滚给定的异常吗?

protected TransactionAttribute determineTransactionAttribute(
    AnnotatedElement ae) {
    return new DelegatingTransactionAttribute(target) {
        @Override
        public boolean rollbackOn(Throwable ex) {
           return (check is exception type as you need for rollback );
       }
};

}

第二种方法不如第一种好,因为它对事务管理器来说确实是全局的。更好地使用自定义注释,因为您可以控制它,仅适用于您真正需要它的方法/类。但是,如果您在任何情况下都需要它,请使用第二种变体,这将是您的默认跨国行为。

【讨论】:

  • 谢谢,扩展@Transactional注解是个好主意。
  • @xyz 多年来我一直使用第二个变体,但使用 spring boot 2.1 / spring 5.1 它停止工作。没有错误,但@Transactional 的行为现在是通常的行为,而不是想要的行为。 – t777 7 分钟前
  • @xyz 我正在考虑采用您的第二个变体,因为它会更容易,并且其他使用代码的程序员不必考虑它。但我不想犯任何错误。 - 为什么不像你的第二个变体中提到的那样在全球范围内做“这么好”?是否存在不回滚已检查异常的情况?我真的很好奇。提前致谢!
【解决方案2】:

这个配置解决了它:

@Configuration
public class MyProxyTransactionManagementConfiguration extends ProxyTransactionManagementConfiguration {

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public TransactionAttributeSource transactionAttributeSource() {
        return new AnnotationTransactionAttributeSource() {

            @Nullable
            protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
                TransactionAttribute ta = super.determineTransactionAttribute(element);
                if (ta == null) {
                    return null;
                } else {
                    return new DelegatingTransactionAttribute(ta) {
                        @Override
                        public boolean rollbackOn(Throwable ex) {
                            return super.rollbackOn(ex) || ex instanceof Exception;
                        }
                    };
                }
            }
        };
    }
}

【讨论】:

  • 全球改变事物的最佳解决方案,谢谢!
  • @Nullable 在我的项目中是未知注释 - 它是什么春季版本?另外,将此配置类添加到我的项目中会阻止服务启动。由于 NPE,它无法实例化 BeanFactoryTransactionAttributeSourceAdvisor。知道为什么会发生这种情况吗?!
  • Spring 版本是 5。查看堆栈跟踪以了解 NPE 来自何处。
  • 您可以将“return super.rollbackOn(ex) || ex instanceof Exception”简化为“return true”,因为 super 调用已经为 Error 返回 true,因此如果添加 Exception,它会覆盖所有 Throwable子类。
【解决方案3】:

这是一种与this answer 类似的方法,即全局更改默认值,但对 Spring 的配置进行尽可能小的更改,并且仍然可以像往常一样自定义每个方法的回滚规则(rollbackFor,@ 987654323@等)。

这可以通过简单地为Exception.class 添加一个默认的RollbackRule 来实现。由于规则根据异常类层次结构具有优先级(适用的最具体异常类的规则胜出),如果注释上没有定义其他规则,则新规则基本上具有最低优先级。

@Configuration
public class MyTransactionManagementConfiguration {
  /**
   * Note: This custom config does NOT recognize {@code javax.transaction.Transactional} annotations in contrast to
   * the original Spring behaviour. Check the original {@code AnnotationTransactionAttributeSource} source code for an idea how to add that.
   *
   * @see AnnotationTransactionAttributeSource#AnnotationTransactionAttributeSource(boolean)
   */
  @Bean
  @Primary
  @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
  public TransactionAttributeSource transactionAttributeSourceWithDefaultRollBackForAllExceptions() {
    return new AnnotationTransactionAttributeSource(
        new SpringTransactionAnnotationParser() {
          
          @Override
          protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
            RuleBasedTransactionAttribute rbta = (RuleBasedTransactionAttribute) super.parseTransactionAnnotation(attributes);
            List<RollbackRuleAttribute> rules = new ArrayList<>(rbta.getRollbackRules());
            rules.add(new RollbackRuleAttribute(Exception.class));
            rbta.setRollbackRules(rules);
            return rbta;
          }

        }
    );
  }
}

【讨论】:

    猜你喜欢
    • 2020-10-18
    • 2018-12-24
    • 2012-08-20
    • 2015-03-21
    • 1970-01-01
    • 2019-06-30
    • 1970-01-01
    • 1970-01-01
    • 2013-05-17
    相关资源
    最近更新 更多