错误使用示例:
注意这是一段伪代码不可直接运行,理解大概意思即可,就是@Transational注解作用于pubilc方法,但是public方法又去调用了private修饰的方法导致事务失的问题。
//伪代码
public testService(){
@Transational
public void fun1(){
//与数据库交互的方法1
AObj obja=new AObj("1");
aaMaper.insertInfoA(obja)
fun2();
}
private void fun2(){
//与数据库交互的方法2
BObj objb=new BObj("2");
bbMaper.insertInfoB(objb)
}
}
分析原因:
问题原因
-
代理机制限制:
-
Spring的事务管理是通过AOP动态代理实现的(默认使用JDK动态代理或CGLIB)。
-
只有
public
方法才能被代理,private
/protected
方法或同类内部调用会绕过代理,导致事务失效。
-
-
内部调用问题:
-
在你的代码中,
fun1()
调用fun2()
是同类内部的直接调用(相当于this.fun2()
),而不是通过代理对象调用,因此fun2()
不会加入事务。
-
目前解决方案有3个:
-
将
fun2()
移到另一个Bean中(推荐):@Service public class ServiceB { @Transactional public void fun2() { //与数据库交互的方法2 BObj objb=new BObj("2"); bbMaper.insertInfoB(objb) } } @Service public class ServiceA { @Autowired private ServiceB serviceB; @Transactional public void fun1() { //与数据库交互的方法1 AObj obja=new AObj("1"); aaMaper.insertInfoA(obja) serviceB.fun2(); // 通过代理调用 } }
-
通过AopContext获取代理对象(需开启
exposeProxy
):@EnableAspectJAutoProxy(exposeProxy = true) public class Application { ... } @Transactional public void fun1() { //与数据库交互的方法1 AObj obja=new AObj("1"); aaMaper.insertInfoA(obja) ((YourServiceClass) AopContext.currentProxy()).fun2(); // 通过代理调用 }
-
-
将
fun2()
改为public
并通过外部调用(不推荐,仅部分解决)。
其他注意事项
-
确保数据库引擎支持事务(如InnoDB)。
-
默认仅对
RuntimeException
回滚,可通过@Transactional(rollbackFor=Exception.class)
调整。 -
避免在同一个类中调用
@Transactional
方法。
总结
你的代码中事务会失效,因为fun2()
是通过内部直接调用的。最佳实践是通过Spring Bean之间的调用来触发代理机制。