springdata+jpa service一个方法同一事物中执行delete和insert
场景:
在service中提供的一个方法是先将符合条件的数据全部删除,然后再将新的条件全部插入数据库中,这个场景需要保证service中执行两步:
- 删除
- 插入
这两步自然是在同一个事务中完成才是一个完整的操作,对于这个场景:
- dao层:在jpa中使用delete操作,需要加上
@Modifying
、@Transactional
这两个注解,因为,@Modifying可以通知SpringData这是一个UPDATE或DELETE操作,而这两个操作需要使用事务,所以在dao层的delete方法上需要加上这两个注解- service层:需要在入口方法上加上
@Transactional
注解,用来保证两步操作在同一个事务内执行这个时候会出现问题,因为执行SpringData的delete操作,并不会立即执行,而是等到service方法执行完成,才会提交事务,但是这样的话insert插入就会出现重复数据存在,不能重复插入的错误。
逻辑:
1、进入方法,开启事务
2、代码delete方法是jpa的delete方法,断点在此处发现并没有执行deleye的SQL语句,但是执行了一条select语句【可见自定义的delete方法是根据查到的数据再拼接sql在事务提交的时候才执行】
3、执行save的插入方法,执行了insert语句,报数据已存在
4、关闭事务
1)解决方案一:
仅更改service代码,手动调用flush()方法,让jpa在select出这个对象之后,拼接了delete语句,然后立刻执行sql作用到数据库。
delete之后,delete语句并没有执行,调用flush让他先把更改作用到数据库,再去执行save插入操作,虽然此刻事务依旧没有提交,但是delete语句已经执行
2)解决方案二:
写@Query语句,告诉jpa,我们要明确执行这条delete的sql语句,避免了让他去查一遍,拿到实体以后再拼接SQL语句,最后在方法结束,事务提交的时候才去执行delete语句。service不变。
这两种方式都可以解决delete操作和save操作在同一个事务中的原子性。