概述
Spring 事务管理 是 Spring 框架的核心功能之一,提供了一致的事务管理抽象,支持声明式和编程式事务管理。通过 AOP 技术,Spring 实现了对底层事务资源的统一管理,大大简化了事务编程的复杂性。
核心特性
- 一致性事务抽象:统一不同事务管理器的接口
- 声明式事务:通过注解或配置文件管理事务
- 编程式事务:通过 API 显式控制事务
- 多数据源支持:支持多种数据访问技术
- 事务传播机制:灵活的事务传播策略
- 异常处理:智能的事务回滚机制
应用场景
| 场景 | 描述 | 示例 |
|---|---|---|
| 数据库操作 | 确保数据一致性 | 转账操作、订单处理 |
| 批量处理 | 保证批量操作的原子性 | 数据导入、批量更新 |
| 分布式系统 | 跨服务的事务一致性 | 微服务间的数据同步 |
| 业务流程 | 复杂业务逻辑的事务控制 | 电商下单流程 |
💡 提示: 事务管理是确保数据一致性和完整性的关键机制,是企业级应用开发的基础。
事务基础理论
ACID 属性
事务必须具备以下四个属性:
1. 原子性(Atomicity)
事务是一个不可分割的工作单位,要么全部执行,要么全部不执行。
@Service
public class BankService {
@Transactional
public void transfer(String fromAccount, String toAccount, BigDecimal amount) {
// 原子性:以下操作要么全部成功,要么全部失败
accountDao.debit(fromAccount, amount); // 扣款
accountDao.credit(toAccount, amount); // 加款
transactionLogDao.log(fromAccount, toAccount, amount); // 记录日志
}
}
2. 一致性(Consistency)
事务必须使数据库从一个一致性状态变换到另一个一致性状态。
@Service
public class OrderService {
@Transactional
public void createOrder(OrderDTO orderDTO) {
// 一致性:业务规则得到维护
// 检查库存(业务规则:库存必须充足)
if (productService.getStock(orderDTO.getProductId()) < orderDTO.getQuantity()) {
throw new InsufficientStockException("库存不足");
}
// 创建订单
Order order = new Order(orderDTO);
orderDao.save(order);
// 扣减库存
productService.reduceStock(orderDTO.getProductId(), orderDTO.getQuantity());
// 更新用户积分
userService.addPoints(orderDTO.getUserId(), order.getAmount().multiply(new BigDecimal("0.01")));
}
}
3. 隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务。
@Service
public class ConcurrentService {
@Transactional(isolation = Isolation.READ_COMMITTED)
public void handleConcurrentOperation(Long id) {
// 隔离性:多个事务并发执行时相互隔离
Account account = accountDao.findById(id);
// 业务逻辑处理
BigDecimal newBalance = account.getBalance().add(new BigDecimal("100"));
account.setBalance(newBalance);
accountDao.update(account);
}
}
4. 持久性(Durability)
事务一旦提交,对数据库的改变是永久性的。
@Service
public class PaymentService {
@Transactional
public PaymentResult processPayment(PaymentRequest request) {
// 持久性:提交后的数据变更是永久的
Payment payment = new Payment(request);
paymentDao.save(payment);
// 更新账户余额
accountService.updateBalance(request.getAccountId(), request.getAmount());
// 发送通知(即使系统重启,支付记录也不会丢失)
notificationService.sendPaymentNotification(payment);
return new PaymentResult(payment.getId(), PaymentStatus.SUCCESS);
}
}
事务并发问题
1. 脏读(Dirty Read)
一个事务读取了另一个事务未提交的数据。
// 事务A
@Transactional
public void transferMoney() {
account.setBalance(1000); // 更新但未提交
// ... 其他操作
}
// 事务B
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void readBalance() {
BigDecimal balance = account.getBalance(); // 可能读到1000(脏读)
// 如果事务A回滚,这里读到的就是脏数据
}
2. 不可重复读(Non-Repeatable Read)
在同一事务中,多次读取同一数据返回的结果不一致。
@Transactional(isolation = Isolation.READ_COMMITTED)
public void demonstrateNonRepeatableRead() {
// 第一次读取
Account account1 = accountDao.findById(1L);
System.out.println("第一次读取余额:" + account1.getBalance());
// 此时其他事务修改了账户余额并提交
// 第二次读取
Account account2 = accountDao.findById(1L);
System.out.println("第二次读取余额:" + account2.getBalance());
// 两次读取结果不一致
}
3. 幻读(Phantom Read)
在同一事务中,多次执行相同查询返回不同的结果集。
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void demonstratePhantomRead() {
// 第一次查询
List<Account> accounts1 = accountDao.findByBalanceGreaterThan(new BigDecimal("1000"));
System.out.println("第一次查询账户数量:" + accounts1.size());
// 此时其他事务插入了新的符合条件的账户
// 第二次查询
List<Account> accounts2 = accountDao.findByBalanceGreaterThan(new BigDecimal("1000"));
System.out.println("第二次查询账户数量:" + accounts2.size());
// 查询结果集不一致(出现了"幻影"记录)
}
Spring事务架构
核心组件
1. PlatformTransactionManager
事务管理器是 Spring 事务管理的核心接口。
public interface PlatformTransactionManager {
/**
* 开启事务
*/
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
/**
* 提交事务
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚事务
*/
void rollback(TransactionStatus status) throws TransactionException;
}
2. 常用事务管理器实现
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
/**
* JDBC 事务管理器
*/
@Bean
public DataSourceTransactionManager dataSourceTransactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
/**
* JPA 事务管理器
*/
@Bean
public JpaTransactionManager jpaTransactionManager(EntityManagerFactory entityManagerFactory) {
return new JpaTransactionManager(entityManagerFactory);
}
/**
* Hibernate 事务管理器
*/
@Bean
public HibernateTransactionManager hibernateTransactionManager(SessionFactory sessionFactory) {
return new HibernateTransactionManager(sessionFactory);
}
/**
* JTA 事务管理器(分布式事务)
*/
@Bean
public JtaTransactionManager jtaTransactionManager() {
return new JtaTransactionManager();
}
}
3. TransactionDefinition
事务定义包含事务的各种属性。
public class CustomTransactionDefinition implements TransactionDefinition {
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
private String name;
// 构造函数和getter/setter方法
public CustomTransactionDefinition(String name) {
this.name = name;
}
@Override
public int getPropagationBehavior() {
return this.propagationBehavior;
}
@Override
public int getIsolationLevel() {
return this.isolationLevel;
}
@Override
public int getTimeout() {
return this.timeout;
}
@Override
public boolean isReadOnly() {
return this.readOnly;
}
@Override
public String getName() {
return this.name;
}
}
4. TransactionStatus
事务状态提供了事务的运行时信息。
public class TransactionStatusDemo {
public void demonstrateTransactionStatus(TransactionStatus status) {
// 检查事务状态
System.out.println("是否为新事务: " + status.isNewTransaction());
System.out.println("是否有保存点: " + status.hasSavepoint());
System.out.println("是否为只读事务: " + status.isReadOnly());
System.out.println("是否已完成: " + status.isCompleted());
// 设置回滚标记
if (someCondition()) {
status.setRollbackOnly();
System.out.println("事务被标记为回滚");
}
// 检查是否被标记为回滚
if (status.isRollbackOnly()) {
System.out.println("事务将被回滚");
}
}
private boolean someCondition() {
// 业务逻辑判断
return true;
}
}
声明式事务
基于注解的事务管理
1. 基础用法
@Service
@Transactional(readOnly = true) // 类级别默认事务属性
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private EmailService emailService;
/**
* 查询用户(只读事务)
*/
public User findById(Long id) {
return userDao.findById(id);
}
/**
* 保存用户(读写事务)
*/
@Transactional(readOnly = false, propagation = Propagation.REQUIRED)
public User save(User user) {
// 验证用户数据
validateUser(user);
// 保存用户
User savedUser = userDao.save(user);
// 发送欢迎邮件
emailService.sendWelcomeEmail(savedUser.getEmail());
return savedUser;
}
/**
* 批量保存用户
*/
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = {Exception.class}
)
public void batchSave(List<User> users) {
for (User user : users) {
validateUser(user);
userDao.save(user);
}
}
private void validateUser(User user) {
if (user.getEmail() == null || user.getEmail().trim().isEmpty()) {
throw new IllegalArgumentException("邮箱不能为空");
}
if (userDao.existsByEmail(user.getEmail())) {
throw new IllegalStateException("邮箱已存在");
}
}
}
2. 高级配置
@Service
public class AdvancedTransactionService {
/**
* 只读事务,优化性能
*/
@Transactional(
readOnly = true,
timeout = 10,
isolation = Isolation.READ_COMMITTED
)
public List<Order> getOrdersByUserId(Long userId) {
return orderDao.findByUserId(userId);
}
/**
* 需要新事务的操作
*/
@Transactional(
propagation = Propagation.REQUIRES_NEW,
isolation = Isolation.SERIALIZABLE
)
public void createAuditLog(String operation, String details) {
AuditLog log = new AuditLog(operation, details);
auditLogDao.save(log);
}
/**
* 不支持事务的操作
*/
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void sendNotification(String message) {
// 发送通知不需要事务
notificationService.send(message);
}
/**
* 嵌套事务示例
*/
@Transactional(propagation = Propagation.REQUIRED)
public void processOrderWithNestedTransaction(Order order) {
// 主事务:处理订单
orderDao.save(order);
try {
// 嵌套事务:发送通知
sendOrderNotification(order);
} catch (Exception e) {
// 嵌套事务失败不影响主事务
logger.error("发送订单通知失败", e);
}
// 继续主事务的其他操作
updateInventory(order);
}
@Transactional(propagation = Propagation.NESTED)
private void sendOrderNotification(Order order) {
// 这是一个嵌套事务
notificationService.sendOrderCreatedNotification(order);
}
}
基于XML的事务配置
1. 传统XML配置
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:aop="http://www.springframework.org/schema/aop"
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 配置数据源 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/testdb"/>
<property name="username" value="root"/>
<property name="password" value="password"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 配置事务通知 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 查询方法:只读事务 -->
<tx:method name="get*" read-only="true" timeout="10"/>
<tx:method name="find*" read-only="true" timeout="10"/>
<tx:method name="query*" read-only="true" timeout="10"/>
<!-- 修改方法:读写事务 -->
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<!-- 批量操作:需要新事务 -->
<tx:method name="batch*" propagation="REQUIRES_NEW" timeout="300"/>
<!-- 其他方法:默认事务 -->
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 配置AOP切面 -->
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.example.service.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>
</beans>
2. 多事务管理器配置
<!-- 主数据源事务管理器 -->
<bean id="primaryTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="primaryDataSource"/>
<qualifier value="primary"/>
</bean>
<!-- 从数据源事务管理器 -->
<bean id="secondaryTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="secondaryDataSource"/>
<qualifier value="secondary"/>
</bean>
<!-- 主数据源事务通知 -->
<tx:advice id="primaryTxAdvice" transaction-manager="primaryTransactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 从数据源事务通知 -->
<tx:advice id="secondaryTxAdvice" transaction-manager="secondaryTransactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
编程式事务
TransactionTemplate 方式
1. 基本用法
@Service
public class ProgrammaticTransactionService {
@Autowired
private TransactionTemplate transactionTemplate;
@Autowired
private AccountDao accountDao;
/**
* 使用TransactionTemplate进行编程式事务管理
*/
public void transferMoney(String fromAccount, String toAccount, BigDecimal amount) {
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
// 检查源账户余额
Account from = accountDao.findByAccountNumber(fromAccount);
if (from.getBalance().compareTo(amount) < 0) {
throw new InsufficientFundsException("余额不足");
}
// 扣款
from.setBalance(from.getBalance().subtract(amount));
accountDao.update(from);
// 模拟异常情况
if (amount.compareTo(new BigDecimal("10000")) > 0) {
throw new IllegalArgumentException("转账金额过大");
}
// 加款
Account to = accountDao.findByAccountNumber(toAccount);
to.setBalance(to.getBalance().add(amount));
accountDao.update(to);
// 记录交易日志
TransactionLog log = new TransactionLog(fromAccount, toAccount, amount);
transactionLogDao.save(log);
} catch (Exception e) {
// 标记回滚
status.setRollbackOnly();
throw new TransactionException("转账失败", e);
}
}
});
}
/**
* 带返回值的事务操作
*/
public Order createOrder(OrderRequest request) {
return transactionTemplate.execute(new TransactionCallback<Order>() {
@Override
public Order doInTransaction(TransactionStatus status) {
// 创建订单
Order order = new Order(request);
order = orderDao.save(order);
// 减少库存
for (OrderItem item : order.getItems()) {
Product product = productDao.findById(item.getProductId());
if (product.getStock() < item.getQuantity()) {
status.setRollbackOnly();
throw new InsufficientStockException("库存不足");
}
product.setStock(product.getStock() - item.getQuantity());
productDao.update(product);
}
return order;
}
});
}
}
2. 自定义TransactionTemplate
@Configuration
public class TransactionConfig {
@Bean
public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
TransactionTemplate template = new TransactionTemplate(transactionManager);
// 设置事务属性
template.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
template.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
template.setTimeout(30);
template.setReadOnly(false);
return template;
}
@Bean
public TransactionTemplate readOnlyTransactionTemplate(PlatformTransactionManager transactionManager) {
TransactionTemplate template = new TransactionTemplate(transactionManager);
// 只读事务模板
template.setReadOnly(true);
template.setTimeout(10);
return template;
}
}
PlatformTransactionManager 方式
1. 直接使用事务管理器
@Service
public class DirectTransactionService {
@Autowired
private PlatformTransactionManager transactionManager;
@Autowired
private UserDao userDao;
public void registerUser(User user) {
// 定义事务属性
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
def.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
def.setTimeout(30);
// 开始事务
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 业务逻辑
validateUser(user);
userDao.save(user);
// 发送欢迎邮件
emailService.sendWelcomeEmail(user.getEmail());
// 提交事务
transactionManager.commit(status);
} catch (Exception e) {
// 回滚事务
transactionManager.rollback(status);
throw new ServiceException("用户注册失败", e);
}
}
/**
* 嵌套事务示例
*/
public void processOrderWithSavepoint(Order order) {
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 保存订单
orderDao.save(order);
// 创建保存点
Object savepoint = status.createSavepoint();
try {
// 发送通知
notificationService.sendOrderNotification(order);
// 释放保存点
status.releaseSavepoint(savepoint);
} catch (Exception e) {
// 回滚到保存点
status.rollbackToSavepoint(savepoint);
logger.error("发送通知失败,回滚到保存点", e);
}
// 更新库存
updateInventory(order);
// 提交事务
transactionManager.commit(status);
} catch (Exception e) {
transactionManager.rollback(status);
throw new ServiceException("订单处理失败", e);
}
}
}
事务传播机制
传播属性详解
1. REQUIRED(默认)
如果当前存在事务,则加入该事务;如果没有事务,则创建一个新事务。
@Service
public class PropagationRequiredDemo {
@Transactional(propagation = Propagation.REQUIRED)
public void methodA() {
// 创建新事务
System.out.println("Method A 开始执行");
userDao.save(new User("用户A"));
// 调用方法B,加入当前事务
methodB();
System.out.println("Method A 执行完成");
}
@Transactional(propagation = Propagation.REQUIRED)
public void methodB() {
// 加入methodA的事务
System.out.println("Method B 开始执行");
userDao.save(new User("用户B"));
// 如果这里抛出异常,整个事务都会回滚
if (someCondition()) {
throw new RuntimeException("Method B 执行失败");
}
System.out.println("Method B 执行完成");
}
}
2. REQUIRES_NEW
无论当前是否存在事务,都创建一个新事务。
@Service
public class PropagationRequiresNewDemo {
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
System.out.println("外部事务开始");
userDao.save(new User("外部用户"));
try {
// 调用内部方法,创建新事务
innerMethod();
} catch (Exception e) {
// 内部事务的异常不会影响外部事务
System.out.println("内部事务失败,但外部事务继续");
}
System.out.println("外部事务结束");
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethod() {
System.out.println("内部事务开始");
userDao.save(new User("内部用户"));
// 这个异常只会回滚内部事务
throw new RuntimeException("内部事务失败");
}
}
3. NESTED
如果当前存在事务,则在嵌套事务内执行;如果没有事务,则创建一个新事务。
@Service
public class PropagationNestedDemo {
@Transactional(propagation = Propagation.REQUIRED)
public void parentMethod() {
System.out.println("父事务开始");
userDao.save(new User("父事务用户"));
try {
// 嵌套事务
nestedMethod();
} catch (Exception e) {
// 嵌套事务失败,可以选择是否回滚父事务
System.out.println("嵌套事务失败,父事务继续");
}
System.out.println("父事务结束");
}
@Transactional(propagation = Propagation.NESTED)
public void nestedMethod() {
System.out.println("嵌套事务开始");
userDao.save(new User("嵌套事务用户"));
// 嵌套事务失败,回滚到保存点
throw new RuntimeException("嵌套事务失败");
}
}
4. SUPPORTS
如果当前存在事务,则加入该事务;如果没有事务,则以非事务方式执行。
@Service
public class PropagationSupportsDemo {
@Transactional(propagation = Propagation.SUPPORTS)
public void supportMethod() {
// 如果被事务方法调用,则加入事务
// 如果被非事务方法调用,则以非事务方式执行
userDao.save(new User("支持事务用户"));
}
@Transactional
public void withTransaction() {
// supportMethod会加入当前事务
supportMethod();
}
public void withoutTransaction() {
// supportMethod以非事务方式执行
supportMethod();
}
}
5. NOT_SUPPORTED
以非事务方式执行操作,如果当前存在事务,则把当前事务挂起。
@Service
public class PropagationNotSupportedDemo {
@Transactional
public void transactionalMethod() {
System.out.println("事务方法开始");
userDao.save(new User("事务用户"));
// 调用非事务方法,当前事务被挂起
nonTransactionalMethod();
System.out.println("事务方法结束");
}
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void nonTransactionalMethod() {
System.out.println("非事务方法执行");
// 这里的操作不在事务中,会立即提交
userDao.save(new User("非事务用户"));
}
}
6. MANDATORY
必须在现有事务中执行,如果没有事务则抛出异常。
@Service
public class PropagationMandatoryDemo {
@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryMethod() {
// 必须在事务中执行
userDao.save(new User("强制事务用户"));
}
@Transactional
public void callMandatoryMethod() {
// 正常调用,因为在事务中
mandatoryMethod();
}
public void callWithoutTransaction() {
// 这里会抛出IllegalTransactionStateException
mandatoryMethod();
}
}
7. NEVER
以非事务方式执行,如果当前存在事务,则抛出异常。
@Service
public class PropagationNeverDemo {
@Transactional(propagation = Propagation.NEVER)
public void neverMethod() {
// 绝不能在事务中执行
userDao.save(new User("绝不事务用户"));
}
public void callNeverMethod() {
// 正常调用,因为没有事务
neverMethod();
}
@Transactional
public void callWithTransaction() {
// 这里会抛出IllegalTransactionStateException
neverMethod();
}
}
事务隔离级别
隔离级别详解
1. READ_UNCOMMITTED(读未提交)
最低隔离级别,允许读取尚未提交的数据变更。
@Service
public class ReadUncommittedDemo {
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
public void readUncommittedData() {
// 可能读取到其他事务未提交的数据(脏读)
List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
System.out.println("账户余额:" + account.getBalance());
// 这个余额可能是其他事务未提交的临时值
}
}
}
2. READ_COMMITTED(读已提交)
允许读取并发事务已经提交的数据。
@Service
public class ReadCommittedDemo {
@Transactional(isolation = Isolation.READ_COMMITTED)
public void readCommittedData() {
// 第一次读取
Account account = accountDao.findById(1L);
BigDecimal balance1 = account.getBalance();
System.out.println("第一次读取余额:" + balance1);
// 模拟等待,其他事务可能修改并提交数据
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 第二次读取
account = accountDao.findById(1L);
BigDecimal balance2 = account.getBalance();
System.out.println("第二次读取余额:" + balance2);
// 可能出现不可重复读
if (!balance1.equals(balance2)) {
System.out.println("发生了不可重复读");
}
}
}
3. REPEATABLE_READ(可重复读)
对同一字段的多次读取结果都是一致的。
@Service
public class RepeatableReadDemo {
@Transactional(isolation = Isolation.REPEATABLE_READ)
public void repeatableReadData() {
// 第一次读取
Account account = accountDao.findById(1L);
BigDecimal balance1 = account.getBalance();
System.out.println("第一次读取余额:" + balance1);
// 模拟长时间处理
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
// 第二次读取
account = accountDao.findById(1L);
BigDecimal balance2 = account.getBalance();
System.out.println("第二次读取余额:" + balance2);
// 在可重复读隔离级别下,两次读取结果应该相同
assert balance1.equals(balance2) : "可重复读保证失败";
// 但是可能出现幻读
List<Account> accounts1 = accountDao.findByBalanceGreaterThan(new BigDecimal("1000"));
System.out.println("第一次查询账户数量:" + accounts1.size());
// 再次查询
List<Account> accounts2 = accountDao.findByBalanceGreaterThan(new BigDecimal("1000"));
System.out.println("第二次查询账户数量:" + accounts2.size());
}
}
4. SERIALIZABLE(可串行化)
最高隔离级别,完全服从ACID原则。
@Service
public class SerializableDemo {
@Transactional(isolation = Isolation.SERIALIZABLE)
public void serializableTransaction() {
// 在可串行化隔离级别下,不会出现任何并发问题
List<Account> accounts = accountDao.findAll();
for (Account account : accounts) {
// 对每个账户进行复杂的计算
BigDecimal interest = calculateInterest(account);
account.setBalance(account.getBalance().add(interest));
accountDao.update(account);
}
// 在此事务执行期间,其他事务无法读取或修改账户数据
System.out.println("所有账户利息计算完成");
}
private BigDecimal calculateInterest(Account account) {
// 复杂的利息计算逻辑
return account.getBalance().multiply(new BigDecimal("0.05"));
}
}
事务回滚规则
默认回滚规则
@Service
public class RollbackRuleDemo {
/**
* 默认回滚规则:RuntimeException和Error会回滚
*/
@Transactional
public void defaultRollbackRule() {
userDao.save(new User("测试用户"));
// 这会导致事务回滚
throw new RuntimeException("运行时异常");
}
/**
* 检查型异常不会回滚
*/
@Transactional
public void checkedExceptionNoRollback() throws Exception {
userDao.save(new User("测试用户"));
// 这不会导致事务回滚
throw new Exception("检查型异常");
}
}
自定义回滚规则
@Service
public class CustomRollbackRuleDemo {
/**
* 指定回滚异常类型
*/
@Transactional(rollbackFor = {Exception.class, CustomException.class})
public void customRollbackFor() throws Exception {
userDao.save(new User("测试用户"));
// 即使是检查型异常,也会回滚
throw new Exception("自定义回滚异常");
}
/**
* 指定不回滚的异常类型
*/
@Transactional(noRollbackFor = {IllegalArgumentException.class})
public void customNoRollbackFor() {
userDao.save(new User("测试用户"));
// 即使是RuntimeException,也不会回滚
throw new IllegalArgumentException("不回滚异常");
}
/**
* 按异常类名指定回滚规则
*/
@Transactional(
rollbackForClassName = {"java.lang.Exception", "com.example.CustomException"},
noRollbackForClassName = {"java.lang.IllegalArgumentException"}
)
public void rollbackByClassName() throws Exception {
userDao.save(new User("测试用户"));
// 业务逻辑
if (someCondition()) {
throw new Exception("按类名回滚");
}
}
/**
* 程序化设置回滚
*/
@Transactional
public void programmaticRollback() {
try {
userDao.save(new User("测试用户"));
// 业务逻辑
if (someBusinessCondition()) {
// 手动标记回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
return;
}
} catch (Exception e) {
// 处理异常但不抛出,事务不会自动回滚
logger.error("业务处理失败", e);
// 手动标记回滚
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
}
}
}
分布式事务
两阶段提交(2PC)
1. JTA事务管理器配置
@Configuration
@EnableTransactionManagement
public class JtaTransactionConfig {
@Bean
public JtaTransactionManager transactionManager() {
JtaTransactionManager jtaTransactionManager = new JtaTransactionManager();
jtaTransactionManager.setUserTransaction(userTransaction());
jtaTransactionManager.setTransactionManager(atomikosTransactionManager());
return jtaTransactionManager;
}
@Bean
public UserTransaction userTransaction() {
UserTransactionImp userTransactionImp = new UserTransactionImp();
userTransactionImp.setTransactionTimeout(300);
return userTransactionImp;
}
@Bean
public TransactionManager atomikosTransactionManager() {
UserTransactionManager userTransactionManager = new UserTransactionManager();
userTransactionManager.setForceShutdown(false);
return userTransactionManager;
}
}
2. 多数据源配置
@Configuration
public class MultiDataSourceConfig {
@Bean
@Primary
public DataSource primaryDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
dataSource.setUniqueResourceName("primary");
dataSource.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
Properties properties = new Properties();
properties.setProperty("url", "jdbc:mysql://localhost:3306/primary_db");
properties.setProperty("user", "root");
properties.setProperty("password", "password");
dataSource.setXaProperties(properties);
return dataSource;
}
@Bean
public DataSource secondaryDataSource() {
AtomikosDataSourceBean dataSource = new AtomikosDataSourceBean();
dataSource.setUniqueResourceName("secondary");
dataSource.setXaDataSourceClassName("com.mysql.cj.jdbc.MysqlXADataSource");
Properties properties = new Properties();
properties.setProperty("url", "jdbc:mysql://localhost:3306/secondary_db");
properties.setProperty("user", "root");
properties.setProperty("password", "password");
dataSource.setXaProperties(properties);
return dataSource;
}
}
3. 分布式事务服务
@Service
public class DistributedTransactionService {
@Autowired
@Qualifier("primaryDataSource")
private DataSource primaryDataSource;
@Autowired
@Qualifier("secondaryDataSource")
private DataSource secondaryDataSource;
@Transactional
public void distributedTransaction() {
// 在主数据库中操作
try (Connection primaryConn = primaryDataSource.getConnection()) {
String sql = "INSERT INTO orders (order_id, amount) VALUES (?, ?)";
PreparedStatement ps = primaryConn.prepareStatement(sql);
ps.setString(1, "ORDER-001");
ps.setBigDecimal(2, new BigDecimal("100.00"));
ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("主数据库操作失败", e);
}
// 在从数据库中操作
try (Connection secondaryConn = secondaryDataSource.getConnection()) {
String sql = "INSERT INTO inventory (product_id, quantity) VALUES (?, ?)";
PreparedStatement ps = secondaryConn.prepareStatement(sql);
ps.setString(1, "PRODUCT-001");
ps.setInt(2, -1);
ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException("从数据库操作失败", e);
}
// 如果任何一个操作失败,整个分布式事务都会回滚
}
}
Seata分布式事务
1. Seata配置
@Configuration
public class SeataConfig {
@Bean
public GlobalTransactionScanner globalTransactionScanner() {
return new GlobalTransactionScanner("my-service-group", "my-tx-group");
}
}
2. 使用Seata管理分布式事务
@Service
public class SeataTransactionService {
@Autowired
private OrderService orderService;
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public void createOrder(OrderRequest request) {
// 创建订单
Order order = orderService.createOrder(request);
// 扣减库存
inventoryService.reduceStock(request.getProductId(), request.getQuantity());
// 扣减余额
paymentService.deductBalance(request.getUserId(), request.getAmount());
// 如果任何一个服务调用失败,全局事务会回滚
}
}
性能优化
1. 事务范围优化
@Service
public class TransactionOptimizationService {
/**
* 不好的做法:事务范围过大
*/
@Transactional
public void badTransactionScope() {
// 耗时的非数据库操作
String result = callRemoteService(); // 网络调用
// 复杂的业务逻辑计算
BigDecimal calculatedValue = performComplexCalculation(result);
// 数据库操作
userDao.updateBalance(userId, calculatedValue);
// 发送邮件
emailService.sendNotification(userId, calculatedValue);
}
/**
* 好的做法:缩小事务范围
*/
public void goodTransactionScope() {
// 先进行非数据库操作
String result = callRemoteService();
BigDecimal calculatedValue = performComplexCalculation(result);
// 只在数据库操作时开启事务
updateBalanceInTransaction(userId, calculatedValue);
// 事务外发送邮件
emailService.sendNotification(userId, calculatedValue);
}
@Transactional
private void updateBalanceInTransaction(Long userId, BigDecimal amount) {
userDao.updateBalance(userId, amount);
}
}
2. 只读事务优化
@Service
public class ReadOnlyTransactionService {
/**
* 只读事务可以提高性能
*/
@Transactional(readOnly = true)
public List<User> getUsersByCondition(UserQueryCondition condition) {
// 只读事务可以:
// 1. 省略事务日志记录
// 2. 优化数据库锁机制
// 3. 使用只读数据源
return userDao.findByCondition(condition);
}
/**
* 批量只读操作
*/
@Transactional(readOnly = true, timeout = 30)
public UserStatistics calculateUserStatistics() {
List<User> allUsers = userDao.findAll();
List<Order> allOrders = orderDao.findAll();
return new UserStatistics(allUsers, allOrders);
}
}
3. 事务超时设置
@Service
public class TransactionTimeoutService {
/**
* 设置事务超时时间
*/
@Transactional(timeout = 30) // 30秒超时
public void batchOperation() {
// 批量操作
List<User> users = userDao.findInactiveUsers();
for (User user : users) {
// 处理每个用户
processUser(user);
}
}
/**
* 分批处理大量数据
*/
@Transactional(timeout = 10)
public void batchProcessInChunks() {
int batchSize = 100;
int offset = 0;
List<User> users;
do {
users = userDao.findInactiveUsers(offset, batchSize);
for (User user : users) {
processUser(user);
}
offset += batchSize;
} while (users.size() == batchSize);
}
}
4. 异步事务处理
@Service
public class AsyncTransactionService {
@Autowired
private TaskExecutor taskExecutor;
public void processOrderAsync(Order order) {
// 主要的订单处理(同步)
processOrderSync(order);
// 异步处理次要任务
taskExecutor.execute(() -> {
processOrderNotificationAsync(order);
});
}
@Transactional
private void processOrderSync(Order order) {
// 核心业务逻辑
orderDao.save(order);
inventoryService.reduceStock(order);
}
@Async
@Transactional
public void processOrderNotificationAsync(Order order) {
// 异步事务处理
notificationService.sendOrderNotification(order);
auditService.logOrderCreated(order);
}
}
实战案例
电商订单处理
@Service
@Transactional
public class OrderProcessingService {
@Autowired
private OrderDao orderDao;
@Autowired
private InventoryService inventoryService;
@Autowired
private PaymentService paymentService;
@Autowired
private CouponService couponService;
@Autowired
private PointService pointService;
/**
* 处理订单创建
*/
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = Exception.class
)
public OrderResult createOrder(CreateOrderRequest request) {
try {
// 1. 验证请求参数
validateOrderRequest(request);
// 2. 检查库存
inventoryService.checkStock(request.getItems());
// 3. 验证优惠券
CouponValidationResult couponResult = null;
if (request.getCouponId() != null) {
couponResult = couponService.validateCoupon(request.getCouponId(), request.getUserId());
}
// 4. 计算订单金额
OrderAmount amount = calculateOrderAmount(request, couponResult);
// 5. 创建订单
Order order = createOrderEntity(request, amount);
order = orderDao.save(order);
// 6. 扣减库存
inventoryService.reduceStock(request.getItems());
// 7. 处理支付
PaymentResult paymentResult = paymentService.processPayment(
request.getUserId(), amount.getPayableAmount());
// 8. 使用优惠券
if (couponResult != null && couponResult.isValid()) {
couponService.useCoupon(request.getCouponId(), request.getUserId());
}
// 9. 增加积分
pointService.addPoints(request.getUserId(),
amount.getPayableAmount().multiply(new BigDecimal("0.01")));
// 10. 更新订单状态
order.setStatus(OrderStatus.PAID);
order.setPaymentResult(paymentResult);
orderDao.update(order);
// 11. 异步处理(事务外)
processOrderAsync(order);
return new OrderResult(order, paymentResult);
} catch (InsufficientStockException e) {
throw new OrderProcessingException("库存不足", e);
} catch (PaymentException e) {
throw new OrderProcessingException("支付失败", e);
} catch (Exception e) {
throw new OrderProcessingException("订单处理失败", e);
}
}
/**
* 处理订单取消
*/
@Transactional(
propagation = Propagation.REQUIRED,
rollbackFor = Exception.class
)
public void cancelOrder(Long orderId, String reason) {
Order order = orderDao.findById(orderId);
if (order == null) {
throw new OrderNotFoundException("订单不存在");
}
if (!order.canCancel()) {
throw new OrderStatusException("订单状态不允许取消");
}
// 恢复库存
inventoryService.restoreStock(order.getItems());
// 退款
if (order.getStatus() == OrderStatus.PAID) {
paymentService.refund(order.getPaymentResult().getPaymentId());
}
// 恢复优惠券
if (order.getCouponId() != null) {
couponService.restoreCoupon(order.getCouponId(), order.getUserId());
}
// 扣减积分
pointService.deductPoints(order.getUserId(), order.getPointsEarned());
// 更新订单状态
order.setStatus(OrderStatus.CANCELLED);
order.setCancelReason(reason);
order.setCancelTime(new Date());
orderDao.update(order);
}
/**
* 异步处理订单相关任务
*/
@Async
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void processOrderAsync(Order order) {
try {
// 发送订单确认邮件
emailService.sendOrderConfirmation(order);
// 推送消息
pushService.pushOrderCreated(order);
// 记录操作日志
auditService.logOrderCreated(order);
// 更新用户统计
userStatisticsService.updateOrderStatistics(order.getUserId());
} catch (Exception e) {
logger.error("异步处理订单失败", e);
// 异步任务失败不影响主流程
}
}
}
银行转账系统
@Service
public class BankTransferService {
@Autowired
private AccountDao accountDao;
@Autowired
private TransactionLogDao transactionLogDao;
@Autowired
private RiskControlService riskControlService;
/**
* 转账操作
*/
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.READ_COMMITTED,
timeout = 30,
rollbackFor = Exception.class
)
public TransferResult transfer(TransferRequest request) {
// 1. 参数验证
validateTransferRequest(request);
// 2. 风险控制检查
riskControlService.checkTransferRisk(request);
// 3. 加载账户(加锁)
Account fromAccount = accountDao.findByIdForUpdate(request.getFromAccountId());
Account toAccount = accountDao.findByIdForUpdate(request.getToAccountId());
// 4. 业务规则检查
validateTransferBusiness(fromAccount, toAccount, request.getAmount());
// 5. 执行转账
fromAccount.debit(request.getAmount());
toAccount.credit(request.getAmount());
// 6. 更新账户
accountDao.update(fromAccount);
accountDao.update(toAccount);
// 7. 记录交易日志
TransactionLog log = new TransactionLog(
request.getFromAccountId(),
request.getToAccountId(),
request.getAmount(),
TransactionType.TRANSFER,
request.getDescription()
);
transactionLogDao.save(log);
return new TransferResult(log.getId(), TransferStatus.SUCCESS);
}
/**
* 批量转账
*/
@Transactional(
propagation = Propagation.REQUIRED,
timeout = 300,
rollbackFor = Exception.class
)
public BatchTransferResult batchTransfer(List<TransferRequest> requests) {
List<TransferResult> results = new ArrayList<>();
BigDecimal totalAmount = BigDecimal.ZERO;
for (TransferRequest request : requests) {
try {
TransferResult result = processSingleTransfer(request);
results.add(result);
totalAmount = totalAmount.add(request.getAmount());
} catch (Exception e) {
// 单笔转账失败,整批回滚
throw new BatchTransferException("批量转账失败", e);
}
}
return new BatchTransferResult(results, totalAmount);
}
private TransferResult processSingleTransfer(TransferRequest request) {
// 单笔转账逻辑(不使用@Transactional,继承外部事务)
return transfer(request);
}
}
常见问题
问题1:事务不生效
❓ 现象: 添加了@Transactional注解但事务不生效
常见原因:
- 方法访问权限不是public
- 类没有被Spring管理
- 自调用问题
- 异常被捕获没有抛出
解决方案:
// ❌ 错误示例
@Service
public class UserService {
// 问题1:方法不是public
@Transactional
private void saveUser(User user) {
userDao.save(user);
}
// 问题2:自调用
@Transactional
public void method1() {
// 直接调用不会走代理
method2();
}
@Transactional
public void method2() {
userDao.save(new User());
}
// 问题3:异常被捕获
@Transactional
public void method3() {
try {
userDao.save(new User());
int i = 1 / 0; // 异常
} catch (Exception e) {
// 异常被捕获,事务不会回滚
}
}
}
// ✅ 正确示例
@Service
public class UserService {
@Autowired
private UserService self; // 注入自己的代理
@Transactional
public void saveUser(User user) {
userDao.save(user);
}
@Transactional
public void method1() {
// 通过代理调用
self.method2();
}
@Transactional
public void method2() {
userDao.save(new User());
}
@Transactional
public void method3() {
try {
userDao.save(new User());
int i = 1 / 0;
} catch (Exception e) {
// 重新抛出异常
throw new ServiceException("保存用户失败", e);
}
}
}
问题2:事务传播行为理解错误
❓ 现象: 事务传播行为不符合预期
解决方案:
@Service
public class TransactionPropagationService {
@Transactional(propagation = Propagation.REQUIRED)
public void outerMethod() {
userDao.save(new User("外部用户"));
try {
// REQUIRES_NEW会创建新事务
innerMethodRequiresNew();
} catch (Exception e) {
// 内部事务失败不会影响外部事务
System.out.println("内部事务失败,外部事务继续");
}
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void innerMethodRequiresNew() {
userDao.save(new User("内部用户"));
throw new RuntimeException("内部事务失败");
}
}
问题3:数据库死锁
❓ 现象: 在高并发情况下出现数据库死锁
解决方案:
@Service
public class DeadlockAvoidanceService {
@Transactional
public void transfer(Long fromAccountId, Long toAccountId, BigDecimal amount) {
// 按ID排序获取锁,避免死锁
Long firstId = fromAccountId < toAccountId ? fromAccountId : toAccountId;
Long secondId = fromAccountId < toAccountId ? toAccountId : fromAccountId;
Account firstAccount = accountDao.findByIdForUpdate(firstId);
Account secondAccount = accountDao.findByIdForUpdate(secondId);
// 确定哪个是转出账户,哪个是转入账户
Account fromAccount = fromAccountId.equals(firstId) ? firstAccount : secondAccount;
Account toAccount = toAccountId.equals(firstId) ? firstAccount : secondAccount;
// 执行转账
fromAccount.debit(amount);
toAccount.credit(amount);
accountDao.update(fromAccount);
accountDao.update(toAccount);
}
}
问题4:长事务导致性能问题
❓ 现象: 长时间运行的事务导致系统性能下降
解决方案:
@Service
public class LongTransactionOptimizationService {
// ❌ 错误:长事务
@Transactional
public void processLargeDataBad() {
List<User> users = userDao.findAll(); // 可能很多数据
for (User user : users) {
// 复杂的业务逻辑
processUser(user);
userDao.update(user);
}
}
// ✅ 正确:分批处理
public void processLargeDataGood() {
int batchSize = 100;
int offset = 0;
List<User> users;
do {
users = processBatch(offset, batchSize);
offset += batchSize;
} while (users.size() == batchSize);
}
@Transactional
private List<User> processBatch(int offset, int batchSize) {
List<User> users = userDao.findUsers(offset, batchSize);
for (User user : users) {
processUser(user);
userDao.update(user);
}
return users;
}
}
相关文章
Spring基础系列
- Spring基本应用 - Spring容器和IoC
- Spring依赖注入 - DI机制详解
- Spring AOP - 面向切面编程
- Spring数据库开发 - 数据访问
Spring MVC系列
- Spring MVC核心架构 - MVC框架基础
- Spring MVC拦截器完整指南 - 拦截器机制
MyBatis集成
- MyBatis核心配置 - 持久层框架
- Spring和MyBatis整合 - 框架集成
SpringBoot现代化开发
- SpringBoot项目结构 - SpringBoot基础
数据库相关
- MySQL性能优化 - 数据库调优
- Redis缓存策略 - 缓存应用
分布式系统
- 分布式事务解决方案 - 分布式一致性
- 消息队列应用 - 异步处理
总结
Spring事务管理是企业级应用开发的核心技术,通过统一的事务抽象和灵活的配置方式,大大简化了事务编程的复杂性。
🎯 核心价值
- 统一抽象:屏蔽底层事务实现差异,提供一致的编程模型
- 声明式管理:通过注解或配置实现非侵入式事务管理
- 灵活配置:支持多种传播机制和隔离级别
- 异常处理:智能的事务回滚和异常处理机制
🛠️ 技术要点
- 事务基础:ACID属性、并发问题、隔离级别
- Spring架构:事务管理器、事务定义、事务状态
- 声明式事务:注解配置、XML配置、传播机制
- 编程式事务:TransactionTemplate、PlatformTransactionManager
- 分布式事务:JTA、Seata、两阶段提交
🚀 最佳实践
- 合理选择事务范围:避免长事务,缩小事务边界
- 正确使用传播属性:根据业务需求选择合适的传播机制
- 优化事务性能:使用只读事务、设置合理超时时间
- 处理异常回滚:明确回滚规则,正确处理异常
- 避免常见陷阱:自调用、访问权限、异常捕获