事务管理完全指南 
字数
1552 字
阅读时间
7 分钟
1 事务的定义 
事务(Transaction)是一个操作序列,这些操作要么都执行,要么都不执行,是一个不可分割的工作单位。事务是数据库维护数据一致性的单位,在数据库提交工作时,可确保要么所有修改都已保存,要么所有修改都不保存。
2 事务的特性(ACID) 
事务具有四个特性,通常称为ACID特性:
2.1 原子性(Atomicity) 
- 事务是不可分割的最小操作单位
- 事务中的所有操作要么全部完成,要么全部失败回滚
- 不存在部分完成的情况
2.2 一致性(Consistency) 
- 事务必须使数据库从一个一致性状态变换到另一个一致性状态
- 保证数据库的完整性约束
- 例如:转账前后,两个账户的总金额保持不变
2.3 隔离性(Isolation) 
- 多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 通过事务隔离级别实现
- 不同隔离级别提供不同程度的隔离性
2.4 持久性(Durability) 
- 一个事务一旦提交,其对数据库的修改将永久保存
- 即使系统崩溃,提交的数据也不会丢失
3 并发问题 
在多个事务并发执行时,可能会出现以下问题:
3.1 脏读(Dirty Read) 
- 一个事务读取了另一个未提交事务修改过的数据
- 如果另一个事务回滚,那么当前事务读取的数据就是无效的
3.2 不可重复读(Non-repeatable Read) 
- 一个事务内多次读取同一数据,但其他事务在这期间修改了数据
- 导致两次读取的结果不一致
3.3 幻读(Phantom Read) 
- 一个事务内多次查询某个范围的记录,但其他事务在这期间插入了新的记录
- 导致两次查询返回的记录数不一致
4 MySQL事务隔离级别 
4.1 READ UNCOMMITTED(读未提交) 
- 最低的隔离级别
- 允许读取未提交的数据
- 可能出现脏读、不可重复读和幻读
- 实际应用中很少使用
4.2 READ COMMITTED(读已提交) 
- 只能读取已经提交的数据
- 可以避免脏读
- 可能出现不可重复读和幻读
- Oracle数据库默认级别
4.3 REPEATABLE READ(可重复读) 
- 保证同一事务中多次读取同样数据的结果一致
- 可以避免脏读和不可重复读
- 可能出现幻读
- MySQL默认的隔离级别
4.4 SERIALIZABLE(串行化) 
- 最高的隔离级别
- 完全串行化的读写,事务之间完全隔离
- 可以避免脏读、不可重复读和幻读
- 性能最差,很少使用
4.5 隔离级别对比 
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
 |-------- -|------|------------|-- ----|
 | READ UNCOMMITTED | 可能 | 可能 | 可能 |
 | READ COMMITTED | 不可能 | 可能 | 可能 |
 | REPEATABLE READ | 不可能 | 不可能 | 可能 |
 | SERIALIZABLE | 不可能 | 不可能 | 不可能 |
4.6 MySQL设置隔离级别 
sql
-- 查看当前隔离级别
SELECT @@transaction_isolation;
-- 设置当前会话隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 设置全局隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;5 需要使用事务管理的场景 
- 涉及多表操作 - 多个表数据需要同时更新
- 保证数据的一致性
 
- 金融交易 - 转账
- 支付
- 退款
 
- 库存管理 - 下单减库存
- 订单取消恢复库存
 
- 数据批量处理 - 批量导入数据
- 批量更新操作
 
- 关键业务操作 - 用户注册
- 订单处理
- 积分变更
 
6 Java事务管理实现 
6.1 JDBC事务 
java
Connection conn = null;
try {
    // 获取连接
    conn = DriverManager.getConnection(URL, USERNAME, PASSWORD);
    // 关闭自动提交
    conn.setAutoCommit(false);
    
    // 执行SQL操作
    Statement stmt = conn.createStatement();
    stmt.execute("UPDATE account SET balance = balance - 100 WHERE id = 1");
    stmt.execute("UPDATE account SET balance = balance + 100 WHERE id = 2");
    
    // 提交事务
    conn.commit();
} catch (Exception e) {
    // 发生异常,回滚事务
    if (conn != null) {
        conn.rollback();
    }
    throw e;
} finally {
    // 恢复自动提交
    if (conn != null) {
        conn.setAutoCommit(true);
    }
}6.2 JTA事务 
java
@Resource
UserTransaction utx;
try {
    utx.begin();
    // 执行业务逻辑
    userDAO.updateUser(user);
    orderDAO.createOrder(order);
    utx.commit();
} catch (Exception e) {
    utx.rollback();
    throw e;
}7 Spring事务管理 
7.1 声明式事务 
java
@Transactional
public class UserService {
    
    @Transactional(rollbackFor = Exception.class)
    public void transfer(Long fromId, Long toId, BigDecimal amount) {
        Account fromAccount = accountDao.getAccount(fromId);
        Account toAccount = accountDao.getAccount(toId);
        
        fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
        toAccount.setBalance(toAccount.getBalance().add(amount));
        
        accountDao.updateAccount(fromAccount);
        accountDao.updateAccount(toAccount);
    }
}7.2 编程式事务 
java
@Autowired
private TransactionTemplate transactionTemplate;
public void transfer(final Long fromId, final Long toId, final BigDecimal amount) {
    transactionTemplate.execute(new TransactionCallbackWithoutResult() {
        protected void doInTransactionWithoutResult(TransactionStatus status) {
            try {
                Account fromAccount = accountDao.getAccount(fromId);
                Account toAccount = accountDao.getAccount(toId);
                
                fromAccount.setBalance(fromAccount.getBalance().subtract(amount));
                toAccount.setBalance(toAccount.getBalance().add(amount));
                
                accountDao.updateAccount(fromAccount);
                accountDao.updateAccount(toAccount);
            } catch (Exception e) {
                status.setRollbackOnly();
                throw e;
            }
        }
    });
}7.3 Spring事务传播行为 
- REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务
- SUPPORTS:如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行
- MANDATORY:如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常
- REQUIRES_NEW:创建一个新的事务,如果当前存在事务,则把当前事务挂起
- NOT_SUPPORTED:以非事务方式运行,如果当前存在事务,则把当前事务挂起
- NEVER:以非事务方式运行,如果当前存在事务,则抛出异常
- NESTED:如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行
7.4 Spring事务配置示例 
java
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
    @Bean
    public DataSource dataSource() {
        return new DruidDataSource();
    }
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    @Bean
    public TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager) {
        return new TransactionTemplate(transactionManager);
    }
}
`
``