什么是事务?
Spring怎么使用事务?
Spring Boot 怎么使用Spring的事务控制机制?可以参考我的这篇博客!
Spring 事务中开启新的事务的场景
通常的情况下,一般的事务直接在Service类上添加@Transactional注解,spring就会帮我们替所有方法自动生成事务。但是在某些情况下,很少出现的。我们希望在一个方法上,出现两个事务,而且2给事务之间互不影响。
如:一个用户的修改操作以及系统的删除操作
@Service
public class TestService {
@Transactional
public void userUpdate(){
//用户修改操作
systemDelete();
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void systemDelete(){
//删除系统数据操作
throw new RuntimeException("抛出一个删除系统数据失败的运行期异常!");
}
}
Propagation.REQUIRES_NEW:表示如果当前存在事务,则挂起当前事务并且开启一个新事物继续执行,新事物执行完毕之后,然后在缓刑之前挂起的事务,如果当前不存在事务的话,则开启一个新事物。
上面的例子,在使用systemDelete()的时候,我显性的抛出一个运行期异常。按照最初的想法,systemDelete()会进行rollback,而用户修改操作不会。但是观察数据库会发现,这2个spring事务都进行了回滚。
那么如果我们抓住systemDelete()抛出的异常
@Service
public class TestService {
@Transactional
public void userUpdate(){
//用户修改操作
try {
systemDelete();
} catch (Exception e) {
e.printStackTrace();
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void systemDelete(){
//删除系统数据操作
throw new RuntimeException("抛出一个删除系统数据失败的运行期异常!");
}
}
这次会发现2个操作都成功了。说明这2个操作其实都在同一个事务之中。
怎么解决这个问题?
有2种方法可以解决这种场景
1.使用@Autowired来开启新事务
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional
public void userUpdate(){
//用户修改操作
serviceB.insert();
}
}
@Service
public class ServiceB {
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void systemDelete(){
//更新系统数据操作
throw new RuntimeException("抛出一个更新系统失败的运行期异常");
}
}
2.使用代理对象开启新事务
Spring 也考虑过这个问题提供了解决方案
spring配置使用
- spring xml配置
<aop:aspectj-autoproxy expose-proxy="true"/>
<aop:config expose-proxy="true">
<!-- spring xml配置上面2个节点的一个就可以了,效果是一样的。这个配置是让spring暴露出代理对象 -->
2.在代码的调用中要求使用代理对象去调用即可
((TestService) AopContext.currentProxy()).systemDelete();
spring boot 配置使用
当然现在主流的 spring boot 也是支持使用的 aop 代理的
1.引入spring aop依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.添加注解暴露代理对象
@SpringBootApplication
@EnableAspectJAutoProxy(exposeProxy = true)
public class TransactionApplication {
public static void main(String[] args) {
SpringApplication.run(TransactionApplication.class, args);
}
}
3.在代码的调用中要求使用代理对象去调用即可
((TestService) AopContext.currentProxy()).systemDelete();