当前位置: 首页>编程语言>正文

javaspring 事务何时生效 spring事务实现过程

说起Spring的事务,笔者想起来刚开始接触java的时候,自己做的都是啥,举几个例子。

第一阶段:jsp+servlet进行学生管理系统的增删改查。具体一点就是新增一个学生,删除一个学生,修改一个学生的信息。

第二阶段:进行复杂一点的功能实现,比如分页展示等,初步尝试了list和map集合。

第三阶段:当实现选课功能时,突然意识到会存在超出人数能选择到的情况,使用了简单的事务控制,此时还是使用的Service代码里手动进行事务的开启和关闭。

第四阶段:接触到框架了,Spring,感叹接触的时间太晚了,这么方便,不过也正是因为自己以前手动实现了从0开始的一个过程,能够很快的理解Spring在帮我们做什么,特别是事务的地方,不必自己手动在每个方法开启,每个方法去关闭,AOP的优势就此体现。

对于Spring的事务,一直在用,但是一直没有总结,因此有必要总结一下,加深一下自己的理解。

1. Spring的配置文件

<xsd:enumeration value="REQUIRED"/>
<xsd:enumeration value="SUPPORTS"/>
<xsd:enumeration value="MANDATORY"/>
<xsd:enumeration value="REQUIRES_NEW"/>
<xsd:enumeration value="NOT_SUPPORTED"/>
<xsd:enumeration value="NEVER"/>
<xsd:enumeration value="NESTED"/>

Spring的源码解释:  

/**
	 * Support a current transaction; create a new one if none exists.
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p>This is typically the default setting of a transaction definition,
	 * and typically defines a transaction synchronization scope.
	 */
	int PROPAGATION_REQUIRED = 0;

	/**
	 * Support a current transaction; execute non-transactionally if none exists.
	 * Analogous to the EJB transaction attribute of the same name.
	 * <p><b>NOTE:</b> For transaction managers with transaction synchronization,
	 * {@code PROPAGATION_SUPPORTS} is slightly different from no transaction
	 * at all, as it defines a transaction scope that synchronization might apply to.
	 * As a consequence, the same resources (a JDBC {@code Connection}, a
	 * Hibernate {@code Session}, etc) will be shared for the entire specified
	 * scope. Note that the exact behavior depends on the actual synchronization
	 * configuration of the transaction manager!
	 * <p>In general, use {@code PROPAGATION_SUPPORTS} with care! In particular, do
	 * not rely on {@code PROPAGATION_REQUIRED} or {@code PROPAGATION_REQUIRES_NEW}
	 * <i>within</i> a {@code PROPAGATION_SUPPORTS} scope (which may lead to
	 * synchronization conflicts at runtime). If such nesting is unavoidable, make sure
	 * to configure your transaction manager appropriately (typically switching to
	 * "synchronization on actual transaction").
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#SYNCHRONIZATION_ON_ACTUAL_TRANSACTION
	 */
	int PROPAGATION_SUPPORTS = 1;

	/**
	 * Support a current transaction; throw an exception if no current transaction
	 * exists. Analogous to the EJB transaction attribute of the same name.
	 * <p>Note that transaction synchronization within a {@code PROPAGATION_MANDATORY}
	 * scope will always be driven by the surrounding transaction.
	 */
	int PROPAGATION_MANDATORY = 2;

	/**
	 * 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 J2EE).
	 * <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;

	/**
	 * 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 J2EE).
	 * <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;

	/**
	 * Do not support a current transaction; throw an exception if a current transaction
	 * exists. Analogous to the EJB transaction attribute of the same name.
	 * <p>Note that transaction synchronization is <i>not</i> available within a
	 * {@code PROPAGATION_NEVER} scope.
	 */
	int PROPAGATION_NEVER = 5;

	/**
	 * 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;


PROPAGATION_REQUIRED

-- 支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。 

PROPAGATION_SUPPORTS -- 支持当前事务,如果当前没有事务,就以非事务方式执行。 
PROPAGATION_MANDATORY -- 支持当前事务,如果当前没有事务,就抛出异常。 
PROPAGATION_REQUIRES_NEW -- 新建事务,如果当前存在事务,把当前事务挂起。 
PROPAGATION_NOT_SUPPORTED -- 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 
PROPAGATION_NEVER -- 以非事务方式执行,如果当前存在事务,则抛出异常。 
PROPAGATION_NESTED

总结一下:最常用的其实就是REQUIRED,这也是一般项目足够用了,要注意它的解释,支持当前事务,也即是说如果执行到某个方法是配置的此选项,那么它就会挂在当前事务下,如果之前都不存在任何事务,那么它新建。这也是我们常说的事务是传递的。

来看一个最简单的业务代码实现:

1.PROPAGATION_REQUIRED 一般在ServiceA中肯定会调用ServiceB的方法吧,那么此时methodA配置了事务,那么methodB失败了,会全部回滚掉,因为事务是传递的,上面解释的很清楚,笔者面试过一些人,他们认为service不应该调用serivce,而是应该调用dao层,很纳闷,为什么不能相互调用呢?

/*事务属性配置为 PROPAGATION_REQUIRED*/
        ServiceA {

            void methodA () {
                // ServiceA调用 ServiceB 的方法
                ServiceB.methodB();
            }
        }

        ServiceB{
            void methodB () {
                serviceB的方法
            }
        }


