https://blog.csdn.net/hanjiaqian/article/details/120501741
事务不生效
原理
生成了代理类,在代理类中实现事务功能。
@Service
public class A{
@Transactinal
public void b(){
a();
}
public void a(){ ... }
}
//Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
public class proxy$A{
A objectA = new A();
public void b(){
startTransaction();
objectA.b();
}
public void a(){
objectA.a();
}
}场景
1.访问权限问题
Spring 要求被代理方法必须是public的
2. 方法用 final 修饰
在代理类中,无法重写该方法,添加事务功能
注意:如果某个方法是 static 的,同样无法通过动态代理,变成事务方法。
3.方法内部调用
未加注解的方法调用加注解的方法,事务失效
@Service
public class A{
public void b(){
a();
}
@Transactinal
public void a(){ ... }
}
//Spring扫描注解后,创建了另外一个代理类,并为有注解的方法插入一个startTransaction()方法:
public class proxy$A{
A objectA = new A();
public void b(){
objectA.b();// 这里其实是调用的非代理类的a(),不走事务
}
public void a(){
startTransaction();
objectA.a();
}
}解决方法:
public class AopInvalidDemo {
// @Autowired
// private AopInvalidDemo proxy;
// 方法 1
// @TestInvalid
public String todo() {
// 坑:这个 this 是 代理类中的 target,不走切面
String result = this.todo1();
// 方法 2
// 这个是代理类
// String result = proxy.todo1();
// 方法 3
// 这个是代理类
// AopInvalidDemo proxy = SpringUtil.getBean(AopInvalidDemo.class);
// String result = proxy.todo1();
String r = result + "_666";
return r;
}
@TestInvalid
public String todo1() {
System.out.println("执行了");
return "todo1";
}
}4.未被 Spring 管理
5.多线程调用
如果在不同的线程,拿到的数据库连接肯定是不一样的,所以是不同的事务。
6.表不支持事务
在 mysql5 之前,默认的数据库引擎是myisam
7.未开启事务
事务不回滚
1.错误的传播特性
2.自己吞了异常
3.手动抛了别的异常
4.自定义了回滚异常
5.嵌套事务回滚多了
大事务问题
事务中尽可能只包含必要的、最少的代码,减少耗时。
编程式事务
封装工具类:
public class TransactionHelper {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private ExceptionManager exceptionManager;
private final Logger logger = LogUtil.getLogger(TransactionHelper.class);
/**
* 将方法封装在事务里,
* 事务传播级别-REQUIRED
* 隔离级别-使用数据库默认
*/
public <T> T doInTransactionWithRequired(Supplier<T> supplier) {
return doInTransactionWithCustom(supplier, TransactionDefinition.PROPAGATION_REQUIRED, TransactionDefinition.ISOLATION_DEFAULT);
}
/**
* 将方法封装在事务里,
* 事务传播级别-REQUIRES_NEW
* 隔离级别-使用数据库默认
*/
public <T> T doInTransactionWithRequiresNew(Supplier<T> supplier) {
return doInTransactionWithCustom(supplier, TransactionDefinition.PROPAGATION_REQUIRES_NEW, TransactionDefinition.ISOLATION_DEFAULT);
}
/**
* 将方法封装在事务里,
* 自定义事务传播级别
* 隔离级别-使用数据库默认
*/
public <T> T doInTransactionWithCustom(Supplier<T> supplier, int propagationBehavior) {
return doInTransactionWithCustom(supplier, propagationBehavior, TransactionDefinition.ISOLATION_DEFAULT);
}
/**
* 将方法封装在事务里,
* 自定义事务传播级别
* 自定义隔离级别
*/
public <T> T doInTransactionWithCustom(Supplier<T> supplier, int propagationBehavior, int isolationLevel) {
DefaultTransactionDefinition transDefinition = new DefaultTransactionDefinition();
transDefinition.setPropagationBehavior(propagationBehavior);
transDefinition.setIsolationLevel(isolationLevel);
TransactionStatus status = transactionManager.getTransaction(transDefinition);
T result = null;
Throwable executeException = null;
try {
result = supplier.get();
} catch (Throwable throwable) {
executeException = throwable;
}
boolean executeFailure = ObjectUtil.isNotNull(executeException);
if (executeFailure) {
// 业务代码抛异常——回滚事务
try {
// 如果回滚事务抛异常,为了不影响业务的异常,需要加try-catch,只打印日志,不抛出
transactionManager.rollback(status);
} catch (Throwable ex) {
String msg = "数据库事务回滚失败!";
exceptionManager.logException(msg, ex, logger);
}
// 将业务代码的异常重新抛出去给外部处理
exceptionManager.rethrow(executeException);
} else {
// 业务代码正常执行——提交事务
transactionManager.commit(status);
}
return result;
}
}