Spring 事务

Spring 事务

1.何为事务:

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。就是把一系列的操作当成原子性执行。

2.事务四个属性ACID

1、原子性(atomicity)

事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用。

2、一致性(consistency)

一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中。

3、隔离性(isolation)

可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏。

4、持久性(durability)

事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中。

@Transactional的使用

3.Spring 中的事务管理

Spring 事务管理分为编程式事务和声明式:

3.1 编程式事务

通过编写代码的方式实现事务,将事务管理代码嵌到业务方法中来控制事务的提交和回滚,编程式是在程序中显式地使用TransactionTemplate来控制事务的开启和提交。(使用较少,可以直接查看TransactionTemplate源代码)

**缺点:**必须在每个事务操作业务逻辑中包含额外的事务管理代码。

3.2 声明式事务

基于 AOP,将业务逻辑与事务处理进行解耦。在方法前开启一个事务,在方法执行后进行commit,中间进行一些异常的判断和处理。AOP的方式对代码没有侵入性,在实际使用中声明式事务用的比较多。

声明式事务有两种方式,一种是在配置文件中进行,另一种是基于@Transactional 注解的方式。

使用@Transactional的相比传统的手动开启事务提交事务来说。它的优点是:

  • 根据你的配置,设置是否自动开启事务
  • 自动提交事务或者遇到异常自动回滚

4、声明式管理事务

4.1通知声明式地管理事务

	<!-- 1. 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
        
	<!-- 2. 配置事务属性 -->
	<!--<tx:advice>元素声明事务通知-->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<!-- 根据方法名指定事务的属性 -->
			<tx:method name="*"/>
			<!--propagation配置事务传播行为-->
			<tx:method name="purchase" propagation="REQUIRES_NEW"/>
			<!--isolation配置事务的隔离级别-->
			<tx:method name="update*" isolation="SERIALIZABLE"/>
			<!--rollback-for配置事务遇到异常必须回滚,no-rollback-for配置事务遇到异常必须不能回滚-->
			<tx:method name="add*" rollback-for="java.io.IOException" no-rollback-for="com.dmsd.spring.tx.BookStockException"/>
			<!--read-only配置事务只读属性-->
			<tx:method name="find*" read-only="true"/>
			<!--timeout配置事务的超时属性-->
			<tx:method name="get*" timeout="3"/>
		</tx:attributes>
	</tx:advice>
	
	<!-- 3. 配置事务切入点, 以及把事务切入点和事务属性关联起来 -->
	<aop:config>
		<aop:pointcut expression="execution(* com.atguigu.spring.tx.xml.service.*.*(..))" 
			id="txPointCut"/>
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>	
	</aop:config>

4.2用 @Transactional 注解声明式地管理事务

Spring中注解的方式@Transactional标注事务方法。为了将方法定义为支持事务处理,可以在方法上添加@Transactional注解。根据Spring AOP基于代理机制,只能标注公有方法。如果在类上标注@Transactional注解,那么这个类中所有公有方法都会被定义为支持事务。

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" 
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 启用事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>

在方法上添加

	//添加事务注解
	//1.使用 propagation 指定事务的传播行为, 即当前的事务方法被另外一个事务方法调用时
	//如何使用事务, 默认取值为 REQUIRED, 即使用调用方法的事务
	//REQUIRES_NEW: 事务自己的事务, 调用的事务方法的事务被挂起. 
	//2.使用 isolation 指定事务的隔离级别, 最常用的取值为 READ_COMMITTED
	//3.默认情况下 Spring 的声明式事务对所有的运行时异常进行回滚. 也可以通过对应的
	//属性进行设置. 通常情况下去默认值即可. 
	//4.使用 readOnly 指定事务是否为只读. 表示这个事务只读取数据但不更新数据, 
	//这样可以帮助数据库引擎优化事务. 若真的事一个只读取数据库值的方法, 应设置 readOnly=true
	//5.使用 timeout 指定强制回滚之前事务可以占用的时间.
	@Transactional(propagation=Propagation.REQUIRES_NEW,
			isolation=Isolation.READ_COMMITTED,
			noRollbackFor={UserAccountException.class},
			rollbackFor = IOException.class,
			readOnly=false,
			timeout=3)
	@Override
	public void purchase(String username, String isbn) {}

