`

Spring事务的传播行为和隔离级别

 
阅读更多

个传播行为,4个隔离级别,

Spring事务的传播行为和隔离级别

[ transaction behavior and isolated level ]

事务的传播行为和隔离级别

[ transaction behavior and isolated level ]

Spring中事务的定义:
一、Propagation :
  key属性确定代理应该给哪个方法增加事务行为。这样的属性最重要的部份是传播行为。有以下选项可供使用:
PROPAGATION_REQUIRED

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

- - 支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY

- - 支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW

- - 新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED

- - 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER

- - 以非事务方式执行,如果当前存在事务,则抛出异常。

很多人看到事务的传播行为属性都不甚了解,我昨晚看了j2ee without ejb的时候,看到这里也不了解,甚至重新翻起数据库系统的教材书,但是也没有找到对这个的分析。今天搜索,找到一篇极好的分析文章,虽然这篇文章是重点分析PROPAGATION_REQUIRED 和 PROPAGATION_REQUIRED_NESTED的

解惑 spring 嵌套事务
/**
* @date 2006-11-24
* @note 转载自http://www.javaeye.com/topic/35907?page=1
*/
* * * * * * * * TransactionDefinition 接口定义* * * * * * * * * * * * * * * * * * *
/**
* Support a current transaction, create a new one if none exists.
* Analogous to EJB transaction attribute of the same name.
*
This is typically the default setting of a transaction definition.
*/
int PROPAGATION_REQUIRED = 0;

/**
* Support a current transaction, execute non-transactionally if none exists.
* Analogous to EJB transaction attribute of the same name.
*

Note: For transaction managers with transaction synchronization,
* PROPAGATION_SUPPORTS is slightly different from no transaction at all,
* as it defines a transaction scopp that synchronization will apply for.
* As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
* will be shared for the entire specified scope. Note that this depends on
* the actual synchronization configuration of the transaction manager.
* @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
*/
int PROPAGATION_SUPPORTS = 1;

/**
* Support a current transaction, throw an exception if none exists.
* Analogous to EJB transaction attribute of the same name.
*/
int PROPAGATION_MANDATORY = 2;

/**
* Create a new transaction, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
*

Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the javax.transaction.TransactionManager to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_REQUIRES_NEW = 3;

/**
* Execute non-transactionally, suspend the current transaction if one exists.
* Analogous to EJB transaction attribute of the same name.
*

Note: Actual transaction suspension will not work on out-of-the-box
* on all transaction managers. This in particular applies to JtaTransactionManager,
* which requires the javax.transaction.TransactionManager to be
* made available it to it (which is server-specific in standard J2EE).
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
int PROPAGATION_NOT_SUPPORTED = 4;

/**
* Execute non-transactionally, throw an exception if a transaction exists.
* Analogous to EJB transaction attribute of the same name.
*/
int PROPAGATION_NEVER = 5;

/**
* Execute within a nested transaction if a current transaction exists,
* behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
*

Note: Actual creation of a nested transaction will only work on specific
* transaction managers. Out of the box, this only applies to the JDBC
* 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;

 

在这篇文章里,他用两个嵌套的例子辅助分析,我这里直接引用了。

ServiceA {

/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
@Transactional(propagation=Propagation.REQUIRED)
void methodA() {
ServiceB.methodB();
}

}

ServiceB {

/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
@Transactional(propagation=Propagation.REQUIRED)
void methodB() {
}

}


 

1: PROPAGATION_REQUIRED
加入当前正要执行的事务不在另外一个事务里,那么就起一个新的事务
比如说,ServiceB. methodB的事务级别定义为PROPAGATION_REQUIRED, 那么由于执行ServiceA. methodA的时候,
ServiceA. methodA已经起了事务,这时调用ServiceB. methodB,ServiceB. methodB看到自己已经运行在ServiceA. methodA
的事务内部,就不再起新的事务。而假如ServiceA. methodA运行的时候发现自己没有在事务中,他就会为自己分配一个事务。
这样,在ServiceA. methodA或者在ServiceB. methodB内的任何地方出现异常,事务都会被回滚。即使ServiceB. methodB的事务已经被
提交,但是ServiceA. methodA在接下来fail要回滚,ServiceB. methodB也要回滚

2: PROPAGATION_SUPPORTS
如果当前在事务中,即以事务的形式运行,如果当前不再一个事务中,那么就以非事务的形式运行


3: PROPAGATION_MANDATORY
必须在一个事务中运行。也就是说,他只能被一个父事务调用。否则,他就要抛出异常

4: PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比如我们设计ServiceA. methodA的事务级别为PROPAGATION_REQUIRED,ServiceB. methodB的事务级别为PROPAGATION_REQUIRES_NEW,
那么当执行到ServiceB. methodB的时候,ServiceA. methodA所在的事务就会挂起,ServiceB. methodB会起一个新的事务,等待ServiceB. methodB的事务完成以后,
他才继续执行。他与PROPAGATION_REQUIRED 的事务区别在于事务的回滚程度了。因为ServiceB. methodB是新起一个事务,那么就是存在
两个不同的事务。如果ServiceB. methodB已经提交,那么ServiceA. methodA失败回滚,ServiceB. methodB是不会回滚的。如果ServiceB. methodB失败回滚,
如果他抛出的异常被ServiceA. methodA捕获,ServiceA. methodA事务仍然可能提交。

5: PROPAGATION_NOT_SUPPORTED
当前不支持事务。比如ServiceA. methodA的事务级别是PROPAGATION_REQUIRED ,而ServiceB. methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,
那么当执行到ServiceB. methodB时,ServiceA. methodA的事务挂起,而他以非事务的状态运行完,再继续ServiceA. methodA的事务。

6: PROPAGATION_NEVER
不能在事务中运行。假设ServiceA. methodA的事务级别是PROPAGATION_REQUIRED, 而ServiceB. methodB的事务级别是PROPAGATION_NEVER ,
那么ServiceB. methodB就要抛出异常了。

7: PROPAGATION_NESTED
理解Nested的关键是savepoint。他与PROPAGATION_REQUIRES_NEW的区别是,PROPAGATION_REQUIRES_NEW另起一个事务,将会与他的父事务相互独立,
而Nested的事务和他的父事务是相依的,他的提交是要等和他的父事务一块提交的。也就是说,如果父事务最后回滚,他也要回滚的。
而Nested事务的好处是他有一个savepoint。

ServiceA {

/**
* 事务属性配置为 PROPAGATION_REQUIRED
*/
@Transactional(propagation=Propagation.REQUIRED)
void methodA() {
try {
//savepoint
@Transactional(propagation=Propagation.NESTED)
ServiceB.methodB(); //PROPAGATION_NESTED 级别

} catch (SomeException) {
// 执行其他业务, 如 ServiceC.methodC();

}
}

}


 

也就是说ServiceB. methodB失败回滚,那么ServiceA. methodA也会回滚到savepoint点上,ServiceA. methodA可以选择另外一个分支,比如
ServiceC. methodC,继续执行,来尝试完成自己的事务。
但是这个事务并没有在EJB标准中定义。

二、Isolation Level ( 事务隔离等级) :
1、Serializable:最严格的级别,事务串行执行,资源消耗最大;
2、REPEATABLE READ:保证了一个事务不会修改已经由另一个事务读取但未提交(回滚)的数据。避免了“脏读取”和“不可重复读取”的情况,但是带来了更多的性能损失。
3、READ COMMITTED: 大多数主流数据库的默认事务等级,保证了一个事务不会读到另一个并行事务已修改但未提交的数据,避免了“脏读取”。该级别适用于大多数系统。
4、Read Uncommitted:保证了读取过程中不会读取到非法数据。隔离级别在于处理多事务的并发问题。
我们知道并行可以提高数据库的吞吐量和效率,但是并不是所有的并发事务都可以并发运行,这需要查看数据库教材的可串行化条件判断了。
这里就不阐述。
我们首先说并发中可能发生的3中不讨人喜欢的事情
1: Dirty reads

- - 读脏数据。也就是说,比如事务A的未提交(还依然缓存)的数据被事务B读走,如果事务A失败回滚,会导致事务B所读取的的数据是错误的。
2: non- repeatable reads

- - 数据不可重复读。比如事务A中两处读取数据- total- 的值。在第一读的时候,total是100,然后事务B就把total的数据改成200,事务A再读一次,结果就发现,total竟然就变成200了,造成事务A数据混乱。
3: phantom reads

- - 幻象读数据,这个和non- repeatable reads相似,也是同一个事务中多次读不一致的问题。但是non- repeatable reads的不一致是因为他所要取的数据集被改变了(比如total的数据),但是phantom reads所要读的数据的不一致却不是他所要读的数据集改变,而是他的条件数据集改变。比如Select account. id where account. name = "ppgogo*" , 第一次读去了6个符合条件的id,第二次读取的时候,由于事务b把一个帐号的名字由"dd" 改成"ppgogo1" ,结果取出来了7个数据。 Dirty reads non- repeatable reads phantom reads
Serializable 不会 不会 不会
REPEATABLE READ 不会 不会 会
READ COMMITTED 不会 会 会
Read Uncommitted 会 会 会

三、readOnly
事务属性中的readOnly标志表示对应的事务应该被最优化为只读事务。

这是一个最优化提示。在一些情况下,一些事务策略能够起到显著的最优化效果,例如在使用Object/ Relational映射工具(如:Hibernate或TopLink)时避免dirty checking(试图“刷新”)。

四、Timeout

在事务属性中还有定义“timeout”值的选项,指定事务超时为几秒。在JTA中,这将被简单地传递到J2EE服务器的事务协调程序,并据此得到相应的解释。

转载自:http://blog.chinaunix.net/u1/55983/showart_2091761.html


附:@interface Transactional

/*
 * Copyright 2002-2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.transaction.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.transaction.TransactionDefinition;

/**
 * Describes transaction attributes on a method or class.
 *
 * <p>This annotation type is generally directly comparable to Spring's
 * {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
 * class, and in fact {@link AnnotationTransactionAttributeSource} will directly
 * convert the data to the latter class, so that Spring's transaction support code
 * does not have to know about annotations. If no rules are relevant to the exception,
 * it will be treated like
 * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute}
 * (rolling back on runtime exceptions).
 *
 * <p>For specific information about the semantics of this annotation's attributes,
 * consider the {@link org.springframework.transaction.TransactionDefinition} and
 * {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
 *
 * @author Colin Sampaleanu
 * @author Juergen Hoeller
 * @since 1.2
 * @see org.springframework.transaction.interceptor.TransactionAttribute
 * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute
 * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute
 */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	/**
	 * A qualifier value for the specified transaction.
	 * <p>May be used to determine the target transaction manager,
	 * matching the qualifier value (or the bean name) of a specific
	 * {@link org.springframework.transaction.PlatformTransactionManager}
	 * bean definition.
	 */
	String value() default "";

	/**
	 * The transaction propagation type.
	 * Defaults to {@link Propagation#REQUIRED}.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior()
	 */
	Propagation propagation() default Propagation.REQUIRED;

	/**
	 * The transaction isolation level.
	 * Defaults to {@link Isolation#DEFAULT}.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel()
	 */
	Isolation isolation() default Isolation.DEFAULT;

	/**
	 * The timeout for this transaction.
	 * Defaults to the default timeout of the underlying transaction system.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout()
	 */
	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	/**
	 * <code>true</code> if the transaction is read-only.
	 * Defaults to <code>false</code>.
	 * <p>This just serves as a hint for the actual transaction subsystem;
	 * it will <i>not necessarily</i> cause failure of write access attempts.
	 * A transaction manager which cannot interpret the read-only hint will
	 * <i>not</i> throw an exception when asked for a read-only transaction.
	 * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly()
	 */
	boolean readOnly() default false;

	/**
	 * Defines zero (0) or more exception {@link Class classes}, which must be a
	 * subclass of {@link Throwable}, indicating which exception types must cause
	 * a transaction rollback.
	 * <p>This is the preferred way to construct a rollback rule, matching the
	 * exception class and subclasses.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}
	 */
	Class<? extends Throwable>[] rollbackFor() default {};

	/**
	 * Defines zero (0) or more exception names (for exceptions which must be a
	 * subclass of {@link Throwable}), indicating which exception types must cause
	 * a transaction rollback.
	 * <p>This can be a substring, with no wildcard support at present.
	 * A value of "ServletException" would match
	 * {@link javax.servlet.ServletException} and subclasses, for example.
	 * <p><b>NB: </b>Consider carefully how specific the pattern is, and whether
	 * to include package information (which isn't mandatory). For example,
	 * "Exception" will match nearly anything, and will probably hide other rules.
	 * "java.lang.Exception" would be correct if "Exception" was meant to define
	 * a rule for all checked exceptions. With more unusual {@link Exception}
	 * names such as "BaseBusinessException" there is no need to use a FQN.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}
	 */
	String[] rollbackForClassName() default {};

	/**
	 * Defines zero (0) or more exception {@link Class Classes}, which must be a
	 * subclass of {@link Throwable}, indicating which exception types must <b>not</b>
	 * cause a transaction rollback.
	 * <p>This is the preferred way to construct a rollback rule, matching the
	 * exception class and subclasses.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}
	 */
	Class<? extends Throwable>[] noRollbackFor() default {};

	/**
	 * Defines zero (0) or more exception names (for exceptions which must be a
	 * subclass of {@link Throwable}) indicating which exception types must <b>not</b>
	 * cause a transaction rollback.
	 * <p>See the description of {@link #rollbackForClassName()} for more info on how
	 * the specified names are treated.
	 * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}
	 */
	String[] noRollbackForClassName() default {};

}



 enum Propagation:

/*
 * Copyright 2002-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.transaction.annotation;

import org.springframework.transaction.TransactionDefinition; 

/**
 * Enumeration that represents transaction propagation behaviors for use
 * with the {@link Transactional} annotation, corresponding to the
 * {@link TransactionDefinition} interface.
 *
 * @author Colin Sampaleanu
 * @author Juergen Hoeller
 * @since 1.2
 */
public enum Propagation {
	
	/**
	 * Support a current transaction, create a new one if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>This is the default setting of a transaction annotation.
	 */
	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	/**
	 * Support a current transaction, execute non-transactionally if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: For transaction managers with transaction synchronization,
	 * PROPAGATION_SUPPORTS is slightly different from no transaction at all,
	 * as it defines a transaction scope that synchronization will apply for.
	 * As a consequence, the same resources (JDBC Connection, Hibernate Session, etc)
	 * will be shared for the entire specified scope. Note that this depends on
	 * the actual synchronization configuration of the transaction manager.
	 * @see org.springframework.transaction.support.AbstractPlatformTransactionManager#setTransactionSynchronization
	 */
	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	/**
	 * Support a current transaction, throw an exception if none exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	/**
	 * Create a new transaction, suspend the current transaction if one exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: Actual transaction suspension will not work on out-of-the-box
	 * on all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the <code>javax.transaction.TransactionManager</code> to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	/**
	 * Execute non-transactionally, suspend the current transaction if one exists.
	 * Analogous to EJB transaction attribute of the same name.
	 * <p>Note: Actual transaction suspension will not work on out-of-the-box
	 * on all transaction managers. This in particular applies to JtaTransactionManager,
	 * which requires the <code>javax.transaction.TransactionManager</code> to be
	 * made available it to it (which is server-specific in standard J2EE).
	 * @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
	 */
	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	/**
	 * Execute non-transactionally, throw an exception if a transaction exists.
	 * Analogous to EJB transaction attribute of the same name.
	 */
	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	/**
	 * Execute within a nested transaction if a current transaction exists,
	 * behave like PROPAGATION_REQUIRED else. There is no analogous feature in EJB.
	 * <p>Note: Actual creation of a nested transaction will only work on specific
	 * transaction managers. Out of the box, this only applies to the JDBC
	 * DataSourceTransactionManager when working on a JDBC 3.0 driver.
	 * Some JTA providers might support nested transactions as well.
	 * @see org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	NESTED(TransactionDefinition.PROPAGATION_NESTED);


	private final int value;


	Propagation(int value) { this.value = value; }
	
	public int value() { return this.value; }
	
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics