Spring Boot 提供了声明式事务管理。

@Transactional 注解

@Transactional 可以用于方法和类。

@Service
public class UserService {
    @Transactional
    public Long registerUser(User user) {
        // 执行数据库操作
        return id;
    }
}

使用方法

  • 配置启用 @EnableTransactionManagement
  • 在 Spring 配置中指定事务管理器
  • 公共方法上添加 @Transactional 注解

声明式事务

事务的四个属性的特性 ACID:

  • Atomic
  • Consistent
  • Isolation
  • Duration

事务隔离级别

  • 脏读,事务 A 读取了事务 B 未提交的数据并做了修改,如果 B 事务回滚,事务 A 读取的数据就是错误的,产生脏读
  • 不可重复读,同一个事务相同的查询得到不同的结果,读取到了已经提交事物的更改数据
  • 幻读,事务 A 读取了事务 B 提交的新数据

MySQL 的默认事务隔离级别:可重复读

Spring 事务管理委托给底层的持久化框架完成。

Spring 为不同的持久化框架提供了不同的 PlatformTransactionManager 接口。

  • org.springframework.jdbc.datasource.DataSourceTransactionManager 提供对单个 javax.sql.DataSource 事务的管理,用于 Spring JDBC ,iBATIS 或 MyBatis 框架事务管理
  • org.springframework.orm.jpa.JpaTransactionManager 对 javax.persistence.EntityManagerFactory 事务支持,集成 JPA 实现框架事务
  • org.springframework.transaction.jta.JtaTransactionManager 提供分布式事务管理支持,事务管理委托 Java EE 应用服务器事务管理

基于 @Transactional 注解的声明式事务

在方法前后进行拦截,在目标方法开始之前创建或加入事务,执行后根据情况提交或回滚事务。

无需在逻辑代码中混入事务管理的代码,通过配置或注解完成。

<?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"
       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">

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

    <!--事物管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--数据源-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/my_test?useSSL=false&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="chenhao1991@"/>
    </bean>

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

    <!--业务bean-->
    <bean id="accountService" class="com.chenhao.aop.AccountServiceImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate"/>
    </bean>

</beans>

Transactional 注解工作原理

  • 传播(propagation) 隔离(isolation)
  • 如何避免 pitfall

Spring 的声明式注解底层使用了 AOP,Spring 为添加了 @Transactional 的类增加代理类,这一层代理在运行时几乎时隐形的,用户不可见。这个方式使得 Spring 可以在方法的前后来增加额外的行为。事务管理就是利用这个原理来实现的。

拦截方法 TransactionInterceptor

TransactionInterceptor 实现了 MethodInterceptor,通过该拦截器拦截被注解 @Transactional 的方法,然后实现了 invoke 方法:

@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
	// Work out the target class: may be {@code null}.
	// The TransactionAttributeSource should be passed the target class
	// as well as the method, which may be from an interface.
	Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

	// Adapt to TransactionAspectSupport's invokeWithinTransaction...
	return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
		@Override
		public Object proceedWithInvocation() throws Throwable {
			return invocation.proceed();
		}
	});
}

TransactionAttributeSourcePointcut 定义 Spring Transaction 的拦截规则

PlatformTransactionManager 事务管理器

如果使用 JPA(Java Persistence API) ,则需要自己手工实现:

UserTransaction utx = entityManager.getTransaction();

try {
	utx.begin();
	businessLogic();
	utx.commit();
} catch(Exception ex) {
	utx.rollback();
	throw ex;
}

这种方式的实现代码很清晰,但是引入了很多缺点:

  • 很罗嗦并且容易出错
  • 任何出错可能造成错误
  • 难以调试和重现问题
  • 降低了代码的可读性
  • 如何处理一个方法调用另一个事务方法的问题
@Transactional
public void businessLogic() {
... use entity manager inside a transaction ...
}

通过使用 @Transactional 注解,一方面提高了代码可读性,另一方面也提高了扩展性。

如果其他事务方法调用 businessLogic() 方法,这个方法会自动加入当前的事务。

The Transaction Manager 处理两件事情:

  • Entity Manager 是否应该创建
  • Database transaction 是否应该开始

在 Before 中需要决定:

  • 事务是否已经开始了
  • 事务的 propagation 属性,比如 REQUIRES_NEW 会立即开始一个新的事务

如果事务管理决定开始一个新的事务:

  • 创建一个新的 entity manager
  • 将 entity manager 绑定到当前线程
  • 从 DB 连接池中获取一个链接
  • 将链接绑定到当前线程

Spring 事务传播机制

编程式事务管理

主要通过 TransactionTemplate 或直接使用 PlatformTransactionManager 实现

@Service
public class UserService {
    @Autowired
    private TransactionTemplate template;

    public Long registerUser(User user) {
        return template.execute(status -> {
            // 执行数据库操作
            return id;
        });
    }
}

事务管理器

Spring Boot 使用事务管理器来处理事务状态(开启、提交、回滚)

  • DataSourceTransactionManager:用于 JDBC 事务
  • JpaTransactionManager:用于 JPA 事务
  • HibernateTransactionManager:用于 Hibernate 事务

传播行为

@Transactional 注解支持配置事务的传播行为,例如:

@Transactional(propagation = Propagation.REQUIRED)
public void someMethod() {
    // 方法体
}

隔离级别

可以通过 @Transactional 注解设置事务的隔离级别:

@Transactional(isolation = Isolation.READ_COMMITTED)
public void someMethod() {
    // 方法体
}