2. PROPAGATION_REQUIRES_NEW

 挑几个说一下,关于这个REQUIRED_NEW,具体来说,它的用途就是不管外部有没有事务,它都会

新起一个内部事务,它新起的这个内部事务和外部事务无关,这个内部事务是否成功失败,与外部事务没有任何关系。当内部事务结束时,外部事务才继续进行。

以下例子都是实际测试过,但是只给出简单的伪代码。

例子1:

同一个TestService中存在两个方法 testA 和testB,testA 配置REQUIRED,testB配置REQUIRED_NEW。
methodA(){
	methodB();
	throw new Exception("testA失败回滚");	
}

methodB回滚了,受到了methodA的异常影响。

例子2:

同一个TestService中存在两个方法 testA 和testB,testA 配置REQUIRED,testB配置REQUIRED_NEW。
methodA(){
	try{
		...A
		....A
		methodB();
	}catch(Exception e){
		//1
	}	
}
methodB(){
	...B
	....B
	throw new Exception("methodB失败回滚");	
}

此例是methodB抛出异常回滚了,在methodA中的注释1能够catch住异常,但是即使你在此处抛出异常,methodA依然能够成功提交,也就是不受methodB的影响。

例子3:

不同Service中存在两个方法 testA 和testB,testA 配置REQUIRED,testB配置REQUIRED_NEW。
ServiceA.methodA(){
	ServiceB.methodB();
	throw new Exception("testA失败回滚");	
}

此例中methodB是真正的不受methodA方法的影响,做到了新起事务。

总结例子1,例子2:

针对REQUIRED_NEW

如果同一个Service内两个方法的内层方法配置了REQUIRED_NEW,不会新起事务,只在当前事务下,所以任何异常都会造成一起回滚,但是如果在外层捕捉此异常,那么外层方法能够正常提交。

如果是不同Service中的两个方法,就会新起事务,相互不影响。

3. PROPAGATION_REQUIRES_NESTED

NESTED是真正的嵌套事务,它与外部事务息息相关,当内部事务开启时,会取得一个savepoint,如果嵌套事务失败,那么会回滚到此savepoint点,它属于外部事务的一部分,在外部事务结束时它才能被提交。

例子1:

<tx:method name="testA" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="testB" propagation="NESTED" rollback-for="Throwable"/>
<tx:method name="testC" propagation="REQUIRED" rollback-for="Throwable"/>
methodA(){
	try{
		...A
		....A
		methodB(); //1
		....A  //2
	}catch(Exception e){
		methodC();
	}	
}
methodB(){
	...B
	....B
	throw new Exception("methodB失败回滚");	
}
methodC(){
	...C
	....C
	throw new Exception("methodC失败回滚");	
}

这个应该是最常见的运用了,methodB采用的是NESTED配置,因此当抛出异常时,会被methodA捕捉,也就是回到savepoint点1,但是下面的2就不会执行了,但是能够执行methodC的,有点分支的意味。

PROPAGATION_REQUIRES_NEW 启动一个新的, 不依赖于环境的 "内部" 事务. 这个事务将被完全 commited 或 rolled back 而不依赖于外部事务, 它拥有自己的隔离范围, 自己的锁, 等等. 当内部事务开始执行时, 外部事务将被挂起, 内务事务结束时, 外部事务将继续执行. 

另一方面, PROPAGATION_NESTED 开始一个 "嵌套的" 事务,  它是已经存在事务的一个真正的子事务. 潜套事务开始执行时,  它将取得一个 savepoint. 如果这个嵌套事务失败, 我们将回滚到此 savepoint. 潜套事务是外部事务的一部分, 只有外部事务结束后它才会被提交. 

因此,PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大区别在于, PROPAGATION_REQUIRES_NEW 完全是一个新的事务, 而 PROPAGATION_NESTED 则是外部事务的子事务, 如果外部事务 commit, 潜套事务也会被 commit, 这个规则同样适用于 roll back.

3. PROPAGATION_NOT_SUPPORTED

关于这个not_supported,是以非事务的方式运行,别忘了,一般项目配置时,会配置save,update,modify等开头的方法以事务方式运行,其他方法会以只读事务进行,但是通常这个只读事务会出现一点点问题。

此例是methodB抛出异常回滚了,在methodA中的注释1能够catch住异常,但是即使你在此处抛出异常,methodA依然能够成功提交,也就是不受methodB的影响。


https://www.xamrdz.com/lan/5e91934599.html

相关文章: