最近再次拜读了spring的源码,对关于spring事物的应用部分加以整理,做如下笔记。如有不正确的地方,欢迎指正,谢谢。本文大致分为如下板块:
- 数据库事物
1.1 ACID- spring事物管理
2.1 PlatformTransactionManager
2.2 @Transactional- 事物管理器
3.1 jdbc事物管理器
3.2 hibernate事物管理器
3.3 其它事物管理器- spring 事物的基本行为
4.1 事物的传播行为
4.1.1 PROPAGATION_REQUIRED
4.1.2 PROPAGATION_SUPPORTS
4.1.3 PROPAGATION_MANDATORY
4.1.4 PROPAGATION_REQUIRES_NEW
4.1.5 PROPAGATION_NOT_SUPPORTED
4.1.6 PROPAGATION_NEVER
4.1.7 PROPAGATION_NESTED
4.2 事物的隔离级别
4.2.1 数据库的隔离问题及级别
4.2.1.1 隔离问题
4.2.1.1 隔离级别
4.2.2 Spirng 的事物隔离级别-Isolation
4.3 事物的只读属性-readOnly
4.4 事物的超时时间-timeout
4.5 回滚规则-rollbackFor
4.6 其它属性
总结
1.数据库事物
数据库事物,指的是数据库执行的逻辑单元,这个逻辑单元遵从ACID原则,用这个原则来保证数据的一致性和完整性。反应到数据库上就是一组sql,要么全部执行成功,要么全部执行失败
。例如下面sql:
--uid001账户余额增加100块
update user_account set account_balance=account_balance+100 where user_id='uid001';
--uid002账户余额减少100块
update user_account set account_balance=account_balance-100 where user_id='uid002';
1.1 ACID
特性 | 英文 | 说明 |
---|---|---|
原子性 | Atomicity | 事物的逻辑单元由一系列操作组成,这一系列操作要么全部成功,要么全部失败,回滚到未执行前一刻的状态 。 |
一致性 | Consistency | 无论操作结果是成功还是失败,数据的业务状态是保持一致的 。如uid001给uid002转账,无论成功还是失败,最终uid001和uid002的账户总和是一致的。 |
隔离性 | Isolation | 数据库同时会存在多个事物,多个事物之间是隔离的,一个事物的操作不会影响到另一个事物的操作结果。但也不是完全隔离,数据库层面定义了多个隔离级别,不通隔离级别隔离的效果是不一样的,隔离的级别越高,数据一致性越好,但是并发度越弱 。四个隔离级别由小到大如下:Read uncommitted<Read committed<Repeatable read<Serializable
|
持久性 | Durability | 事物提交后,对数据的修改时永久的。及时系统发生故障,也能在不会丢失对数据的修改 。一般通过执行前写入日志的方式保证持久性,即使系统崩溃,也能在重启的过程中,通过日志恢复崩溃时处于执行状态的事物。 |
2.spring事物管理
所谓的事物管理指的是“按照指定事物规则执行提交或者回滚操作”
,而spring的事物管理,指的是spring对事物管理规则的抽象
。具体来说就是spring不直接管理事物,而是为了不同的平台(如jdbc hibernate jpa 等)提供了不同的事物管理器 ,将具体的事物管理委托给相应的平台实现,spring并不关心具体事物管理的实现,从而为事物管理提供了通用编程模型
。
2.1 PlatformTransactionManager
上述通用抽象反应到代码层面就是org.springframework.jdbc.datasource.PlatformTransactionManager
类。
/**
* 根据不同的事物行为,获取当前事物,或者新建事物。
* @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
* @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
*/
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
/**
* 就给定的事物状态提交事物。如果事物状态被标记为回滚,则回滚事物
* @param status 当前事物对象
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚当前事物
* @param status 当前事物对象
*/
void rollback(TransactionStatus status) throws TransactionException;
2.2 @Transactional
spring 中使用事物分为两种,一种是声明式事物,一种是编程式事物。考虑到编程式事物基本很少使用,故下文以声明式事物来介绍spring的事物。而一般我们又比较常用@Transactional来实现事物声明。@Transactional 常用以下属性propagation,tionisolation,readOnly,rollbackFor,rollbackForClassName,noRollbackFor,noRollbackForClassName
3 事物管理器
spring 为不同的平台提供了不同的事物管理器。不同的事物管理器都继承自抽象类org.springframework.transaction.support.AbstractPlatformTransactionManager
。该抽象类实现了org.springframework.jdbc.datasource.PlatformTransactionManager ,实现了事物管理器的骨架实现,通过继承该类,仅仅关心平台相关内容即可实现不同的事物管理器。
3.1 jdbc事物管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
jdbc 事物管理器是通过javax.sql.DataSource.getConnection() 获取javax.sql.Connection,通过javax.sql.Connection来进行事物的提交和回滚
。
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
3.2 hibernate事物管理器
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
HibernateTransactionManager 是通过绑定org.hibernate.SessionFactory来实现的 ,通过sessionFactory来绑定org.hibernate.Transaction,提交或者回滚的时候调用org.hibernate.Transaction的commit或者rollback来操作
。
3.3 其它spring事物管理器
除了上面介绍的两个常用的事物管理器之外,spring还提供了其它的事物管理器。如 org.springframework.orm.jpa,org.springframework.transaction.jta.JtaTransactionManager,org.springframework.jms.connection.JmsTransactionManager,org.springframework.transaction.jta.WebLogicJtaTransactionManager等。感兴趣的可以自行查看,在idea中查看PlatformTransactionManager实现类如下
4 spring 事物的基本行为
通过org.springframework.transaction.getTransaction(TransactionDefinition definition)
/**
* 根据不同的事物行为,获取当前事物,或者新建事物。
* @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
* @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
*/
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
可以得知,事物的相关属性就定义在java.sql.Connection.TransactionDefinition类中。那么我们看看该类中分别定义了什么
/**
* 获取传播行为
*/
int getPropagationBehavior();
/**
* 获取隔离级别
*/
int getIsolationLevel();
/**
* 获取超时时间
*/
int getTimeout();
/**
* 获取只读属性
*/
boolean isReadOnly();
/**
* 获取事物的名称 默认为classname+"."+methodName
*/
String getName();
4.1 事物的传播行为
所谓事物的传播行为,指的是被事物标记的方法(注解或者xml声明),之间进行嵌套调用时事物的传播规则
。拿jdbc事物管理器来说,就是共用同一个jdbc connection,还是新建connection,还是抛异常
。这个规则就叫做事物的传播行为。spring定义了如下传播行为:
传播行为 | 说明 |
---|---|
PROPAGATION_REQUIRED | 如果存在事物,则加入当前事物。如果不存在事物,则新建事物。 |
PROPAGATION_SUPPORTS | 支持事物,如果不存在事物则以非事物的状态运行。 |
PROPAGATION_MANDATORY | 必须存在事物,不存在则抛异常。 |
PROPAGATION_REQUIRES_NEW | 无论当前是否存在事物,都新建事物。仅支持JtaTransactionManager作为事物管理器 。 |
PROPAGATION_NOT_SUPPORTED | 不支持事物,如当前存在事物则将当前事物挂起,以非事物的方式运行。仅支持JtaTransactionManager作为事物管理器 。 |
PROPAGATION_NEVER | 不支持事物,如当前存在事物则抛异常。 |
PROPAGATION_NESTED | 嵌套事物。如果当前不存在事物,则以PROPAGATION_REQUIRED的方式运行。如果存在事物则以嵌套事物的方式运行。仅支持DataSourceTransactionManager作为事物管理器和部分JTA事物管理器 。 |
4.1.1 PROPAGATION_REQUIRED
如果存在事物,则加入当前事物。如果不存在事物,则新建事物。默认传播行为。
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
void methodB(){
//doSomethingB()
}
methodB()被调用时不存在事物,则会新建一个事物。methodB 会以事物方式运行。相当于如下操作
void methodA(){
//doSomethingA()
Connection connection=null;
try{
connection=getConnection();
//doSomethingB()
connection.commit();
}catch (RuntimeException e){
connection.rollback();
}finally {
connection.close();
}
}
方法内调用之所以用类似 ((ServiceName)AopContext.currentProxy()).methodB()。是因为spring事物是基于aop实现,aop又是基于代理类实现。直接方法内调用是不会使用到增强类,也就不会调用到被代理类的增强方法
。
4.1.2 PROPAGATION_SUPPORTS
支持事物,如果不存在事物则以非事物的状态运行。
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.SUPPORTS)
void methodB(){
//doSomethingB()
}
调用methodA时,相当于如下:
void methodA(){
Connection connection=null;
try{
connection=getConnection();
//doSomethingA()
//doSomethingB()
connection.commit();
}catch (RuntimeException e){
connection.rollback();
}finally {
connection.close();
}
}
当单独调用methodB 时,methodB 以非事物的方式运行。
4.1.3 PROPAGATION_MANDATORY
必须存在事物,不存在则抛异常。
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.MANDATORY)
void methodB(){
//doSomethingB()
}
无论时调用methodA,还是单独调用methodB都会抛异常
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
4.1.4 PROPAGATION_REQUIRES_NEW
无论当前是否存在事物,都新建事物。
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
((ServiceName)AopContext.currentProxy()).methodB();
//doSomethingA()
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
void methodB(){
//doSomethingB()
//successOrNot
}
当methodA中的doSomethingA()发生异常回滚时,methodB可以正常提交。
相当于如下:
void methodA(){
//当前连接
Connection currentConnection=null;
try{
currentConnection=getConnection();
//挂起当前事物
suspend(currentConnection);
Connection newConnection;
try {
//开启新事物
newConnection=getConnection();
//doSomethingB()
newConnection.commit();
}catch (Exception e){
newConnection.rollback(sa);
}finally {
newConnection.close();
}
//恢复当前事物
resume(currentConnection);
currentConnection.commit();
}catch (RuntimeException e){
currentConnection.rollback();
}finally {
currentConnection.close();
}
}
新建事物不受外部事物的影响,PROPAGATION_REQUIRES_NEW可以用在保证接口最近在读spring源码的时候,对spring事物又有了一点新的理解。遂加以整理如下笔记。如有不正确的地方,欢迎指正,谢谢。本文大致分为如下板块:
- 什么是数据库事物
- 什么是spring事物
- spring事物核心接口
- 不同的事物管理器
- 事物的传播行为
- 事物的隔离级别
- 事物工具类
1.什么是数据库事物
数据库事物,指的是数据库执行的逻辑单元,这个逻辑单元遵从ACID原则,用这个原则来保证数据的一致性和完整性。反应到数据库上就是一组sql,要么全部执行成功,要么全部执行失败。例如下面sql:
--uid001账户余额增加100块
update user_account set account_balance=account_balance+100 where user_id='uid001';
--uid002账户余额减少100块
update user_account set account_balance=account_balance-100 where user_id='uid002';
1.1 ACID
特性 | 英文 | 说明 |
---|---|---|
原子性 | Atomicity | 事物的逻辑单元由一系列操作组成,这一系列操作要么全部成功,要么全部失败,回滚到未执行前一刻的状态。 |
一致性 | Consistency | 无论操作结果是成功还是失败,数据的业务状态是保持一致的。如uid001给uid002转账,无论成功还是失败,最终uid001和uid002的账户总和是一致的。 |
隔离性 | Isolation | 数据库同时会存在多个事物,多个事物之间是隔离的,一个事物的操作不会影响到另一个事物的操作结果。但也不是完全隔离,数据库层面定义了多个隔离级别,不通隔离级别隔离的效果是不一样的,隔离的级别越高,数据一致性越好,但是并发度越弱。四个隔离级别由小到大如下:Read uncommitted<Read committed<Repeatable read<Serializable |
持久性 | Durability | 事物提交后,对数据的修改时永久的。及时系统发生故障,也能在不会丢失对数据的修改。一般通过执行前写入日志的方式保证持久性,即使系统崩溃,也能在重启的过程中,通过日志恢复崩溃时处于执行状态的事物。 |
2.什么是spring事物管理
所谓的事物管理指的是“按照指定事物规则执行提交或者回滚操作”,而spring的事物管理,指的是spring对事物管理规则的抽象。具体来说就是spring不直接管理事物,而是为了不同的平台提供了不同的事物管理器 如jdbc hibernate jpa 等,将具体的事物管理委托给相应的持久化框架实现,spring并不关心具体事物管理的实现,具体的实现留给对应的平台来实现,从而为事物管理提供了通用编程模型。这个通用抽象反应到代码层面就是org.springframework.jdbc.datasource.PlatformTransactionManager 类。
/**
* 根据不同的事物行为,获取当前事物,或者新建事物。
* @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
* @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
*/
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
/**
* 就给定的事物状态提交事物。如果事物状态被标记为回滚,则回滚事物
* @param status 当前事物对象
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚当前事物
* @param status 当前事物对象
*/
void rollback(TransactionStatus status) throws TransactionException;
3 事物管理器
spring 为不同的平台提供了不同的事物管理器。不同的事物管理器都继承自抽象类org.springframework.transaction.support.AbstractPlatformTransactionManager。该抽象类实现了org.springframework.jdbc.datasource.PlatformTransactionManager ,实现了事物管理器的骨架实现,通过继承该类,仅仅关心平台相关内容即可实现不同的事物管理器。
3.1 jdbc事物管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
jdbc 事物管理器是通过javax.sql.DataSource.getConnection() 获取javax.sql.Connection,通过javax.sql.Connection来进行事物的提交和回滚。
@Override
protected void doCommit(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Committing JDBC transaction on Connection [" + con + "]");
}
try {
con.commit();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not commit JDBC transaction", ex);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
if (status.isDebug()) {
logger.debug("Rolling back JDBC transaction on Connection [" + con + "]");
}
try {
con.rollback();
}
catch (SQLException ex) {
throw new TransactionSystemException("Could not roll back JDBC transaction", ex);
}
}
3.2 hibernate事物管理器
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
HibernateTransactionManager 是通过绑定org.hibernate.SessionFactory来实现的 ,通过sessionFactory来绑定org.hibernate.Transaction,提交或者回滚的时候调用org.hibernate.Transaction的commit或者rollback来操作。
@Override
protected void doCommit(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Committing Hibernate transaction on Session [" +
txObject.getSessionHolder().getSession() + "]");
}
try {
txObject.getSessionHolder().getTransaction().commit();
}
catch (org.hibernate.TransactionException ex) {
// assumably from commit call to the underlying JDBC connection
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
catch (HibernateException ex) {
// assumably failed to flush changes to database
throw convertHibernateAccessException(ex);
}
}
3.3 其它spring事物管理器
除了上面介绍的两个常用的事物管理器之外,spring还提供了其它的事物管理器。如 org.springframework.orm.jpa,org.springframework.transaction.jta.JtaTransactionManager,org.springframework.jms.connection.JmsTransactionManager,org.springframework.transaction.jta.WebLogicJtaTransactionManager等。感兴趣的可以自行查看,在idea中查看PlatformTransactionManager实现类如下
4 spring 事物的基本行为
通过org.springframework.transaction.getTransaction(TransactionDefinition definition)
/**
* 根据不同的事物行为,获取当前事物,或者新建事物。
* @param definition 事物定义信息,包含事物行为,隔离级别,超时时间等
* @return org.springframework.transaction.TransactionStatus 代表当前或者新的事物的事物对象
*/
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
可以得知,事物的相关属性就定义在java.sql.Connection.TransactionDefinition类中。那么我们看看该类中分别定义了什么
/**
* 获取传播行为
*/
int getPropagationBehavior();
/**
* 获取隔离级别
*/
int getIsolationLevel();
/**
* 获取超时时间
*/
int getTimeout();
/**
* 获取只读属性
*/
boolean isReadOnly();
/**
* 获取事物的名称 默认为classname+"."+methodName
*/
String getName();
4.1 事物的传播行为-propagation
所谓事物的传播行为,指的是被事物标记的方法(注解或者xml声明),之间进行嵌套调用时事物的传播规则。拿jdbc事物管理器来说,就是共用同一个jdbc connection,还是新建connection,还是抛异常。这个规则就叫做事物的传播行为。spring定义了如下传播行为:
传播行为 | 说明 |
---|---|
PROPAGATION_REQUIRED | *如果存在事物,则加入当前事物。如果不存在事物,则新建事物 * |
PROPAGATION_SUPPORTS | 支持事物,如果不存在事物则以非事物的状态运行 |
PROPAGATION_MANDATORY | 必须存在事物,不存在则抛异常 |
PROPAGATION_REQUIRES_NEW | 无论当前是否存在事物,都新建事物 |
PROPAGATION_NOT_SUPPORTED | 不支持事物,如当前存在事物则将当前事物挂起,以非事物的方式运行 |
PROPAGATION_NEVER | 不支持事物,如当前存在事物则抛异常 |
PROPAGATION_NESTED | 嵌套事物。如果当前不存在事物,则以PROPAGATION_REQUIRED的方式运行。如果存在事物则以嵌套事物的方式运行 |
4.1.1 PROPAGATION_REQUIRED
如果存在事物,则加入当前事物。如果不存在事物,则新建事物。默认传播行为。
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.REQUIRED)
void methodB(){
//doSomethingB()
}
methodB()被调用时不存在事物,则会新建一个事物。methodB 会以事物方式运行。相当于如下操作
void methodA(){
//doSomethingA()
Connection connection=null;
try{
connection=getConnection();
//doSomethingB()
connection.commit();
}catch (RuntimeException e){
connection.rollback();
}finally {
connection.close();
}
}
方法内调用之所以用类似 ((ServiceName)AopContext.currentProxy()).methodB()。是因为spring事物是基于aop实现,aop又是基于代理类实现。直接方法内调用是不会使用到增强类,也就不会调用到被代理类的增强方法。
4.1.2 PROPAGATION_SUPPORTS
支持事物,如果不存在事物则以非事物的状态运行。
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.SUPPORTS)
void methodB(){
//doSomethingB()
}
调用methodA时,相当于如下:
void methodA(){
Connection connection=null;
try{
connection=getConnection();
//doSomethingA()
//doSomethingB()
connection.commit();
}catch (RuntimeException e){
connection.rollback();
}finally {
connection.close();
}
}
当单独调用methodB 时,methodB 以非事物的方式运行。
4.1.3 PROPAGATION_MANDATORY
必须存在事物,不存在则抛异常。
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.MANDATORY)
void methodB(){
//doSomethingB()
}
无论时调用methodA,还是耽误调用methodB都会抛异常
// No existing transaction found -> check propagation behavior to find out how to proceed.
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
4.1.4 PROPAGATION_REQUIRES_NEW
无论当前是否存在事物,都新建事物。
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
((ServiceName)AopContext.currentProxy()).methodB();
//doSomethingA()
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
void methodB(){
//doSomethingB()
//successOrNot
}
当methodA中的doSomethingA()发生异常回滚时,methodB可以正常提交。值得注意的是,PROPAGATION_REQUIRES_NEW 仅仅支持JtaTransactionManager作为事物管理器。
/**
* Create a new transaction, suspending the current transaction if one exists.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available it to it (which is server-specific in standard Java EE).
* <p>A {@code PROPAGATION_REQUIRES_NEW} scope always defines its own
* transaction synchronizations. Existing synchronizations will be suspended
* and resumed appropriately.
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_REQUIRES_NEW = 3;
相当于如下:
void methodA(){
//当前连接
Connection currentConnection=null;
try{
currentConnection=getConnection();
//挂起当前事物
suspend(currentConnection);
Connection newConnection;
try {
//开启新事物
newConnection=getConnection();
//doSomethingB()
newConnection.commit();
}catch (Exception e){
newConnection.rollback(sa);
}finally {
newConnection.close();
}
//恢复当前事物
resume(currentConnection);
currentConnection.commit();
}catch (RuntimeException e){
currentConnection.rollback();
}finally {
currentConnection.close();
}
}
4.1.5 PROPAGATION_NOT_SUPPORTED
不支持事物,如果当前存在事物,则将当前事物挂起,已非事物的方式运行。同样,仅支持JtaTransactionManager做为事物管理器
。
/**
* Do not support a current transaction; rather always execute non-transactionally.
* Analogous to the EJB transaction attribute of the same name.
* <p><b>NOTE:</b> Actual transaction suspension will not work out-of-the-box
* on all transaction managers. This in particular applies to
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* which requires the {@code javax.transaction.TransactionManager} to be
* made available it to it (which is server-specific in standard Java EE).
* <p>Note that transaction synchronization is <i>not</i> available within a
* {@code PROPAGATION_NOT_SUPPORTED} scope. Existing synchronizations
* will be suspended and resumed appropriately.
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_NOT_SUPPORTED = 4;
4.1.6 PROPAGATION_NEVER
不支持事物,如果当前存在事物,则抛异常。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
4.1.7 PROPAGATION_NESTED
嵌套事物。如果当前存在事物,则以嵌套事物的方式运行
。如果不存在事物,则以PROPAGATION_REQUIRED
的方式运行。
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
//doSomethingA()
((ServiceName)AopContext.currentProxy()).methodB();
}
@Transactional(propagation = Propagation.NESTED)
void methodB(){
//doSomethingB()
//successOrNot
}
单独调用methodB 时,相当于 PROPAGATION_REQUIRED。
void method(){
Connection connection=null;
try{
connection=getConnection();
//doSomethingB()
connection.commit();
}catch (RuntimeException e){
connection.rollback();
}finally {
connection.close();
}
}
当调用methodA时,则以嵌套事物的方式运行,methodB作为methodA的子事物,提交和回滚都会受methodA的事物的影响
。
void methodA(){
Connection currentConnection=null;
Savepoint savepoint=null;
try{
currentConnection=getConnection();
//doSomethingA();
savepoint=currentConnection.setSavepoint(createSavePoint());
try{
//doSomethingB();
}catch (RuntimeException e){
currentConnection.rollback(savepoint);
}finally {
currentConnection.close();
}
currentConnection.commit();
}catch (RuntimeException e){
currentConnection.rollback();
}finally {
currentConnection.close();
}
}
值得注意的一点是,嵌套事物只支持DataSourceTransactionManager,或者部分JTA事物管理器。且要求jdbc 3.0驱动及以上,同时 jdk版本在1.4 以上
/**
* Execute within a nested transaction if a current transaction exists,
* behave like {@link #PROPAGATION_REQUIRED} else. There is no analogous
* feature in EJB.
* <p><b>NOTE:</b> Actual creation of a nested transaction will only work on
* specific transaction managers. Out of the box, this only applies to the JDBC
* {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* when working on a JDBC 3.0 driver. Some JTA providers might support
* nested transactions as well.
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
int PROPAGATION_NESTED = 6;
/**
* Creates a savepoint with the given name in the current transaction
* and returns the new <code>Savepoint</code> object that represents it.
*
* <p> if setSavepoint is invoked outside of an active transaction, a transaction will be started at this newly created
*savepoint.
*
* @param name a <code>String</code> containing the name of the savepoint
* @return the new <code>Savepoint</code> object
* @exception SQLException if a database access error occurs,
* this method is called while participating in a distributed transaction,
* this method is called on a closed connection
* or this <code>Connection</code> object is currently in
* auto-commit mode
* @exception SQLFeatureNotSupportedException if the JDBC driver does not support
* this method
* @see Savepoint
* @since 1.4
*/
Savepoint setSavepoint(String name) throws SQLException;
一般我们用PROPAGATION_NESTED来执行分支逻辑
。
@Transactional(propagation = Propagation.REQUIRED)
void methodA(){
try{
((ServiceName)AopContext.currentProxy()).methodB();
}catch (Exception e){
//doSomethingC();
}
}
@Transactional(propagation = Propagation.NESTED)
void methodB(){
//doSomethingB()
//successOrNot
}
当methodB执行失败的时候,则methodA 回滚到之前的保存点,然后执行catch块中的其它业务逻辑,就像methodB从未执行过一样。这也是PROPAGATION_NESTED最常用的用法,而PROPAGATION_REQUIRED和PROPAGATION_NEW都无法做到这一点。
大多数情况下,PROPAGATION_REQUIRED可以满足我们的绝大部分需求,所以它也应该是我们优先考虑的传播行为,同时其也是@Transactional 的默认传播行为
4.2 事物的隔离级别
事物的隔离级别,指的是并发情况下,事物之间的隔离度。不同的隔离级别,可以解决不同的隔离问题,也对应着不同的并发度,使用的时候,要结合实际情况,按需选择。
4.2.1 数据库的隔离问题及级别
4.2.1.1 隔离问题
问题 | 描述 |
---|---|
脏读 | 事物A读到了事物B未提交的数据,如果事物B的操作被回滚了,那么事物A读到的就是无效数据。 |
不可重复读 | 事物A执行select 查询到结果集R1,此时事物B执行update 并提交,事物A再执行同样的select 查询到结果集R2。两次查询到的结果集不一致。 |
幻读 | 事物A执行select 查询到结果集R1,此事事物B执行了 insert 或者 delete 操作 并提交,事物A再执行同样的select 查询到结果集R2。此时发现R2相比R1多了或者少了一些记录。 |
4.2.1.2 隔离级别
级别 | 描述 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|---|
Read Uncommitted | 读未提交 | × |
× |
× |
Read Committed | 读已提交,oracle 默认隔离级别,利用快照读解决脏读
|
√ |
× |
× |
Repeated Read | 重复读,mysql 默认隔离级别,除了读快照之外,事物启动后不允许执行update操作
|
√ |
√ |
× |
Serializable | 串行化,事物串行执行,效率最差,安全性也最高
|
√ |
√ |
√ |
4.2.2 Spirng 的事物隔离级别-isolation
spring的事物隔离级别也是依赖于底层的数据库隔离级别
级别 | 描述 |
---|---|
ISOLATION_DEFAULT | 使用数据库隔离级别。默认隔离级别
|
ISOLATION_READ_UNCOMMITTED | 读未提交 |
ISOLATION_READ_COMMITTED | 读已提交 |
ISOLATION_REPEATABLE_READ | 可重复读 |
ISOLATION_SERIALIZABLE | 串行化 |
以DataSourceTransactionManager为例,是通过设置Connection的TransactionIsolation属性来实现绑定不同的隔离级别的。
// Apply specific isolation level, if any.
Integer previousIsolationLevel = null;
if (definition != null && definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
if (logger.isDebugEnabled()) {
logger.debug("Changing isolation level of JDBC Connection [" + con + "] to " +
definition.getIsolationLevel());
}
int currentIsolation = con.getTransactionIsolation();
if (currentIsolation != definition.getIsolationLevel()) {
previousIsolationLevel = currentIsolation;
con.setTransactionIsolation(definition.getIsolationLevel());
}
}
4.3 事物的只读属性-readOnly
spring 事物的只读属性是通过设置,java.sql.Connection 的 readOnly 属性来实现的。当设置了只读属性后,数据库会对只读事物进行一些优化
,如不启用回滚段,不启用回滚日志等。同时值得注意的是,设置了只读属性后,如果进行写操作会报错。
// Set read-only flag.
if (definition != null && definition.isReadOnly()) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Setting JDBC Connection [" + con + "] read-only");
}
con.setReadOnly(true);
}
4.4 事物的超时时间-timeout
事物的超时指的是设置一个时间,当执行时间超过这个时间后,抛异常并回滚。是通过设置一个截至时间来实现的,值得注意的是,这个超时时间只有特定情况下才会生效,如dao层使用jdbcTemplete 执行sql
设置超时时间
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
/**
* Set the timeout for this object in seconds.
* @param seconds number of seconds until expiration
*/
public void setTimeoutInSeconds(int seconds) {
setTimeoutInMillis(seconds * 1000L);
}
/**
* Set the timeout for this object in milliseconds.
* @param millis number of milliseconds until expiration
*/
public void setTimeoutInMillis(long millis) {
this.deadline = new Date(System.currentTimeMillis() + millis);
}
检查超时时间
/**
* Return the time to live for this object in seconds.
* Rounds up eagerly, e.g. 9.00001 still to 10.
* @return number of seconds until expiration
* @throws TransactionTimedOutException if the deadline has already been reached
*/
public int getTimeToLiveInSeconds() {
double diff = ((double) getTimeToLiveInMillis()) / 1000;
int secs = (int) Math.ceil(diff);
checkTransactionTimeout(secs <= 0);
return secs;
}
/**
* Set the transaction rollback-only if the deadline has been reached,
* and throw a TransactionTimedOutException.
*/
private void checkTransactionTimeout(boolean deadlineReached) throws TransactionTimedOutException {
if (deadlineReached) {
setRollbackOnly();
throw new TransactionTimedOutException("Transaction timed out: deadline was " + this.deadline);
}
}
4.5 回滚规则-rollbackFor
回滚规则只得是spring 事物遇到哪种异常会回滚。默认只回滚RuntimeException和Error
源码如下
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
ex2.initApplicationException(ex);
throw ex2;
}
catch (RuntimeException ex2) {
logger.error("Application exception overridden by rollback exception", ex);
throw ex2;
}
catch (Error err) {
logger.error("Application exception overridden by rollback error", ex);
throw err;
}
}
@Override
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
当配置了rollbackFor 属性时,源码如下
@Override
public boolean rollbackOn(Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Applying rules to determine whether transaction should rollback on " + ex);
}
RollbackRuleAttribute winner = null;
int deepest = Integer.MAX_VALUE;
if (this.rollbackRules != null) {
for (RollbackRuleAttribute rule : this.rollbackRules) {
int depth = rule.getDepth(ex);
if (depth >= 0 && depth < deepest) {
deepest = depth;
winner = rule;
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Winning rollback rule is: " + winner);
}
// User superclass behavior (rollback on unchecked) if no rule matches.
if (winner == null) {
logger.trace("No relevant rollback rule found: applying default rules");
return super.rollbackOn(ex);
}
return !(winner instanceof NoRollbackRuleAttribute);
}
public int getDepth(Throwable ex) {
return getDepth(ex.getClass(), 0);
}
private int getDepth(Class<?> exceptionClass, int depth) {
if (exceptionClass.getName().contains(this.exceptionName)) {
// Found it!
return depth;
}
// If we've gone as far as we can go and haven't found it...
if (exceptionClass == Throwable.class) {
return -1;
}
return getDepth(exceptionClass.getSuperclass(), depth + 1);
}
可以看到,此时回滚的规则为当前异常类型在配置的异常类型里面
。
4.6 其它属性
类似4.5的属性还有rollbackForClassName,noRollbackFor,noRollbackForClassName 。都是类似的作用,可以自行查看。
总结
任何场景下都应该根据合适的场景选择合适的方式,知其然还要只其所以然,才能在出先问题的时候,快读定位排查。后续我们将分析下spring的事物管理的源码,一起看下spring的事物究竟是怎么生效的。
参考文章
//www.greatytc.com
Spring 源码深度解析-郝佳