@Transactional方法内调用失效但加在类上能够生效

首先说一下方法内调用失败的问题,这个网上很多说明,大概说一下:

@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()是没有事物控制的,所以无法生效

如何生效?

  1. 保证要调用的事物方法位于第三类中,可以将代码抽出单独的一个类文件中,实现对象级别调用,这个调用会获取到代理对象并执行代理对象的方法,实现事物控制
  2. 通过AopContext获取当前代理对象,执行代理对象的方法
  3. @Transactional加在类上(与事物传播特性有关)
  4. 使用自身对象调用自身方法(会存在循环依赖)

这里有个问题:为什么方法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是具备事物的。不过这种循环依赖还是尽量避免

你可能感兴趣的:(spring)