如下代码:
同一个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=“新增”。