Spring 事务管理机制是 Spring 框架的重要组成部分,通过它可以简化数据库事务的管理。Spring 提供了声明式事务管理和编程式事务管理两种方式,其中声明式事务管理最为常用。
声明式事务管理
声明式事务管理通过配置和注解的方式,将事务管理逻辑从业务代码中分离出来,使得代码更加简洁和易维护。
关键配置和注解
-
在 XML 中配置事务管理器:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- 配置数据源 --> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <property name="username" value="user"/> <property name="password" value="password"/> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <!-- 启用声明式事务管理 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans> -
使用注解进行事务管理:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class MyService { @Autowired private MyRepository myRepository; @Transactional public void performTransactionalOperation() { // 业务逻辑 myRepository.saveSomeData(); // 其他操作 } }
核心类和接口
PlatformTransactionManager:事务管理器接口,不同类型的事务(如 JDBC、JPA)有不同的实现。TransactionDefinition:定义事务的属性,如传播行为、隔离级别等。TransactionStatus:表示事务的当前状态。
编程式事务管理
编程式事务管理需要在代码中显式地管理事务,适用于对事务管理要求更为精细控制的场景。
示例代码
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
@Service
public class MyService {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private MyRepository myRepository;
public void performTransactionalOperation() {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 业务逻辑
myRepository.saveSomeData();
// 其他操作
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw e;
}
}
}
事务传播行为
在 Spring 事务管理中,事务传播行为用于定义当一个事务方法被另一个事务方法调用时,事务应该如何进行协调。以下是各个事务传播行为的对比及其应用场景:
1. PROPAGATION_REQUIRED
描述
如果当前没有事务,则新建一个事务;如果当前有事务,则加入到这个事务中。
应用场景
这是最常用的传播行为,适用于大多数情况。例如,一个服务层方法调用另一个需要事务的方法时,希望它们共享同一个事务,以确保数据一致性。
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
innerMethod();
}
@Transactional(propagation = Propagation.REQUIRED)
public void innerMethod() {
// ...
}
2. PROPAGATION_REQUIRES_NEW
描述
总是新建一个事务。如果当前存在事务,挂起当前事务。
应用场景
适用于必须独立于外部事务执行的操作。例如,记录日志或审计信息,这些操作不应因外部事务的回滚而受影响(内外部事务任何一个回滚,另一个都不受影响)。
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
try {
// 主业务逻辑
} catch (Exception e) {
logError(e);
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void logError(Exception e) {
// 独立事务,记录错误日志
}
3. PROPAGATION_SUPPORTS
描述
支持当前事务,如果当前没有事务,就以非事务方式执行。
应用场景
适用于既可以在事务内执行,也可以在非事务环境下执行的操作。例如,读取一些不太重要的数据,不要求强一致性。
@Transactional(propagation = Propagation.SUPPORTS)
public void fetchData() {
// 读操作,可以在事务环境或非事务环境下执行
}
4. PROPAGATION_NOT_SUPPORTED
描述
以非事务方式执行操作,如果当前存在事务,挂起当前事务。
应用场景
适用于不需要事务的场景,且希望避免事务带来的性能开销。例如,批量数据处理中的某些操作。
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void processBatch() {
// 非事务操作
}
5. PROPAGATION_NEVER
描述
以非事务方式执行,如果当前存在事务,则抛出异常。
应用场景
适用于明确不允许在事务环境中执行的操作。例如,某些读操作希望确保没有任何事务存在。
@Transactional(propagation = Propagation.NEVER)
public void readWithoutTransaction() {
// 如果有事务存在,将会抛出异常
}
6. PROPAGATION_MANDATORY
描述
支持当前事务,如果当前没有事务,则抛出异常。
应用场景
适用于必须在事务环境中执行的操作。如果调用该方法时没有事务存在,需要显式地报告错误。
@Transactional(propagation = Propagation.MANDATORY)
public void mustRunInTransaction() {
// 如果没有事务存在,将会抛出异常
}
7. PROPAGATION_NESTED
描述
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则新建一个事务。
应用场景
适用于需要保存子事务点的场景。例如,在复杂的业务操作中,部分成功,部分失败时希望能够回滚到子事务点(内部事务回滚,外部事务不受影响;外部事务回滚,内部事务也会回滚)。
嵌套事务的工作机制
PROPAGATION_NESTED 使用保存点(Savepoint)来实现事务的嵌套。当内部事务开始时,会创建一个保存点,如果内部事务回滚,只会回滚到这个保存点,不影响外部事务的其余部分。然而,如果外部事务回滚,则所有嵌套的事务都会回滚。
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
saveParent();
try {
nestedMethod();
} catch (Exception e) {
// 处理嵌套事务的异常
}
}
@Transactional(propagation = Propagation.NESTED)
public void nestedMethod() {
saveChild();
// 出现异常时,可以回滚到 saveChild() 前的状态,而不影响 saveParent()
}
事务隔离级别
事务隔离级别是数据库事务管理中的重要概念,它决定了一个事务在并发操作时如何可见其他事务的修改。不同的隔离级别提供不同程度的数据一致性和并发性能。这些隔离级别通常依赖于底层数据库系统的实现。以下是详细的解释:
1. ISOLATION_READ_UNCOMMITTED(未提交读)
描述
允许一个事务读取另一个事务没有提交的更改。
问题
- 脏读:可能读取到其他事务尚未提交的数据。
- 不可重复读:同一个事务两次读取同一数据,结果可能不同。
- 幻读:同一个事务两次查询条件相同的数据集,结果可能不同。
应用场景
适用于不太关心数据一致性的应用,例如日志记录或监控系统。
// 示例:读取未提交的数据
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void readUncommittedData() {
// 业务逻辑
}
2. ISOLATION_READ_COMMITTED(已提交读)
描述
只允许一个事务读取另一个事务已经提交的更改,这是大多数数据库的默认隔离级别。
问题
- 不可重复读:同一个事务两次读取同一数据,结果可能不同。
- 幻读:同一个事务两次查询条件相同的数据集,结果可能不同。
应用场景
适用于大多数常规应用,既能保证一定的一致性,又有较好的并发性能。
// 示例:读取已提交的数据
@Transactional(isolation = Isolation.READ_COMMITTED)
public void readCommittedData() {
// 业务逻辑
}
3. ISOLATION_REPEATABLE_READ(可重复读)
描述
确保同一事务内的多次读取结果一致,防止不可重复读,但仍可能发生幻读。
问题
- 幻读:同一个事务两次查询条件相同的数据集,结果可能不同。
应用场景
适用于需要较高数据一致性的场景,例如金融交易或库存管理系统。
// 示例:可重复读取数据
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void repeatableReadData() {
// 业务逻辑
}
4. ISOLATION_SERIALIZABLE(可串行化)
描述
最高的隔离级别,通过强制事务顺序执行,防止脏读、不可重复读和幻读。
问题
- 性能开销大,可能导致大量锁争用和阻塞。
应用场景
适用于对数据一致性要求极高的场景,通常在实际应用中较少使用。
// 示例:串行化读取数据
@Transactional(isolation = Isolation.SERIALIZABLE)
public void serializableReadData() {
// 业务逻辑
}
隔离级别对比
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| READ_UNCOMMITTED | 可能 | 可能 | 可能 |
| READ_COMMITTED | 不可能 | 可能 | 可能 |
| REPEATABLE_READ | 不可能 | 不可能 | 可能 |
| SERIALIZABLE | 不可能 | 不可能 | 不可能 |
总结
选择合适的隔离级别需要权衡数据一致性和系统性能:
- READ_UNCOMMITTED:性能最好,但数据一致性最低,适用于不要求高一致性的场景。
- READ_COMMITTED:一般默认隔离级别,平衡了性能和一致性,适用于大多数应用。
- REPEATABLE_READ:提高了一致性,适用于需要多次读取一致结果的场景,如财务系统。
- SERIALIZABLE:最高一致性,但性能最差,适用于要求极高数据完整性的关键业务。