springBean嵌套调用自己内部方法的事务问题

如下代码:
同一个service中的三个方法save/insert/update,
要求在save中调用insert和update,save方法没有事务,insert和update这两个方法在独立的事务中。
即如果update方法发生异常后回滚,不影响insert的执行结果。

@Service
public class DemoService{
	
	@Autowired
	private DemoDao demoDao;
	
	//该方法没有声明事务
	public void save(Demo demo){
		//要求insert()和update()在单独的事务中。
		demo.setRemark("新增");
		insert(demo);
		
		demo.setRemark("更新");
		update(demo);
	}

	//声明事务并指定回滚异常,默认就是RuntimeException,也可以省略不指定
	@Transactional(rollbackFor = RuntimeException.class)
	public void insert(demo){
		//数据库操作 
		demoDao.insert(demo);	
	}

	//声明事务并指定回滚异常,默认就是RuntimeException,也可以省略不指定
	@Transactional(rollbackFor = RuntimeException.class)
	public void update(demo){
		//数据库操作 
		demoDao.update(demo);
		if(1==1){
			throw new RuntimeException("数据库回滚测试");
		}	
	}
	
}

//在controller中调用service
@Controller
public class DemoController(){
	@Autowired
	private DemoService demoService;
	
	@PostMapping("/save")
	public String save(Demo demo){
		demoService.save(demo);
		return "success";
	}
}

问题: 在controller中调用demoService.save(demo)方法后,数据库中的demo表的remark字段是“新增"还是”更新"?
答案: ”更新"。

疑问: save()调用了update()方法,update()用@Transactional注解声明了事务,执行update()方法抛出了异常,为什么数据库没有回滚? remark字段不应该回滚变为“新增"吗。

分析: spring事务管理是通过代理实现的,save方法是通过代理方式调用的,但是insert方法和update方法是在同一个对象的save方法内部调用的,因此没有走代理,他们的事务依赖与上层的save方法,由于save方法没有声明事务,insert和update也就没有被事务管理起来,因此方法中的每一个数据库操作执行完就直接commit了。

那我怎么通过代理的方式来在save方法中调用insert和update方法呢?
把insert和update方法拆分到另外一个service中,这样当然可以,但这么做将功能相似的方法拆分到不同类中,不遵循”高类聚“的设计原则。
如果不拆分该如何处理?有两种方式:

方法一:直接通过AopContext获取当前对象的代理

@Service
public class DemoService{
    ... ... 
    ... ...
	public void save(Demo demo){
	    //获取当前对象自己的代理
		DemoService thisProxy=(DemoService)(AopContext.currentProxy());
		demo.setRemark("新增");
		//通过当前代理thisProxy调用insert
		thisProxy.insert(demo);
		
		demo.setRemark("更新");
		//通过当前代理thisProxy调用update
		thisProxy.update(demo);
	}
	... ...
	... ...
}

这种方式每次调用都需要通过AopContext.currentProxy()获取自己的代理,还需要强制转换,比较麻烦,而且代码不够优雅,我们来看方法二:

方法二:注入自己

@Service
public class DemoService{
    ... ... 
    ... ...
    
    //懒加载注入,解决循环依赖报错的问题   
    @Lazy
    @Autowired
	private DemoService demoService;
	
	public void save(Demo demo){
		demo.setRemark("新增");
		//通过当前代理thisProxy调用insert
		demoService.insert(demo);
		demo.setRemark("更新");
		//通过当前代理thisProxy调用update
		demoService.update(demo);
	}
	... ...
	... ...
}

通过方法一方法二得到的都是我们想要的结果了,insert和update在两个独立的事务中,如果update方法抛出回滚异常,则insert会执行成功,update会回滚到insert执行后的状态,即remark=“新增”。

原创文章 21 获赞 13 访问量 8395

猜你喜欢

转载自blog.csdn.net/iteye_19045/article/details/100115630