需求描述
在一个Spring Boot 2.x项目中,有一个需求是在serviceA的method1方法中执行两个数据库操作,并在其中的某一步调用了serviceB的method2,而method2也包含了数据库操作。
如果在这个过程中发生了异常,希望能够确保所有的数据库操作都能够回滚,以保持数据的一致性。该如何在代码中实现这个需求?
正文
先上结论:method1和method2都加上 @Transactional注解
当在method1方法上添加@Transactional注解时,该方法及其内部的数据库操作将在一个事务中执行。如果在方法执行过程中抛出异常,Spring会回滚整个事务,包括method1方法中的数据库操作。
当method1方法中调用serviceB的method2时,如果method2方法也被标记为@Transactional,并且抛出了异常,整个事务将回滚,包括method1中的数据库操作和method2中的数据库操作。
事务的回滚是针对整个事务范围的,跨方法调用的情况同样适用。如果在任何被@Transactional标记的方法中抛出异常,Spring会回滚整个事务,确保所有数据库操作的一致性。
1、在serviceA的method1方法上添加@Transactional注解:
java
@Service
public class ServiceA {
@Autowired
private ServiceB serviceB;
@Transactional
public void method1() {
// 第一个数据库操作
// 调用另一个Service的@Transactional方法
serviceB.method2();
// 第二个数据库操作
// 如果发生异常,整个事务将回滚
}
}
使用@Transactional注解的方法,如果在执行过程中发生异常,Spring会自动回滚整个事务。
2、在serviceB的method2方法上同样添加@Transactional注解:
java
@Service
public class ServiceB {
@Transactional
public void method2() {
// 第三个数据库操作
// 如果发生异常,整个事务将回滚
}
}
method2需要在方法上throws异常出去吗?
Spring默认会将未经检查的异常视为回滚原因,并触发事务回滚,无需在方法签名中声明。
如果抛出的是未经检查的异常(继承自RuntimeException或Error),你不需要在method2的方法签名中使用throws声明。
如果发生的异常是受检查的异常(继承自Exception),你可能需要根据实际情况调整代码以处理这些异常。
示例代码:
java
@Service
public class ServiceB {
@Transactional(rollbackFor = XiaodouException.class)
public void method2() throws YourCheckedException {
// 操作数据库3
// 抛出自定义,Spring会回滚事务
throw new XiaodouException("Something went wrong in method2");
}
}
额外注意事项
1、确保Spring Boot应用开启了事务管理
在项目启动类上添加@EnableTransactionManagement注解,以启用Spring的事务管理功能。
java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.transaction.annotation.EnableTransactionManagement;
@SpringBootApplication
@EnableTransactionManagement
public class XiaodouApplication {
public static void main(String[] args) {
SpringApplication.run(XiaodouApplication.class, args);
}
}
2、数据库的配置需要在application.yml文件中添加,参考配置如下
yml
spring:
datasource:
url: jdbc:mysql://localhost:3306/xiaodoudatabase # 数据库密码
username: root # 数据库登录用户名
password: 123456 # 数据库密码
driver-class-name: com.mysql.cj.jdbc.Driver # 数据库驱动