首先说一下方法内调用失败的问题,这个网上很多说明,大概说一下:
@Transactional
用于开启事务,可以加在方法上或者类上,当加在方法上时,方法内部调用会由于不经过代理类而造成事物失效,如下:
Class A {
public vode m1(){
m2();
}
@Transactional
public vode m2(){
}
}
上面的m2()
是无法开启事务的,这是由于在A在注入Spring容器时,注入的并不是A的实例对象,而是经过AOP处理器进行代理后的A$proxy
,即通过ApplicationContext.getBean("a")
获得的是 A$proxy
的实例对象
Class A$proxy {
private A a;
public vode m1(){
a.m1();
}
public vode m2(){
startTransactional;
a.m2();
commit;
}
}
A$proxy
如上所示,当通过A$proxy
调用m2()
时,是可以开启事务的,但当调用m1()
时,会进入到A的m1()
方法,然后会调用A的m2()
方法,但这个m2()
是没有事物控制的,所以无法生效
如何生效?
- 保证要调用的事物方法位于第三类中,可以将代码抽出单独的一个类文件中,实现对象级别调用,这个调用会获取到代理对象并执行代理对象的方法,实现事物控制
- 通过AopContext获取当前代理对象,执行代理对象的方法
- 将
@Transactional
加在类上(与事物传播特性有关) - 使用自身对象调用自身方法(会存在循环依赖)
这里有个问题:为什么方法3将注解加在类上就起作用了?
首先,加在类上后,会为当前类中所有方法加上事物控制方法,如下:
Class A$proxy {
private A a;
public vode m1(){
startTransactional;
a.m1();
commit;
}
public vode m2(){
startTransactional;
a.m2();
commit;
}
}
这时候,调用m1()
,m1()
开启事务,进入到A中的m1()
,A中的m1()
会调用自己的m2()
,由于m1()
位于事物中且Spring的默认事物传播机制为REQUIRE,即m2()
会加入到m1()
的事物中,从而也会有事物保证。
因此通过分析可知,方法3的生效前提与采用的事物传播机制有关,而方法1和方法2是与事物传播无关的
实验
@RestController
@RequestMapping("/teacher")
public class TeacherController {
private TeacherService teacherService;
@Autowired
public TeacherController(TeacherService teacherService) {
this.teacherService = teacherService;
}
@GetMapping("/wrapper")
public void wrapper(){
teacherService.wrapper();
}
}
@Service("teacherService")
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public class TeacherServiceImp implements TeacherService {
private MysqlTeacherMapper mysqlTeacherMapper;
@Autowired
public TeacherServiceImp(MysqlTeacherMapper mysqlTeacherMapper) {
this.mysqlTeacherMapper = mysqlTeacherMapper;
}
@Override
public void insert(Teacher teacher) {
int i = mysqlTeacherMapper.insert(teacher);
System.out.println(i);
throw new RuntimeException("test");
}
@Override
public void wrapper() {
Teacher teacher = new Teacher();
teacher.setId(1);
teacher.setName("mutouboy");
insert(teacher);
}
}
以上代码在service
层设置了事物传播特性为不支持,即insert()
不会加入到wrapper()
的事物中去,实验结果表明记录能够成功插入,本文结论得证
第四种
第四种代码如下:
@Autowired
private TeacherService teacherService;
@Autowired
public TeacherServiceImp(MysqlTeacherMapper mysqlTeacherMapper) {
this.mysqlTeacherMapper = mysqlTeacherMapper;
}
@Override
@Transactional
public void insert(Teacher teacher) {
int i = mysqlTeacherMapper.insert(teacher);
System.out.println(i);
throw new RuntimeException("test");
}
@Override
public void wrapper() {
Teacher teacher = new Teacher();
teacher.setId(1);
teacher.setName("mutouboy");
teacherService.insert(teacher);
}
这种情况下inset
同样具备事物,因为teacherServiceimpl
自身注入了自身(这时候就不能用构造器注入了),且这个注入的是个代理,如下:
因此调用inset
是具备事物的。不过这种循环依赖还是尽量避免