Spring 的事务管理 API

本文介绍 Spring 提供的事务管理 API,以及编程式事务、XML 声明式事务和注解声明式事务三种配置方式。


一、为什么需要 Spring 事务 API?

前面我们通过 AOP + 自定义事务管理器的方式控制事务,虽然解决了代码侵入问题,但仍需要自己编写事务管理的代码(开启、提交、回滚等),较为繁琐。

Spring 提供了一套完整的事务管理 API,我们只需要配置,无需手写事务控制逻辑。


二、Spring 事务管理的三大核心 API

2.1 PlatformTransactionManager(平台事务管理器)

这是 Spring 事务管理的核心接口,负责真正的事务管理工作。常用实现类如下:

实现类 底层技术
DataSourceTransactionManager JDBC / MyBatis
HibernateTransactionManager Hibernate

2.2 TransactionDefinition(事务定义信息)

用于定义事务的相关属性:

事务隔离级别
常量 说明
ISOLATION_DEFAULT 使用数据库默认隔离级别(Spring 默认值)
ISOLATION_READ_UNCOMMITTED 读未提交,可能出现脏读
ISOLATION_READ_COMMITTED 读已提交,解决脏读(Oracle 默认)
ISOLATION_REPEATABLE_READ 可重复读,解决不可重复读(MySQL 默认)
ISOLATION_SERIALIZABLE 串行化,解决幻读,性能最低
事务传播行为

传播行为决定了当一个事务方法被另一个事务方法调用时,如何处理事务:

传播行为 说明
REQUIRED 有事务则加入,没有则新建(默认值,适用于增删改)
SUPPORTS 有事务则加入,没有则以非事务方式执行(适用于查询)
MANDATORY 必须在事务中运行,否则抛出异常
REQUIRES_NEW 新建事务,如果当前有事务则挂起
NOT_SUPPORTED 以非事务方式运行,如果有事务则挂起
NEVER 以非事务方式运行,如果有事务则抛出异常
NESTED 在嵌套事务中运行(有事务则嵌套,没有则类似 REQUIRED)
其他属性
  • 超时时间timeout):事务最长执行时间,默认 -1(无限制),单位为秒
  • 是否只读readOnly):只读事务用于查询操作,可提升性能

2.3 TransactionStatus(事务状态)

描述某个时间点上事务的状态,提供刷新、回滚、判断等方法(通常由框架内部使用)。


三、编程式事务(了解)

Spring 编程式事务控制是不常用的方式,这里简单了解即可。

3.1 配置平台事务管理器和事务模板

xml 复制代码
<!-- 配置平台事务管理器 -->
<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 配置事务管理模板 -->
<bean id="transactionTemplate"
    class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager"></property>
</bean>

3.2 在业务类中注入并使用

xml 复制代码
<bean id="accountService" class="com.xq.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
    <property name="transactionTemplate" ref="transactionTemplate"></property>
</bean>

在业务方法中,使用 TransactionTemplateexecute() 方法执行事务逻辑,代码侵入感依然较强,实际项目不推荐。


四、声明式事务 ------ XML 方式(重点)

声明式事务通过配置的方式管理事务,业务代码完全不需要修改。这是实际项目中最常用的方式之一。

4.1 去掉事务管理模板,回归纯净业务类

业务类只负责业务逻辑,不含任何事务代码:

java 复制代码
public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public void transfer(String sourceName, String targetName, Double money) {
        Account sourceAccount = accountDao.findAccountByName(sourceName);
        Account targetAccount = accountDao.findAccountByName(targetName);
        sourceAccount.setMoney(sourceAccount.getMoney() - money);
        targetAccount.setMoney(targetAccount.getMoney() + money);
        accountDao.updateAccount(sourceAccount);
        int i = 1 / 0; // 模拟异常
        accountDao.updateAccount(targetAccount);
    }
}

4.2 配置平台事务管理器

xml 复制代码
<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

4.3 配置事务增强(tx:advice)

xml 复制代码
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- name:给哪个业务方法配置事务 -->
        <!-- propagation:事务传播行为,REQUIRED 是默认值 -->
        <tx:method name="transfer" propagation="REQUIRED"/>
        <!-- 也可以用通配符,为所有方法配置 -->
        <!-- <tx:method name="*" propagation="REQUIRED"/> -->
        <!-- 查询方法设为只读 -->
        <!-- <tx:method name="find*" propagation="SUPPORTS" read-only="true"/> -->
    </tx:attributes>
</tx:advice>

4.4 配置 AOP,将事务增强和切点关联

xml 复制代码
<aop:config>
    <aop:pointcut
        expression="execution(* com.xq.service.impl.*.*(..))"
        id="p1">
    </aop:pointcut>
    <!-- 使用 advisor 将事务增强绑定到切点 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="p1"></aop:advisor>
</aop:config>

4.5 完整配置文件

xml 复制代码
<bean id="accountDao" class="com.xq.dao.impl.AccountDaoImpl">
    <property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>

<bean id="accountService" class="com.xq.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/ssm"></property>
    <property name="user" value="root"></property>
    <property name="password" value="Admin123!"></property>
</bean>

<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="transfer" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

<aop:config>
    <aop:pointcut expression="execution(* com.xq.service.impl.*.*(..))" id="p1"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="p1"></aop:advisor>
</aop:config>

五、声明式事务 ------ 注解方式(重点)

注解方式是目前最流行的事务配置方式,步骤更简单。

5.1 配置开启注解对事务的支持

xml 复制代码
<!-- 配置平台事务管理器 -->
<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!-- 开启注解事务支持 -->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

5.2 在业务类(或方法)上添加 @Transactional 注解

java 复制代码
@Service
@Transactional // 作用在类上,对所有方法生效
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

    // 也可以在方法上单独配置
    @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
    public void transfer(String sourceName, String targetName, Double money) {
        Account sourceAccount = accountDao.findAccountByName(sourceName);
        Account targetAccount = accountDao.findAccountByName(targetName);
        sourceAccount.setMoney(sourceAccount.getMoney() - money);
        targetAccount.setMoney(targetAccount.getMoney() + money);
        accountDao.updateAccount(sourceAccount);
        int i = 1 / 0;
        accountDao.updateAccount(targetAccount);
    }
}

@Transactional 注解支持的属性:

属性 说明 默认值
propagation 传播行为 REQUIRED
isolation 隔离级别 DEFAULT
timeout 超时时间(秒) -1
readOnly 是否只读 false
rollbackFor 指定回滚的异常类型 运行时异常
noRollbackFor 指定不回滚的异常类型 ---

六、三种事务方式对比

方式 复杂度 代码侵入 推荐程度
编程式事务 高(业务代码含事务逻辑) 不推荐
XML 声明式事务 推荐(配置集中)
注解声明式事务 极低(只加注解) 推荐(代码简洁)

七、小结

Spring 的事务管理体现了框架设计的精髓:

  • 编程式事务:灵活但繁琐,了解即可
  • XML 声明式事务:配置统一,适合大型项目统一管理
  • 注解声明式事务:简洁直观,是目前主流的选择

理解事务的传播行为和隔离级别,是面试和实际开发中的重点,需要重点掌握。