Spring事务原理
Spring事务的本质是对数据库事务的封装支持,没有数据库对事务的支持,Spring本身无法提供事务管理功能。对于用JDBC操作数据库想要用到事务,必须经过获取连接——》开启事务——》执行CRUD操作——》提交/回滚事务——》关闭连接几部分操作。使用Spring管理事务后,可以省掉自己写代码开启、提交/回滚事务的操作。
Spring事务通过AOP动态代理实现,使用上通常要先在配置文件中开启事务,然后通过xml文件或注解配置要执行注解的类方法,然后在调用对应类实例方法时,Spring会自动生成代理,在调用前设置事务操作、调用方法后进行事务回滚或提交操作。
Spring事务的SPI(服务提供接口,给第三方服务实现类提供的接口实现要求)接口主要有:TransactionDefinition、PlatformTransactionManager、TransactionStatus。
1、org.springframework.transaction.TransactionDefinition,它用于定义一个事务。它包含了事务的静态属性,比如:事务传播行为、隔离级别、超时时间等等。
Spring 为我们提供了一个默认的实现类:DefaultTransactionDefinition,该类适用于大多数情况。如果该类不能满足需求,可以通过实现 TransactionDefinition 接口来实现自己的事务定义。
2、org.springframework.transaction.PlatformTransactionManager,用于执行具体的事务操作,方法列表如下:全部抛出TransactionException异常
PlatformTransactionManager 的主要实现类大致如下:
根据底层所使用的不同的持久化 API 或框架,使用如下:
DataSourceTransactionManager:适用于使用JDBC和iBatis进行数据持久化操作的情况。
HibernateTransactionManager:适用于使用Hibernate进行数据持久化操作的情况。
JpaTransactionManager:适用于使用JPA进行数据持久化操作的情况。
另外还有JtaTransactionManager 、JdoTransactionManager、JmsTransactionManager等等。
如果我们使用JTA进行事务管理,我们可以通过 JNDI 和 Spring 的 JtaTransactionManager 来获取一个容器管理的 DataSource。JtaTransactionManager 不需要知道 DataSource 和其他特定的资源,因为它将使用容器提供的全局事务管理。而对于其他事务管理器,比如DataSourceTransactionManager,在定义时需要提供底层的数据源作为其属性,也就是 DataSource。与 HibernateTransactionManager 对应的是 SessionFactory,与 JpaTransactionManager 对应的是 EntityManagerFactory 等等。
下面分析DataSourceTransactionManager,指明dataSourcce:javax.sql.DataSource。
注入dataSourcce:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
3、TransactionStatus
PlatformTransactionManager.getTransaction(…) 方法返回一个 TransactionStatus 对象。返回的TransactionStatus 对象可能代表一个新的或已经存在的事务(如果在当前调用堆栈有一个符合条件的事务)。TransactionStatus 接口提供了一个简单的控制事务执行和查询事务状态的方法。
TransactionStatus 接口中定义的主要方法
public interface TransactionStatus{
boolean isNewTransaction();
void setRollbackOnly();
boolean isRollbackOnly();
}
事务的隔离级别
默认是数据库事务的隔离级别,大部分数据库是读提交(可解决脏读问题,事务未提交前被数据被其它事务读到),MySQL 是可重复读(一个事务中间进行了两次数据读操作,因为两次之间有其它事务对数据进行了update,而导致两次读取的数据不一致),最高的隔离级别是串读(可解决幻读问题,一个事务读了两次某一范围的数据,中间其它事务对这一范围数据做了增加或删除操作而导致前后读到的数据条数不一致。与不可重复读的区别时前者是范围内数据受到增加或删除影响,需要锁表解决,后者是受到数据修改的影响,可以通过锁行解决)
事务的传播特性
常用的传播特性有PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED三种,要注意它们之间的区别。
使用嵌套事务的场景有两点需求:
- 需要事务BC与事务AD一起commit,即:作为事务AD的子事务,事务BC只有在事务AD成功commit时(阶段3成功)才commit。这个需求简单称之为“联合成功”。这一点PROPAGATION_REQUIRED可以做到。
- 需要事务BC的rollback不(无条件的)影响事务AD的commit。这个需求简单称之为“隔离失败”。这一点PROPAGATION_REQUIRES_NEW可以做到。
使用PROPAGATION_REQUIRED满足需求1,但子事务BC的rollback会无条件地使父事务AD也rollback,不能满足需求2。
使用PROPAGATION_REQUIRES_NEW满足需求2,但子事务(这时不应该称之为子事务)BC是完全新的事务上下文,父事务(这时也不应该称之为父事务)AD的成功与否完全不影响BC的提交,不能满足需求1。
同时满足上述两条需求就要用到PROPAGATION_NESTED了。PROPAGATION_NESTED在事务AD执行到B点时,设置了savePoint(关键)。
当BC事务成功commit时,PROPAGATION_NESTED的行为与PROPAGATION_REQUIRED一样。只有当事务AD在D点成功commit时,事务BC才真正commit,如果阶段3执行异常,导致事务AD rollback,事务BC也将一起rollback ,从而满足了“联合成功”。
当阶段2执行异常,导致BC事务rollback时,因为设置了savePoint,AD事务可以选择与BC一起rollback或继续阶段3的执行并保留阶段1的执行结果,从而满足了“隔离失败”。
PROPAGATION_REQUIRED