一、Spring事务核心接口:
Spring 事务接口如图:
1.1 事务管理器
Spring并不直接管理事务,而是提供了多种事务管理器,他们将事务管理的职责委托给Hibernate或者JTA等持久化机制所提供的相关平台框架的事务来实现。
Spring事务管理器的接口是org.springframework.transaction.PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC、Hibernate等都提供了对应的事务管理器,但是具体的实现就是各个平台自己的事情了。
Public interface PlatformTransactionManager()...{
// 由TransactionDefinition得到TransactionStatus对象
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
// 提交
Void commit(TransactionStatus status) throws TransactionException;
// 回滚
Void rollback(TransactionStatus status) throws TransactionException;
}
1.1.1 JDBC事务
如果应用程序中直接使用JDBC来进行持久化,DataSourceTransactionManager会为你处理事务边界。为了使用DataSourceTransactionManager,你需要使用如下的XML将其装配到应用程序的上下文定义中:
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
实际上,DataSourceTransactionManager是通过调用java.sql.Connection来管理事务,而后者是通过DataSource获取到的。通过调用连接的commit()方法来提交事务,同样,事务失败则通过调用rollback()方法进行回滚。
1.1.2 Hibernate事务
如果应用程序的持久化是通过Hibernate实习的,那么你需要使用HibernateTransactionManager。对于Hibernate3,需要在Spring上下文定义中添加如下的声明:
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
sessionFactory属性需要装配一个Hibernate的session工厂,HibernateTransactionManager的实现细节是它将事务管理的职责委托给org.hibernate.Transaction对象,而后者是从Hibernate Session中获取到的。当事务成功完成时,HibernateTransactionManager将会调用Transaction对象的commit()方法,反之,将会调用rollback()方法。
2.1.3 Java持久化API事务(JPA)
Hibernate多年来一直是事实上的Java持久化标准,但是现在Java持久化API作为真正的Java持久化标准进入大家的视野。如果你计划使用JPA的话,那你需要使用Spring的JpaTransactionManager来处理事务。你需要在Spring中这样配置JpaTransactionManager:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
JpaTransactionManager只需要装配一个JPA实体管理工厂(javax.persistence.EntityManagerFactory接口的任意实现)。JpaTransactionManager将与由工厂所产生的JPA EntityManager合作来构建事务。
2.1.4 Java原生API事务
如果你没有使用以上所述的事务管理,或者是跨越了多个事务管理源(比如两个或者是多个不同的数据源),你就需要使用JtaTransactionManager:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManagerName" value="java:/TransactionManager" />
</bean>
JtaTransactionManager将事务管理的责任委托给javax.transaction.UserTransaction和javax.transaction.TransactionManager对象,其中事务成功完成通过UserTransaction.commit()方法提交,事务失败通过UserTransaction.rollback()方法回滚。
1.2事务属性:
上面讲到的事务管理器接口PlatformTransactionManager通过getTransaction(TransactionDefinition definition)方法来得到事务,这个方法里面的参数是TransactionDefinition类,这个类就定义了一些基本的事务属性。
那么什么是事务属性呢?事务属性可以理解成事务的一些基本配置,描述了事务策略如何应用到方法上。事务属性包含了5个方面,如图所示:
而TransactionDefinition接口内容如下:
public interface TransactionDefinition {
int getPropagationBehavior(); // 返回事务的传播行为
int getIsolationLevel(); // 返回事务的隔离级别,事务管理器根据它来控制另外一个事务可以看到本事务内的哪些数据
int getTimeout(); // 返回事务必须在多少秒内完成
boolean isReadOnly(); // 事务是否只读,事务管理器能够根据这个返回值进行优化,确保事务是只读的
}
二、spring 事务原理
Spring事务的本质其实就是数据库对事务的支持,没有数据库的事务支持,spring是无法提供事务功能的。对于纯JDBC操作数据库,想要用到事务,可以按照以下步骤进行:
获取连接 Connection con = DriverManager.getConnection()
开启事务con.setAutoCommit(true/false);
执行CRUD
提交事务/回滚事务 con.commit() / con.rollback();
关闭连接 conn.close();
使用Spring的事务管理功能后,我们可以不再写步骤 2 和 4 的代码,而是由Spirng 自动完成。
那么Spring是如何在我们书写的 CRUD 之前和之后开启事务和关闭事务的呢?解决这个问题,也就可以从整体上理解Spring的事务管理实现原理了。下面简单地介绍下,注解方式为例子
配置文件开启注解驱动,在相关的类和方法上通过注解@Transactional标识。
spring 在启动的时候会去解析生成相关的bean,这时候会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据@Transaction的相关参数进行相关配置注入,这样就在代理中为我们把相关的事务处理掉了(开启正常提交事务,异常回滚事务)。
真正的数据库层的事务提交和回滚是通过binlog或者redo log实现的。
Spring事务的传播属性
传播行为 含义
PROPAGATION_REQUIRED(XML文件中为REQUIRED) 表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)
PROPAGATION_SUPPORTS(XML文件中为SUPPORTS) 表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行
PROPAGATION_MANDATORY(XML文件中为MANDATORY) 表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常
PROPAGATION_NESTED(XML文件中为NESTED) 表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同PROPAGATION_REQUIRED的一样
PROPAGATION_NEVER(XML文件中为NEVER) 表示当方法务不应该在一个事务中运行,如果存在一个事务,则抛出异常
PROPAGATION_REQUIRES_NEW(XML文件中为REQUIRES_NEW) 表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。
PROPAGATION_NOT_SUPPORTED(XML文件中为NOT_SUPPORTED) 表示该方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行
1) PROPAGATION_REQUIRED
如果B的方法methodB()的事务传播特性是propagation_supports,A.methodA()调用B的methodB()方法,那么如果A的方法包含事务,则B的方法则不从新开启事务,A不包含事务的话B会开启事务。
1、 如果B的methodB()抛出异常,A的methodB()没有捕获,则A和B的事务都会回滚;
2、 如果B的methodB()运行期间异常会导致B的methodB()的回滚,A如果捕获了异常,并正常提交事务,则会发生Transaction rolled back because it has been marked as rollback-only的异常。
3、 如果A的methodA()运行期间异常,则A和B的Method的事务都会被回滚
2) PROPAGATION_SUPPORTS
如果B的方法methodB()的事务传播特性是propagation_supports,A.methodA()调用B的methodB()方法,那么如果A的方法包含事务,则B运行在此事务环境中,如果A的方法不包含事务,则B运行在非事务环境;
1、如果A没有事务,则A和B的运行出现异常都不会回滚。
2、如果A有事务,A的method方法执行抛出异常,B.methodB和A.methodA都会回滚。
3、如果A有事务,B.method抛出异常,B.methodB和A.methodA都会回滚,如果A捕获了B.method抛出的异常,则会出现异常Transactionrolled back because it has been marked as rollback-only。
3) PROPAGATION_MANDATORY
B.methodB()事务传播特性定义为:PROPAGATION_MANDATORY,表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常
1、如果A的methoda()方法没有事务运行环境,则B的methodB()执行的时候会报如下异常:No existingtransaction found for transaction marked with propagation ‘mandatory’
2、如果A的Methoda()方法有事务并且执行过程中抛出异常,则A.methoda()和B.methodb()执行的操作被回滚;
3、如果A的methoda()方法有事务,则B.methodB()抛出异常时,A的methoda()和B.methodB()都会被回滚;如果A捕获了B.method抛出的异常,则会出现异常Transaction rolled back because ithas been marked as rollback-only
4) PROPAGATION_NESTED
B的methodB()定义的事务为PROPAGATION_NESTED;
1、如果A的MethodA()不存在事务,则B的methodB()运行在一个新的事务中,B.method()抛出的异常,B.methodB()回滚,但A.methodA()不回滚;如果A.methoda()抛出异常,则A.methodA()和B.methodB()操作不回滚。
2、如果A的methodA()存在事务,则A的methoda()抛出异常,则A的methoda()和B的Methodb()都会被回滚;
3、如果A的MethodA()存在事务,则B的methodB()抛出异常,B.methodB()回滚,如果A不捕获异常,则A.methodA()和B.methodB()都会回滚,如果A捕获异常,则B.methodB()回滚,A不回滚;
5)PROPAGATION_NEVER
表示事务传播特性定义为PROPAGATION_NEVER的方法不应该运行在一个事务环境中
如果B.methodB()的事务传播特性被定义为PROPAGATION_NEVER,则如果A.methodA()方法存在事务,则会出现异常Existingtransaction found for transaction marked with propagation ‘never’。
6)PROPAGATION_REQUIRES_NEW
表示事务传播特性定义为PROPAGATION_REQUIRES_NEW的方法需要运行在一个新的事务中。
如有一下调用关系:B.methodB()事务传播特性为PROPAGATION_REQUIRES_NEW.
1、如果A存在事务,A.methodA()抛出异常,A.methodA()的事务被回滚,但B.methodB()事务不受影响;如果B.methodB()抛出异常,A不捕获的话,A.methodA()和B.methodB()的事务都会被回滚。如果A捕获的话,A.methodA()的事务不受影响但B.methodB()的事务回滚。
7) PROPAGATION_NOT_SUPPORTED
表示该方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行。
如果B.methodB()方法传播特性被定义为:PROPAGATION_NOT_SUPPORTED。
1、如果A.methodA()存在事务,如果B.methodB()抛出异常,A.methodA()不捕获的话,A.methodA()的事务被回滚,而B.methodB()出现异常前数据库操作不受影响。如果A.methodA()捕获的话,则A.methodA()的事务不受影响,B.methodB()异常抛出前的数据操作不受影响。
隔离级别
隔离级别 含义
ISOLATION_DEFAULT 使用后端数据库默认的隔离级别
ISOLATION_READ_UNCOMMITTED 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
ISOLATION_READ_COMMITTED 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
ISOLATION_REPEATABLE_READ 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生
ISOLATION_SERIALIZABLE 最高的隔离级别,完全服从ACID的隔离级别,确保阻止脏读、不可重复读以及幻读,也是最慢的事务隔离级别,因为它通常是通过完全锁定事务相关的数据库表来实现的
声明式事务