设置默认事务隔离
SpringBoot:
可以在属性文件里配置Spring Boot配置事务默认的隔离级:
#隔离级别数字配置的含义:
#-1 数据库默认隔离级别
#1 未提交读 READ UNCOMMITTED
#2 读写提交 READ COMMITTED
#4 可重复读 REPEATABLE READ
#8 串行化 SERIALIZABLE
#hikari数据源默认隔离级别
spring.datasource.hikari.transaction-isolation=2
#tomcat数据源默认隔离级别
spring.datasource.tomcat.default-transaction-isolation=2
#dbcp2数据库连接池默认隔离级别
spring.datasource.dbcp2.default-transaction-isolation=2
MySql:
- 可以在my.ini文件中使用transaction-isolation选项来设置服务器的缺省事务隔离级别。
四种级别:
- READ-UNCOMMITTED
- READ-COMMITTED
- REPEATABLE-READ
- SERIALIZABLE
例如:
[mysqld]
#可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE
transaction-isolation = READ-COMMITTED
- 通过命令动态设置隔离级别:
//设置read uncommitted级别:
set session transaction isolation level read uncommitted;
//设置read committed级别:
set session transaction isolation level read committed;
//设置repeatable read级别:
set session transaction isolation level repeatable read;
//设置serializable级别:
set session transaction isolation level serializable;
查询MySql当前的事物隔离等级用下面语句:
select @@tx_isolation;
//或者
show variables like '%tx_isolation%'
SpringBoot事务实现的方式
1. @Transactional注解
配置在类上:
@Service("menuService")
@Transactional(rollbackFor = Exception.class)
public class MenuServiceImpl implements MenuService {
}
配置在方法上(推荐):
public class MenuServiceImpl implements MenuService {
@Override
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public int insertMenuInfo(List<MenuDto> menuDtoList) {
}
}
💡提示: 如果同时配置在类跟方法上面,那肯定是方法上面的覆盖类上的,推荐方法上的原因是因为粒度更细。
2. 全局事务管理器
package org.wjw.mt;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
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.TransactionDefinition;
import org.springframework.transaction.TransactionManager;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;
/**
* 全局事务管理器 <p/>
* 注意: 1. 要在启动类里面引入注解@EnableTransactionManagement <p/>
* 2. 尽量不要再使用@Transactional注解,如果要使用@Transactional注解,推荐配置在方法上,粒度细 <p/>
* 3. 在使用全局事务的时候,方法命名一定要在下面的规范列表中,切勿出现奇葩命名 <p/>
* @author White Stone
*
* 2021年5月21日
*/
@Aspect
@Configuration
public class TransactionalAopConfig {
/**
* 配置方法过期时间,如果是-1表时永不超时
*/
private final static int METHOD_TIME_OUT = 5*1000;
/**
* 配置切入点表达式
*/
private static final String POINTCUT_EXPRESSION = "execution(* org.wjw.mt.service..*.*(..))";
/**
* 容器注入的事务管理器
*/
@Autowired
private TransactionManager transactionManager;
@Bean
public TransactionInterceptor txAdvice() {
//只读事务,不做更新操作
RuleBasedTransactionAttribute readOnly = new RuleBasedTransactionAttribute();
readOnly.setReadOnly(true);
readOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
//当前存在事务就使用当前事务,当前不存在事务就创建一个新的事务
RuleBasedTransactionAttribute required = new RuleBasedTransactionAttribute();
//抛出异常后执行切点回滚,你可以更换异常的类型
required.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
//PROPAGATION_REQUIRED:事务隔离性为1,若当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。这是默认值
required.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
//设置事务失效时间,如果超过5秒,则回滚事务
required.setTimeout(METHOD_TIME_OUT);
Map<String, TransactionAttribute> attributesMap = new HashMap<>(30);
//设置增删改上传等使用事务
attributesMap.put("save*", required);
attributesMap.put("remove*", required);
attributesMap.put("update*", required);
attributesMap.put("batch*", required);
attributesMap.put("clear*", required);
attributesMap.put("add*", required);
attributesMap.put("append*", required);
attributesMap.put("modify*", required);
attributesMap.put("edit*", required);
attributesMap.put("insert*", required);
attributesMap.put("delete*", required);
attributesMap.put("do*", required);
attributesMap.put("create*", required);
attributesMap.put("import*", required);
//查询开启只读
attributesMap.put("select*", readOnly);
attributesMap.put("get*", readOnly);
attributesMap.put("valid*", readOnly);
attributesMap.put("list*", readOnly);
attributesMap.put("count*", readOnly);
attributesMap.put("find*", readOnly);
attributesMap.put("load*", readOnly);
attributesMap.put("search*", readOnly);
//事务管理规则,声明具备事务管理的方法名
NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
source.setNameMap(attributesMap);
return new TransactionInterceptor(transactionManager, source);
}
/**
* 设置切面=切点pointcut+通知TxAdvice
*/
@Bean
public Advisor txAdviceAdvisor() {
//声明切点的面:切面就是通知和切入点的结合。通知和切入点共同定义了关于切面的全部内容——它的功能、在何时和何地完成其功能
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
//声明和设置需要拦截的方法,用切点语言描写
pointcut.setExpression(POINTCUT_EXPRESSION);
//设置切面=切点pointcut+通知TxAdvice
return new DefaultPointcutAdvisor(pointcut, txAdvice());
}
}
💡提示: 可以根据自己的需求更改attributesMap的内容等
注意点:
- 事务使用之前,要在启动类里面引入注解*@EnableTransactionManagement*
- 如果要使用*@Transactional*注解,推荐配置在方法上,粒度细
- 实际项目中还是推荐全局事务管理的,就怕有同事不写注解
- 使用全局事务的时候,方法命名一定要在上述规范中,切勿出现奇葩命名