Spring Boot 事务管理是确保数据一致性和完整性的重要机制。Spring 通过 @Transactional 注解简化了事务管理。以下是 Spring Boot 事务管理的详细教程。
1. 环境准备
确保你已经有一个 Spring Boot 项目。如果没有,可以通过 Spring Initializr 创建一个。
2. 添加依赖
Spring Boot 默认使用 Spring Data JPA 和 Hibernate 作为 ORM 框架。确保 pom.xml 中包含以下依赖:
3. 配置数据源
在 application.properties 或 application.yml 中配置数据源和 H2 数据库(用于测试):
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
4. 创建实体类
创建一个简单的实体类 User:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Getters and Setters
}
5. 创建 Repository 接口
创建一个 UserRepository 接口,继承 JpaRepository:
import org.springframework.data.jpa.repository.JpaRepository;
public interface UserRepository extends JpaRepository
}
6. 创建 Service 层
在 Service 层中,使用 @Transactional 注解来管理事务:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Transactional
public void createUser(User user) {
userRepository.save(user);
}
@Transactional(readOnly = true)
public User getUserById(Long id) {
return userRepository.findById(id).orElse(null);
}
@Transactional
public void updateUser(User user) {
userRepository.save(user);
}
@Transactional
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
7. 创建 Controller 层
创建一个简单的 REST Controller 来处理 HTTP 请求:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public void createUser(@RequestBody User user) {
userService.createUser(user);
}
@GetMapping("/{id}")
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
@PutMapping
public void updateUser(@RequestBody User user) {
userService.updateUser(user);
}
@DeleteMapping("/{id}")
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
8. 事务传播行为
Spring 提供了多种事务传播行为,可以通过 @Transactional 注解的 propagation 属性来指定:
REQUIRED(默认):如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则挂起当前事务。SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务方式执行。NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,则挂起当前事务。MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则创建一个新的事务。
示例:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createUserWithNewTransaction(User user) {
userRepository.save(user);
}
9. 事务隔离级别
Spring 支持多种事务隔离级别,可以通过 @Transactional 注解的 isolation 属性来指定:
DEFAULT:使用底层数据库的默认隔离级别。READ_UNCOMMITTED:允许读取未提交的数据变更。READ_COMMITTED:只能读取已提交的数据变更。REPEATABLE_READ:确保在同一事务中多次读取同一数据时,结果一致。SERIALIZABLE:最高的隔离级别,确保事务串行执行。
示例:
@Transactional(isolation = Isolation.READ_COMMITTED)
public void updateUserWithIsolation(User user) {
userRepository.save(user);
}
10. 事务回滚
默认情况下,Spring 会在遇到 RuntimeException 或 Error 时回滚事务。你可以通过 @Transactional 注解的 rollbackFor 和 noRollbackFor 属性来指定哪些异常触发回滚:
@Transactional(rollbackFor = Exception.class)
public void createUserWithRollback(User user) throws Exception {
userRepository.save(user);
if (user.getName() == null) {
throw new Exception("User name cannot be null");
}
}
11. 测试事务
你可以编写单元测试来验证事务是否正常工作:
import static org.junit.jupiter.api.Assertions.assertThrows;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@SpringBootTest
public class UserServiceTest {
@Autowired
private UserService userService;
@Test
public void testCreateUser() {
User user = new User();
user.setName("John Doe");
user.setEmail("john.doe@example.com");
userService.createUser(user);
// 验证用户是否成功创建
}
@Test
public void testUpdateUserWithException() {
assertThrows(RuntimeException.class, () -> {
userService.updateUser(1L, "new.email@example.com");
});
}
}
12. 事务的嵌套
Spring 支持嵌套事务,可以通过 Propagation.NESTED 来实现。嵌套事务允许在一个事务中启动另一个事务,内部事务可以独立提交或回滚,而外部事务可以控制整体事务的提交或回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
// 外部事务逻辑
innerMethod();
// 外部事务逻辑
}
@Transactional(propagation = Propagation.NESTED)
public void innerMethod() {
// 内部事务逻辑
}
13. 事务的只读属性
如果某个方法只是读取数据而不修改数据,可以将事务标记为只读,以提高性能:
@Transactional(readOnly = true)
public User getUser(Long userId) {
return userRepository.findById(userId).orElseThrow(() -> new RuntimeException("User not found"));
}
14. 事务的超时设置
你可以通过 @Transactional 注解的 timeout 属性来设置事务的超时时间(以秒为单位):
@Transactional(timeout = 10)
public void someMethod() {
// 业务逻辑
}
15. 事务失效的常见原因
15.1 方法非 public
@Transactional 只能用于 public 方法。
15.2 自调用问题
同一类中方法 A 调用方法 B(带 @Transactional),事务不生效。 解决:注入自身 Bean 或使用 AopContext.currentProxy()。
15.3 异常被捕获
@Transactional
public void update() {
try {
jdbcTemplate.update("..."); // SQL 异常
} catch (DataAccessException e) {
// 异常被吞,事务不会回滚!
}
}
15.4 数据库引擎不支持
如 MySQL 的 MyISAM 引擎不支持事务,需切换为 InnoDB。
16. 高级话题
分布式事务:Spring 的 JtaTransactionManager 或 Seata 框架。
编程式事务:使用 TransactionTemplate 手动控制。
@Autowired
private TransactionTemplate transactionTemplate;
public void doInTransaction() {
transactionTemplate.execute(status -> {
// 业务逻辑
return result;
});
}
17. 总结
特性配置方式常用场景传播行为propagation多方法嵌套调用隔离级别isolation高并发环境回滚规则rollbackFor/noRollbackFor自定义异常处理逻辑只读优化readOnly=true查询操作超时控制timeout防止长事务阻塞
通过 @Transactional 注解,Spring Boot 提供了简单而强大的事务管理机制。你可以通过配置传播行为、隔离级别和回滚规则来满足不同的业务需求。在实际开发中,合理使用事务管理可以确保数据的一致性和完整性。
18. 进一步学习
学习 Spring 的声明式事务管理和编程式事务管理。了解分布式事务和 Spring 的 @Transactional 在微服务架构中的应用。探索 Spring 的事务同步机制和事务事件监听器。