spring事务的使用
1. 未启用spring事务管理功能@EnableTransactionManagement
Spring事务的实现原理的往aop里面写入一个advisor,如果没有开启这个EnableTransactionManagement的话就不会注册advisor,也就是没有使用。
注:springboot框架下面不用手动开启,start里面已经自己开启了这个注释
~/org/springframework/boot/spring-boot-autoconfigure/2.3.0.RELEASE/spring-boot-autoconfigure-2.3.0.RELEASE.jar!/META-INF/spring.factories
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(PlatformTransactionManager.class)
@AutoConfigureAfter({ JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class })
@EnableConfigurationProperties(TransactionProperties.class)
public class TransactionAutoConfiguration {
...
@Configuration(proxyBeanMethods = false)
@ConditionalOnBean(TransactionManager.class)
@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
public static class EnableTransactionManagementConfiguration {
@Configuration(proxyBeanMethods = false)
//这边注入了
@EnableTransactionManagement(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
//这边注入了
@EnableTransactionManagement(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
}
2. 方法不是public类型的
因为事务底层的实现原理是aop,aop增强的必须是public的,private的只能同一个方法内部调用
3. 数据源未配置事务管理器
这个很好理解,如果不是同一个事务管理器的话,连接池就不是同一个,就不存在事务的概念了,事务底层还是依赖mysql的事务去做的
4. 自身调用问题
因为spring事务是基于aop来实现的,必须调用的代理对象才行,直接调用的话就是方法内调用,没有加任何切面,所以也不会有事务
public void test() {
System.out.println("执行M1");
this.m1();
}
@Transactional
public void m1() {
jdbcTemplate.update(insertQuery1, 101, "Bob", 50000.00);
jdbcTemplate.update(badSql, 1, "A", 25);
System.out.println("数据1插入成功!");
}
5. 异常类型错误
他这边默认只会回滚RuntimeException,可以在注解上面配置rollbackFor
判断是否回滚代码
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
这样自己指定异常类型的话,当抛出exception异常时都会处理,回滚
@Transactional(rollbackFor =Exception.class )
实例
如果不是runtimeException的话,不会回滚,第一条写入语句正常
@Transactional
public void t1() throws IOException{
jdbcTemplate.update(insertQuery1, 102, "Bob", 50000.00);
System.out.println("数据1插入成功!")
//这边抛出ZipException,非runtimeException,
throw new IOException("zip exception");
}
6. 异常被吞了
这边有2种情况,差别在于是否在方法中有抛出异常,如果有抛出异常,整一个大事务都会失效,如果在刚抛出的异常时catch了,就不会回滚其他,导致事务失败
(1)事务未执行,一半成功,一半失败,
@Transactional
public void insertData() {
String insertQuery1 = "INSERT INTO student (id, name, age) VALUES (?, ?, ?)";
jdbcTemplate.update(insertQuery1, 1, "A", 25);
System.out.println("数据1插入成功!");
dataService.insertData2();
}
@Transactional
public void insertData2() {
String insertQuery2 = "INSERT INTO student (id, name, salll) VALUES (?, ?, ?)";
try {
jdbcTemplate.update(insertQuery2, 101, "Bob", 50000.00);
} catch (Exception e) {
System.out.println("数据2插入失败 = " + e);
return;
}
System.out.println("数据2插入成功!");
}
(2)事务执行,全部失败(可通过配置修改)
这种情况下是全部回滚了,报错,可以配置这个参数,他就是只会滚一部分
transaction.UnexpectedRollbackException: Transaction rolled back because it has been marked as rollback-only
@Transactional
public void insertData() {
String insertQuery1 = "INSERT INTO student (id, name, age) VALUES (?, ?, ?)";
jdbcTemplate.update(insertQuery1, 1, "A", 25);
System.out.println("数据1插入成功!");
try {
dataService.insertData2();
} catch (Exception e) {
System.out.println("数据2插入失败 = " + e);
}
}
@Transactional
public void insertData2() {
String insertQuery2 = "INSERT INTO student (id, name, salll) VALUES (?, ?, ?)";
jdbcTemplate.update(insertQuery2, 101, "B", 50000.00);
System.out.println("数据2插入成功!");
}
7. 业务和spring事务代码必须在一个线程中
因为连接是保存在threadLocal里面的,如果不是同一个线程的话拿到的数据库连接就不是同一个,会导致事务失效
8. 多个方法调用时,事务传播机制不是同一个事务
badSql是错误的sql,insertQuery1是正常的sql,当隔离级别设置为REQUIRES_NEW时,insertData2正常执行,已经提交后insertData报错,这时候insertData2不会回滚,
@Transactional
public void insertData() {
dataService.insertData2();
jdbcTemplate.update(badSql, 1, "A", 25);
System.out.println("数据1插入成功!");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void insertData2() {
jdbcTemplate.update(insertQuery1, 101, "Bob", 50000.00);
System.out.println("数据2插入成功!");
}