前言
为啥本渣渣会忽然学习Spring事务隔离级别以及传播呢?
在昨天一位同事吐槽说,更新后查询到的是旧数据,so本渣渣决定搞个明白。
案发现场
然后我就尝试了一下 save*(查->update->查),还有xx(查->update->查);
结果
我们数据库是RC级别
第一种情况:查到旧数据,更新后数据库后navicat没有看到更新的数据,查询查到旧数据
第二种情况:查到旧数据,更新后数据库后navicat看到更新的数据,查询查到旧数据(而且还命中缓存了)
也就是第一种情况update的时候是没有提交的。
导致的原因
show you the code
@Getter
@Setter
@Configuration
public class TransactionConfiguration {
private static final String CUSTOM_PLATFORM_TRANSACTION_MANAGER_NAME = "platformTransactionManager";
private static final String CUSTOM_TRANSACTION_INTERCEPTOR_NAME = "customTransactionInterceptor";
private static final String CUSTOM_BEAN_NAME_AUTO_PROXY_CREATOR_NAME = "beanNameAutoProxyCreator";
/**
* 默认的只读事务
*/
private static final String[] DEFAULT_READ_ONLY_METHOD_RULE_TRANSACTION_ATTRIBUTES = { "get*", "count*", "find*",
"query*", "select*", "list*", "*", };
/**
* 默认只对 "*Service" , "*ServiceImpl" Bean 进行事务处理,"*"表示模糊匹配, 比如 :
* userService,orderServiceImpl
*/
private static final String[] DEFAULT_TRANSACTION_BEAN_NAMES = { "*Service", "*ServiceImpl" };
/**
* 可传播事务配置
*/
private static final String[] DEFAULT_REQUIRED_METHOD_RULE_TRANSACTION_ATTRIBUTES = { "add*", "save*", "insert*",
"delete*", "update*", "edit*", "batch*", "create*", "remove*", };
/**
* 自定义事务 BeanName 拦截
*/
private String[] customTransactionBeanNames = {};
/**
* 自定义方法名的事务属性相关联,可以使用通配符(*)字符关联相同的事务属性的设置方法; 只读事务
*/
private String[] customReadOnlyMethodRuleTransactionAttributes = {};
/**
* 自定义方法名的事务属性相关联,可以使用通配符(*)字符关联相同的事务属性的设置方法; 传播事务(默认的)
* {@link org.springframework.transaction.annotation.Propagation#REQUIRED}
*/
private String[] customRequiredMethodRuleTransactionAttributes = {};
@Autowired
@Qualifier(value="druidDataSource")
private DataSource dataSource;
@Bean(name = CUSTOM_PLATFORM_TRANSACTION_MANAGER_NAME)
public PlatformTransactionManager platformTransactionManager() {
return new DataSourceTransactionManager(this.dataSource);
}
/**
* 配置事务拦截器,注意:transactionInterceptor 名称的 bean 可能已经存在,导致该生成 bean
* 的方法不能执行,所以定义成另外一个名字
*
* @param platformTransactionManager
* 事务管理器
* @return
*/
@Bean(name = CUSTOM_TRANSACTION_INTERCEPTOR_NAME)
public TransactionInterceptor customTransactionInterceptor(
@Qualifier(CUSTOM_PLATFORM_TRANSACTION_MANAGER_NAME) PlatformTransactionManager platformTransactionManager) {
NameMatchTransactionAttributeSource transactionAttributeSource = new NameMatchTransactionAttributeSource();
RuleBasedTransactionAttribute readOnly = this.readOnlyTransactionRule();
RuleBasedTransactionAttribute required = this.requiredTransactionRule();
// 默认的只读事务配置
for (String methodName : DEFAULT_READ_ONLY_METHOD_RULE_TRANSACTION_ATTRIBUTES) {
transactionAttributeSource.addTransactionalMethod(methodName, readOnly);
}
// 定制的只读事务配置
for (String methodName : customReadOnlyMethodRuleTransactionAttributes) {
transactionAttributeSource.addTransactionalMethod(methodName, readOnly);
}
// 默认的传播事务配置
for (String methodName : DEFAULT_REQUIRED_METHOD_RULE_TRANSACTION_ATTRIBUTES) {
transactionAttributeSource.addTransactionalMethod(methodName, required);
}
// 定制的传播事务配置
for (String methodName : customRequiredMethodRuleTransactionAttributes) {
transactionAttributeSource.addTransactionalMethod(methodName, required);
}
return new TransactionInterceptor(platformTransactionManager, transactionAttributeSource);
}
/**
* 当前存在事务就使用当前事务,当前不存在事务就创建一个新的事务
* {@link org.springframework.transaction.annotation.Propagation#REQUIRED}
*/
private RuleBasedTransactionAttribute requiredTransactionRule() {
RuleBasedTransactionAttribute required = new RuleBasedTransactionAttribute();
required.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
required.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
required.setTimeout(TransactionDefinition.TIMEOUT_DEFAULT);
return required;
}
/**
* 只读事务
* {@link org.springframework.transaction.annotation.Propagation#NOT_SUPPORTED}
*/
private RuleBasedTransactionAttribute readOnlyTransactionRule() {
RuleBasedTransactionAttribute readOnly = new RuleBasedTransactionAttribute();
readOnly.setReadOnly(true);
readOnly.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
return readOnly;
}
/**
* 配置事务拦截
* <p>
* {@link #customTransactionInterceptor(PlatformTransactionManager)}
*/
@Bean(name = CUSTOM_BEAN_NAME_AUTO_PROXY_CREATOR_NAME)
public BeanNameAutoProxyCreator customTransactionBeanNameAutoProxyCreator() {
BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
// 设置定制的事务拦截器
beanNameAutoProxyCreator.setInterceptorNames(CUSTOM_TRANSACTION_INTERCEPTOR_NAME);
// 默认 + 定制
String[] unions = ArrayUtils.addAll(DEFAULT_TRANSACTION_BEAN_NAMES, customTransactionBeanNames);
beanNameAutoProxyCreator.setBeanNames(unions);
beanNameAutoProxyCreator.setProxyTargetClass(true);
return beanNameAutoProxyCreator;
}
}
这一块在网上也是随手可以搜到的,也就是大家无脑式copy过来后就使用,西西~
分析
上面那一坨代码,有点像xml的配置事务。也就是以方法名去匹配,然后加事务。
那这样的话,再来看下之前那两种情况导致的原因。
第一种情况的原因
save开了一个默认事务,然后在get的时候,事务挂起。update又恢复原来的事务,get又挂起,然后提交。
第二种情况的原因
普通方法是不开启事务的,然后get正常跑,update开启一个事务,get这里是不会挂起update事务的,应该算是子事务,这个时候,update提交事务了。get命中缓存,查到旧数据。
Spring事务传播以及隔离级别学习
spring.io官网走起
spring事务级别
Enum Propagation
PROPAGATION_REQUIRED
默认隔离级别
如果当前有事务则,用当前事务。没有的话创建一个。
注意点
当内层函数出现问题回滚,将导致外层函数一起回滚。
PROPAGATION_REQUIRES_NEW
总是新建事务。
注意点
互相不影响,事务回滚
PROPAGATION_NESTED
事务嵌套。
老母猪戴套,一个又一个,啊哈哈。
就是一个事务里头,又有另一个事务。外头大事务回滚会导致小事务回滚,但是小事务回滚不影响大事务执行。