spring boot 全局事务配置

微笑、不失礼 提交于 2020-11-11 21:44:55

我发现很多开源的springBoot项目,使用事务都是 直接使用 事务注解。并没有配置全局事务的。
其实目前现在不是新人程序员就以为 事务就只能靠加注解来控制了。根本没听说过全局事务配置。
网上很多全局事务其实都是不够好的。都是抄来抄去的。真的不知道能不能用。
其实这样很不好的。

  1. 写代码的时候如果漏了加上事务注解,那异常不回滚太可怕了
  2. 如果写代码的时候都需要手动加上注解,多费事啊。配置全局事务注解多省事。

配置代码


package com.door.config;

import org.aspectj.lang.annotation.Aspect;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.*;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/** 全局事务控制,防止漏加 事务注解,也就是说开发中可以不用加事务注解了 */
@Configuration
@Aspect
public class TransactionConfig {
  private static final int TX_METHOD_TIMEOUT = 60;

  private static final String AOP_POINTCUT_EXPRESSION = "execution(* com.door..service..*(..))";

  @Autowired private PlatformTransactionManager transactionManager;

  @Bean
  public TransactionInterceptor txAdvice() {
    NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();

    RuleBasedTransactionAttribute readOnly = new RuleBasedTransactionAttribute();
    readOnly.setReadOnly(true);
    readOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    // 禁止使用这种 。这种会导致 只读事务不生效的。而且 可以 在代码里面 提交事务的
    //    readOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);

    RuleBasedTransactionAttribute required = new RuleBasedTransactionAttribute();
    required.setRollbackRules(
        Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
    required.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

    // 单位是 秒
    required.setTimeout(TX_METHOD_TIMEOUT);
    Map<String, TransactionAttribute> txMap = new HashMap<>(50);
    txMap.put("add*", required);
    txMap.put("save*", required);
    txMap.put("insert*", required);
    txMap.put("update*", required);
    txMap.put("delete*", required);
    txMap.put("create*", required);
    txMap.put("init*", required);
    txMap.put("submit*", required);
    txMap.put("remove*", required);
    txMap.put("edit*", required);
    txMap.put("modify*", required);
    txMap.put("batch*", required);
    txMap.put("mass*", required);
    txMap.put("handle*", required);
    txMap.put("exec*", required);
    txMap.put("import*", required);
    txMap.put("set*", required);

    txMap.put("get*", readOnly);
    txMap.put("select*", readOnly);
    txMap.put("list*", readOnly);
    txMap.put("query*", readOnly);
    txMap.put("find*", readOnly);
    txMap.put("count*", readOnly);
    txMap.put("page*", readOnly);
    txMap.put("all*", readOnly);
    txMap.put("*", readOnly);

    source.setNameMap(txMap);
    return new TransactionInterceptor(transactionManager, source);
  }

  //  @Bean 这种方式不够好,不能配置回滚异常。 其实和上面的方式 的原理是一样的。
  //  public TransactionInterceptor txAdvice() {
  //
  //    DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
  //    txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  //    txAttr_REQUIRED.setTimeout(TX_METHOD_TIMEOUT);
  //
  //    DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
  //    txAttr_REQUIRED_READONLY.setPropagationBehavior(
  //        TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
  //    txAttr_REQUIRED_READONLY.setReadOnly(true);
  //
  //    NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
  //    source.addTransactionalMethod("add*", txAttr_REQUIRED);
  //    source.addTransactionalMethod("save*", txAttr_REQUIRED);
  //    source.addTransactionalMethod("delete*", txAttr_REQUIRED);
  //    source.addTransactionalMethod("update*", txAttr_REQUIRED);
  //    source.addTransactionalMethod("exec*", txAttr_REQUIRED);
  //    source.addTransactionalMethod("set*", txAttr_REQUIRED);
  //
  //    //    source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
  //    //    source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
  //    //    source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
  //    //    source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
  //    //    source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
  //    source.addTransactionalMethod("*", txAttr_REQUIRED_READONLY);
  //
  //
  //
  //    return new TransactionInterceptor(transactionManager, source);
  //  }

  @Bean
  public Advisor txAdviceAdvisor() {
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
    return new DefaultPointcutAdvisor(pointcut, txAdvice());
  }
}


TransactionDefinition.PROPAGATION_NOT_SUPPORTED 不要配置

以前同事就是这样配置的,之前一直都没有注意到,今天测试 只读事务的时候,才发现根本没有起作用。
还是可以在 方法里面执行 写数据库操作的,而且并没有事务控制!!

source.setNameMap(txMap); 与 addTransactionalMethod 是一样的原理

source.setNameMap(txMap); 的源码 就是 循环调用 addTransactionalMethod 方法

搜索很多都是 DefaultTransactionAttribute 这样的代码,并不能定义Execption异常类型回滚

xml 全局事务配置

没有使用spring boot 开发之前的老项目,基本都是 使用 xml 进行配置全局事务拦截的

 <!--事务配置示例-->
    <aop:config proxy-target-class="true">
		<aop:advisor advice-ref="txAdvice" pointcut="execution(* com.skg.crm.*.domain..*(..)) "  />
	</aop:config>  
	
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
		    <!--对于oracle来说 read-only="true" 没什么效果,
		    	配置回滚异常是为了防止 不小心使用了操作数据出现异常没有回滚。
		    	如果方法里面没有操作数据而出现异常,配置回滚没有影响   -->
		  	<tx:method name="get*" read-only="true" rollback-for="java.lang.Exception" />
			<tx:method name="is*" read-only="true" rollback-for="java.lang.Exception" />
			<tx:method name="find*" read-only="true" rollback-for="java.lang.Exception" />
			<tx:method name="query*" read-only="true" rollback-for="java.lang.Exception" />
			<tx:method name="*" propagation="REQUIRED" read-only="false" isolation="DEFAULT" rollback-for="java.lang.Exception"/>
		</tx:attributes>
	</tx:advice>
	
	 <!-- 开启事务控制的注解支持,proxy-target-class="true"类代理适用范围广  -->
	 <!-- order 一般是不用配置的,如果aop xml 配置了事务了,代码又使用了事务的注解,那么以代码的注解为主。
	 带上order 是防止以后代码上有其他自定义的AOP 而影响了事务注解的aop的异常的事务回滚,默认事务注解的aop的优先级最低。
	 加上也是为了明确告诉spirng 声明式事务注解优先级最高。
	   -->  
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" order="200"/>

参考

事务配置
拦截点配置

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!