4.3声明式事务的用法

@Transactional注解可以加在类或方法上,加在类上时是对该类的所有public方法开启事务。加在方法上时也是只对public方法起作用。另外@Transactional注解也可以加在接口上,但只有在设置了基于接口的代理时才会生效,因为注解不能继承。所以该注解最好是加在类的实现上。

下面看一下@Transactional注解的各项参数。

public @interface Transactional {
    @AliasFor("transactionManager")
    String value() default "";

    @AliasFor("value")
    String transactionManager() default "";

    Propagation propagation() default Propagation.REQUIRED;

    Isolation isolation() default Isolation.DEFAULT;

    int timeout() default -1;

    boolean readOnly() default false;

    Class<? extends Throwable>[] rollbackFor() default {};

    String[] rollbackForClassName() default {};

    Class<? extends Throwable>[] noRollbackFor() default {};

    String[] noRollbackForClassName() default {};
}

timtout

timtout是用来设置事务的超时时间,可以看到默认为-1,不会超时。

isolation

isolation属性是用来设置事务的隔离级别,数据库有四种隔离级别:读未提交、读已提交、可重复读、可串行化。MySQL的默认隔离级别是可重复读。

public enum Isolation {
    DEFAULT(-1),
    READ_UNCOMMITTED(1),  // 读未提交
    READ_COMMITTED(2), 		// 读已提交
    REPEATABLE_READ(4), 	// 可重复读
    SERIALIZABLE(8);			// 可串行化
}
1234567

readOnly

readOnly属性用来设置该属性是否是只读事务,只读事务要从两方面来理解:它的功能是设置了只读事务后在整个事务的过程中,其他事务提交的内容对当前事务是不可见的。

那为什么要设置只读事务呢?它的好处是什么?可以看出它的作用是保持整个事务的数据一致性,如果事务中有多次查询,不会出现数据不一致的情况。所以在一个事务中如果有多次查询,可以启用只读事务,如果只有一次查询就无需只读事务了。

另外,使用了只读事务,数据库会提供一些优化。

但要注意的是,只读事务中只能有读操作,不能含有写操作,否则会报错。

propagation

propagation属性用来设置事务的传播行为,对传播行为的理解,可以参考如下场景,一个开启了事务的方法A,调用了另一个开启了事务的方法B,此时会出现什么情况?这就要看传播行为的设置了。

public enum Propagation {
    REQUIRED(0), 				// 如果有事务则加入,没有则新建
    SUPPORTS(1),				// 如果已有事务就用,如果没有就不开启(继承关系)
    MANDATORY(2),				// 必须在已有事务中
    REQUIRES_NEW(3),		// 不管是否已有事务,都要开启新事务,老事务挂起
    NOT_SUPPORTED(4),   // 不开启事务
    NEVER(5),						// 必须在没有事务的方法中调用,否则抛出异常
    NESTED(6);					// 如果已有事务,则嵌套执行,如果没有,就新建(和REQUIRED类似,和REQUIRES_NEW容易混淆)
}
123456789

REQUIRES_NEW 和 NESTED非常容易混淆,因为它们都是开启了一个新的事务。我去查询了一下它们之间的区别,大概是这样:

REQUIRES_NEW是开启一个完全的全新事务,和当前事务没有任何关系,可以单独地失败、回滚、提交。并不依赖外部事务。在新事务执行过程中,老事务是挂起的。

NESTED也是开启新事务,但它开启的是基于当前事务的子事务,如果失败的话单独回滚,但如果成功的话,并不会立即commit,而是等待外部事务的执行结果,外部事务commit时,子事务才会commit。

rollbackFor

在@Transactional注解中,有一个重要属性是roolbackFor,这是用来判断在什么异常下会进行回滚的,当方法内抛出指定的异常时,进行事务回滚。rollbackForClassName也是类似的。

rollbackFor有个问题是默认情况会做什么,以前认为默认会对所有异常进行回滚,但其实默认情况下只对RuntimeException回滚。

noRollbackFor

这个和上面正好相反,用来设置出现指定的异常时,不进行回滚。

4.4声明式事务的实现机制

