需求描述
在一个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 # 数据库驱动