Spring在调用事务增强器的代理类时会首先执行TransactionInterceptor进行增强,在TransactionInterceptor的invoke方法中完成事务逻辑。首先看下TransactionInterceptor的类图结构。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-63TBqpIU-1610848600547)(https://s2.ax1x.com/2019/06/13/VfLIiD.png)]

TransactionInterceptor

	public Object invoke(MethodInvocation invocation) throws Throwable {
		// Work out the target class: may be {@code null}.
		// The TransactionAttributeSource should be passed the target class
		// as well as the method, which may be from an interface.
    // 获取目标类
		Class<?> targetClass = (invocation.getThis() != null ?AopUtils.getTargetClass(invocation.getThis()) : null);

		// Adapt to TransactionAspectSupport's invokeWithinTransaction...
		return invokeWithinTransaction(invocation.getMethod(), targetClass,invocation::proceed);
	}
12345678910

在invoke方法里,调用了父类的模板方法invokeWithinTransaction,下面我们看下TransactionAspectSupport类。

protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
			final InvocationCallback invocation) throws Throwable {

		// If the transaction attribute is null, the method is non-transactional.
  	// 获取事务属性
		TransactionAttributeSource tas = getTransactionAttributeSource();
		final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
  	// 获取PlatformTransactionManager的实现类,底层事务的处理实现都是由PlatformTransactionManager的实现类实现的,比如 DataSourceTransactionManager 管理 JDBC 的 Connection。
		final PlatformTransactionManager tm = determineTransactionManager(txAttr);
  	// 切点标识
		final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

		if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
			// Standard transaction demarcation with getTransaction and commit/rollback calls.
      // 这里是根据事务的传播行为属性去判断是否创建一个事务
			TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
			Object retVal = null;
			try {
				// This is an around advice: Invoke the next interceptor in the chain.
				// This will normally result in a target object being invoked.
        // around增强,这里是执行的回调的目标方法
				retVal = invocation.proceedWithInvocation();
			}
			catch (Throwable ex) {
				// target invocation exception
        // 异常回滚
				completeTransactionAfterThrowing(txInfo, ex);
				throw ex;
			}
			finally {
        // 清理信息
				cleanupTransactionInfo(txInfo);
			}
      // 提交事务
			commitTransactionAfterReturning(txInfo);
			return retVal;
		}

		else {
			final ThrowableHolder throwableHolder = new ThrowableHolder();

			// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
			try {
				Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr, status -> {
					TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
					try {
						return invocation.proceedWithInvocation();
					}
					catch (Throwable ex) {
						if (txAttr.rollbackOn(ex)) {
							// A RuntimeException: will lead to a rollback.
							if (ex instanceof RuntimeException) {
								throw (RuntimeException) ex;
							}
							else {
								throw new ThrowableHolderException(ex);
							}
						}
						else {
							// A normal return value: will lead to a commit.
							throwableHolder.throwable = ex;
							return null;
						}
					}
					finally {
						cleanupTransactionInfo(txInfo);
					}
				});

				// Check result state: It might indicate a Throwable to rethrow.
				if (throwableHolder.throwable != null) {
					throw throwableHolder.throwable;
				}
				return result;
			}
			catch (ThrowableHolderException ex) {
				throw ex.getCause();
			}
			catch (TransactionSystemException ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
					ex2.initApplicationException(throwableHolder.throwable);
				}
				throw ex2;
			}
			catch (Throwable ex2) {
				if (throwableHolder.throwable != null) {
					logger.error("Application exception overridden by commit exception", throwableHolder.throwable);
				}
				throw ex2;
			}
		}
	}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293

createTransactionIfNecessary

可以看到,这个invokeWithinTransaction方法已经包含了事务执行的整个流程,这里是使用了模板模式,具体的实现交给子类去实现。下面我们分析一下其中的重要方法。

protected TransactionInfo createTransactionIfNecessary(@Nullable PlatformTransactionManager tm,
			@Nullable TransactionAttribute txAttr, final String joinpointIdentification) {

		// If no name specified, apply method identification as transaction name.
		if (txAttr != null && txAttr.getName() == null) {
			txAttr = new DelegatingTransactionAttribute(txAttr) {
				@Override
				public String getName() {
					return joinpointIdentification;
				}
			};
		}

		TransactionStatus status = null;
		if (txAttr != null) {
			if (tm != null) {
        // 获取事务状态,判断各种属性
				status = tm.getTransaction(txAttr);
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
							"] because no transaction manager has been configured");
				}
			}
		}
  	// 返回一个TransactionInfo
		return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
	}
1234567891011121314151617181920212223242526272829

getTransaction

下面们看下用来用来获取TransactionStatus的getTransaction方法,这个方法是在AbstractPlatformTransactionManager抽象类中,

public final TransactionStatus getTransaction(@Nullable TransactionDefinition definition) throws TransactionException {
  	// 这里是开启一个事务,不同的事务类型(JPA  kafka jta等有不同的实现)
		Object transaction = doGetTransaction();

		// Cache debug flag to avoid repeated checks.
		boolean debugEnabled = logger.isDebugEnabled();

		if (definition == null) {
			// Use defaults if no transaction definition given.
			definition = new DefaultTransactionDefinition();
		}
		// 判断当前是否存在事务,如果存在则进行一些判断和操作
		if (isExistingTransaction(transaction)) {
			// Existing transaction found -> check propagation behavior to find out how to behave.
			return handleExistingTransaction(definition, transaction, debugEnabled);
		}
		// 设置超时
		// Check definition settings for new transaction.
		if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
			throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
		}

  	// 不存在事务,但上面讲了PROPAGATION_MANDATORY要求必须已有事务,则抛出异常
		// 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'");
		}
  	//后续其他属性都需要去新建一个事务
		else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
				definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
			SuspendedResourcesHolder suspendedResources = suspend(null);
			if (debugEnabled) {
				logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
			}
			try {
				boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
				DefaultTransactionStatus status = newTransactionStatus(
						definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
				doBegin(transaction, definition);
				prepareSynchronization(status, definition);
				return status;
			}
			catch (RuntimeException | Error ex) {
				resume(null, suspendedResources);
				throw ex;
			}
		}
		else {
			// Create "empty" transaction: no actual transaction, but potentially synchronization.
			if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
				logger.warn("Custom isolation level specified but no actual transaction initiated; " +
						"isolation level will effectively be ignored: " + definition);
			}
			boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
			return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
		}
	}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859

可以看到getTransaction是用来做了一些事务的初始化工作,包括一些判断,新建事务等等。

其中一些对已有事务的处理、嵌入式事务的处理的细节,暂时就略过了~

completeTransactionAfterThrowing

下面我们回到TransactionInterceptor,看一下下一个流程:回滚

/**
	 * Handle a throwable, completing the transaction.
	 * We may commit or roll back, depending on the configuration.
	 * @param txInfo information about the current transaction
	 * @param ex throwable encountered
	 */
	protected void completeTransactionAfterThrowing(@Nullable TransactionInfo txInfo, Throwable ex) {
		if (txInfo != null && txInfo.getTransactionStatus() != null) {
			if (logger.isTraceEnabled()) {
				logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() +
						"] after exception: " + ex);
			}
			// 这里是判断异常类型:默认会判断RuntimeException和Error,后面会分析
			if (txInfo.transactionAttribute != null && 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 | Error ex2) {
					logger.error("Application exception overridden by rollback exception", ex);
					throw ex2;
				}
			}
			// 下面是不满足回滚条件的,会照样提交
			else {
				// We don't roll back on this exception.
				// Will still roll back if TransactionStatus.isRollbackOnly() is true.
				try {
					txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
				}
				catch (TransactionSystemException ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					ex2.initApplicationException(ex);
					throw ex2;
				}
				catch (RuntimeException | Error ex2) {
					logger.error("Application exception overridden by commit exception", ex);
					throw ex2;
				}
			}
		}
	}
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647

上面有个rollbackOn(ex)方法,是用来判断回滚类型的,我们看下它的实现,它有不同的实现类,看下默认的

Spring 事务

	public boolean rollbackOn(Throwable ex) {
		return (ex instanceof RuntimeException || ex instanceof Error);
	}

1234

确实是RuntimeException和Error。

我们接着看一下回滚的实现。

public final void rollback(TransactionStatus status) throws TransactionException {
  // 事务已经完成时,回滚会出现异常
		if (status.isCompleted()) {
			throw new IllegalTransactionStateException(
					"Transaction is already completed - do not call commit or rollback more than once per transaction");
		}

		DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
		processRollback(defStatus, false);
	}
12345678910

processRollback

 * Process an actual rollback.
	 * The completed flag has already been checked.
	 * @param status object representing the transaction
	 * @throws TransactionException in case of rollback failure
	 */
	private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
		try {
			boolean unexpectedRollback = unexpected;

			try {
				triggerBeforeCompletion(status);
				// 有保存点时候回退到保存点(这里是指子事务?应该是指嵌套事务吧)
				if (status.hasSavepoint()) {
					if (status.isDebug()) {
						logger.debug("Rolling back transaction to savepoint");
					}
					status.rollbackToHeldSavepoint();
				}
				// 如果是新事务,则直接回退
				else if (status.isNewTransaction()) {
					if (status.isDebug()) {
						logger.debug("Initiating transaction rollback");
					}
					doRollback(status);
				}
				else {
					// Participating in larger transaction
					if (status.hasTransaction()) {
						if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
							}
							// 如果不是独立的事务,就只记录下来,等外部事务执行完毕一起执行
							doSetRollbackOnly(status);
						}
						else {
							if (status.isDebug()) {
								logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
							}
						}
					}
					else {
						logger.debug("Should roll back transaction but cannot - no transaction available");
					}
					// Unexpected rollback only matters here if we're asked to fail early
					if (!isFailEarlyOnGlobalRollbackOnly()) {
						unexpectedRollback = false;
					}
				}
			}
			catch (RuntimeException | Error ex) {
				triggerAfterCompletion(status, TransactionSynchronization.STATUS_UNKNOWN);
				throw ex;
			}

			triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);

			// Raise UnexpectedRollbackException if we had a global rollback-only marker
			if (unexpectedRollback) {
				throw new UnexpectedRollbackException(
						"Transaction rolled back because it has been marked as rollback-only");
			}
		}
		finally {
			cleanupAfterCompletion(status);
		}
	}

上面的代码中,有保存点的是指的嵌套事务,因为嵌套事务并不是真正的两个事务,所以会有保存点的信息进行回滚。

事务的提交和回滚的流程类似,同样进行了不同事务类型的判断,在此不进行额外的分析。

5、事务管理器

Spring的核心事务管理抽象,管理封装了一组方法,无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。

org.springframework.transaction.PlatformTransactionManager

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
 </bean>

Hibernate事务

<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

JPA事务

<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="sessionFactory" ref="sessionFactory" />
</bean>

Java原生API事务

通常用于跨越多个事务管理源(多数据源),则需要使用下面的内容

<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
	<property name="transactionManagerName" value="java:/TransactionManager" />
</bean>

6、事务属性

Spring 事务

6.1传播行为

事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如方法可能继续在现有事务中运行,也可能开启一个新的事务,并在自己的事务运行。spring中的事务传播行为可以由传播属性指定。spring指定了7种类传播行为。

REQUIRED 如果有事务在运行,当前的方法就在这个事务内运行,否则就开启一个新的事务,并在自己的事务内运行,默认传播行为
REQUIRED_NEW 当前方法必须启动新事务,并在自己的事务内运行,如果有事务正在运行,则将它挂起
SUPPORTS 如果有事务在运行,当前的方法就在这个事务内运行,否则可以不运行在事务中
NOT_SUPPORTED 表示该方法不应该运行在事务中。如果存在当前事务,在该方法运行期间,当前事务将被挂起。如果使用JTATransactionManager的话,则需要访问TransactionManager
MANDATORY 当前的方法必须运行在事务内部,如果没有正在运行的事务,就会抛出异常
NEVER 当前方法不应该运行在事务中,如果有运行的事务,就抛出异常
NESTED 如果有事务在运行,当前的方法就应该在这个事务的嵌套事务内运行。嵌套的事务可以独立于当前事务进行单独地提交或回滚。如果当前事务不存在,那么其行为与PROPAGATION_REQUIRED一样。

通常情况下,第一种和第二种用的比较多,需要多理解一下

6.2事务隔离级别

并发事务会导致发生以下三种类型的问题

脏读 发生在一个事务读取了另一个事务改写尚未提交的数据时,改写的数据被回滚了,那么第一个事务获取的数据无效
不可重复读 当同一个事务执行两次及以上相同的查询时,每次都得到不同的数据。一般因为另一并发事务在两次查询期间进行了更新
幻读 第一个事务读取了一些数据,此时第二个事务在该表中插入了一些新数据,这是第一个事务再读取相同的数据就会多几行

不可重复读和幻读的区别:不可重复读侧重点在相同数据被修改,而幻读是删除或新增

从理论上讲,事务应该完全隔离,避免并发事务导致的问题,但是这样可能对性能产生极大影响,因为事务必须按顺序进行了。所以在实际的开发中,为了提升性能,事务会以比较低的隔离级别运行。

spring中事务的隔离级别可以通过隔离属性指定

DEFAULT 使用底层数据库的默认隔离级别,大部分数据库,默认隔离级别都是READ_COMMITED
READ_COMMITED 只允许事务读取已经被其他事务提交的更改,可以避免脏读,但不可重复读和幻读问题仍然可能出现
READ_UNCOMMITED 允许事务读取未被其他事务提交的更改。脏读,不可重复读,幻读都可能会出现
REPEATABLE_READ 确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新,可以避免脏读和不可重复读,但是幻读的问题依然存在
SERIALIZABLE 确保事务可以从一个表中读取相同的行,在这个事务持续期间,禁止其他事务对该表执行插入,更新,删除。所有的并发问题都能避免,但是性能比较低。

注意:事务的隔离级别需要底层数据库引擎的支持,而不是应用程序或者框架的支持

Oracle支持2种事务隔离级别:READ_COMMITED,SERIALIZABLE

MySQL支持4种事务隔离级别

6.3回滚规则

默认情况下只有未检查异常(RuntimeException和Error类型的异常)会导致事务回滚。事务的回滚规则可以通过属性管理

rollbackFor:遇到时必须进行回滚

noRollbackFor:一组异常类,遇到时必须不能回滚

rollbackFor={IOException.class,SQLException.class},
noRollbackFor=ArithmeticException.class;

6.4只读属性

如果事务只读数据但不修改可以通过配置只读事务属性,帮助数据库引擎优化事务。只读事务属性:表示这个事务只读读取数据,但是不更新数据

6.5超时事务属性

事务可以在行和表上获得锁,因此长事务会占用资源,并对整体性能产生影响。可以配置超时事务属性,事务在强制回滚之前可以保持多久,这样可以避免长期运行的事务占用资源

7、事务失效

因为spring事务是基于aop的代理机制,当方法中调用this本身的方法时候即使在this的方法标明事务注解,但是事务注解会失效。如下

	@Transactional
	@Override
	public void purchase(String username, String isbn) {
		this.update(username, isbn);
	}
 
	@Transactional
	public void update(String username, String isbn) {
		//1. 获取书的单价
		int price = bookShopDao.findBookPriceByIsbn(isbn);
		//2. 更新数的库存
		bookShopDao.updateBookStock(isbn);
		//3. 更新用户余额
		bookShopDao.updateUserAccount(username, price);
	}

****原因****:因为调用ths本身方法不走代理机制,这个时候可以通过配置解决这个问题。

解决事务失效

在配置中添加如下内容

<!--开启aspectj代理,并暴露aop代理到ThreadLocal-->
<aop:aspectj-autoproxy expose-proxy="true"/>

将上述调用的地方改成如下

@Transactional
@Override
public void purchase(String username, String isbn) {
	((BookShopServiceImpl)AopContext.currentProxy()).update(username, isbn);
}

总结

事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

username, String isbn) {
this.update(username, isbn);
}

@Transactional
public void update(String username, String isbn) {
	//1. 获取书的单价
	int price = bookShopDao.findBookPriceByIsbn(isbn);
	//2. 更新数的库存
	bookShopDao.updateBookStock(isbn);
	//3. 更新用户余额
	bookShopDao.updateUserAccount(username, price);
}

***\*原因\****:因为调用ths本身方法不走代理机制,这个时候可以通过配置解决这个问题。

### **解决事务失效**

在配置中添加如下内容

```java
<!--开启aspectj代理,并暴露aop代理到ThreadLocal-->
<aop:aspectj-autoproxy expose-proxy="true"/>

将上述调用的地方改成如下

@Transactional
@Override
public void purchase(String username, String isbn) {
	((BookShopServiceImpl)AopContext.currentProxy()).update(username, isbn);
}

总结

事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!

上一篇:spring事务详解(三)源码详解


下一篇:loadrunner----web_get_int_property函数