Spring--事务小结

Spring为我们提供了非常方便的事务管理,在不需要了解不同持久层事务处理的情况下,使用配置或注解的方式实现了事务的统一管理。这也就体现了之前所说的Spring核心AOP功能的作用。而为了更好的使用Spring事务管理,需要了解以下知识:

一、数据库事务相关基础知识

1、  何为事务

最简单的话就是,事务内的多个SQL语句,在事务提交时,要么都成功,要么都失败。必定是一个整体。事务的四个特性:

1)  原子性:组成数据库的多个事务操作是一个不可分割的原子单位。若有失败时,已成功的操作也被撤销。

2)  一致性:事务操作成功后,数据库状态和业务规则一致。

3)  隔离性:并发执行数据操作时,不同事务拥有不同的事务空间,互不干扰。

4)  持久性:事务提交成功后,所有数据操作必须持久到数据库内。

2、  数据并发出现的问题

1)  脏读:事务A读取到事务B修改但是未提交的数据。当事务B回滚后,A事务如果以之前读到的数据操作,必定会出错。

2)  不可重复读:事务A读取了两次数据,而这两次数据由于事务B的执行前后不一致。而数据的不一致会导致问题。

3)  幻象读:事务A按照某查询条件读取到数据,这时其他事务新增操作导致新增结果同样满足A的查询条件。事务A再读取时会出现前后数据不一致。

虽然不可重复读和幻象读有相似之处,但是区别在于不可重复读由于数据的更改导致,而幻象读由于新事务提交导致。

丢失更新:

           两个事务同时对一个数据操作,在事务互不知道的情况下,其中一个事务的操作可能会被另外一个事务操作覆盖。分为包裹时的回滚覆盖和提交成功的覆盖。

接下来,由于数据库中定义的隔离和Spring事务定义的隔离有重复部分,所以直接介绍Spring的事务属性。

二、Spring事务隔离

         定义了当前事务与其他事务的隔离状态。

1、  ISOLATION_DEFAULT:

这是定义的默认隔离级别,自动采用数据库默认的事务隔离级别。

2、  ISOLATION_READ_UNCOMMITED:

这是隔离级别最低的事务隔离。会读取到修改过但是未提交的数据。因此会出现脏读,不可重复读,幻象读。隔离级别在于处理多事务的并发问题。 

3、  ISOLATION_READ_COMMITED:

保证不会读取修改过但是未提交的数据。避免了脏读,但是不可重复读和幻象读无法避免。大部分数据库的隔离级别。

4、  ISOLATION_REPEATABLE_READ:

保证不会修改另一个事务读取但未提交的数据,可以避免脏读和不可重复读。但是带来较多的性能损失。

5、  ISOLATION_SERIALIZABLE:

事务串行执行,性能损失最大。

三、Spring事务传播

正常而言,给一个方法定义事务执行是十分清晰的。但是,如果在一个方法中调用了另外一个方法,那么事务操作应该如何执行呢?接下来就是有关事务传播的话题了:

Spring定义了其中类型的事务传播行为,它们规定了事务方法和事务方法发生嵌套调用时如何进行传播:

1、  PROPAGATION_REQUIRED:

如果当前执行有事务,则加入当前事务,若没有事务,则新建一个事务。

例:有ServiceA.methodA,执行时调用ServiceB.methodB。ServiceB.methodB的事务传播属性设为REQUIRED。methodB执行时,先看当前methodA执行环境有没有事 务,如果有,则加入A所在事务,A,B任意方法异常,统一回滚。若没有事务,新建一个事务供B方法执行。

2、  PROPAGATION_SUPPORTS:

如果当前执行有事务,则以事务方式进行。若没有事务,则以非事务方式进行。

3、  PROPAGATION_MANDATORY:

如果当前执行有事务,则以事务方式进行。若没有事务,则抛出异常。

4、  PROPAGATION_REQUIRES_NEW:

无论当前有没有事务,都新建一个事务执行。新建的事务和旧事务分属于两个事务,互不相干。

再以methodA和methodB为例。A如果有事务环境,调用到B方法时,将A所在的事务挂起,B新建一个事务,B执行完后,重新进入A的事务环境。执行过程中,如果A出现 异常,B已经提交,则不用回滚。B出现异常,A方法同样可能提交。

5、  PROPAGATION_NOT_SUPPORTED:

如果当前执行有事务,将事务挂起后继续执行。

6、  PROPAGATION_NEVER:

如果当前执行有事务,将事务挂起后继续执行

7、  PROPAGATION_NESTED:

同样新建一个事务,与REQUIRES_NEW的区别在于,它与父事务是相依的。出现异常时,回滚一致。而且有一个savePoint的选择。

四、Spring事务配置

         配置事务大方向有三种:编程式配置(侵入代码较严重,不介绍),XML配置,注解配置。

1、  XML配置:

1)  原始的proxyBean:

<bean id="transactionManager"
		  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>	  
	</bean>
	
	<!-- 织入对象 -->
	<bean id="userServiceTarget" class="com.paditang.service.UserService"
		p:userDao-ref="userDao">
	</bean>
	<!-- 织入生成的代理对象,执行时自动处理业务逻辑 -->
	<bean id="userService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
		p:transactionManager-ref="transactionManager"
		p:target-ref="userServiceTarget">
		<property name="transactionAttributes">
			<props>
				<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
				<prop key="*">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>


以上配置很容易看出是基于动态代理的实现形式。但是需要对每个业务类都进行配置,且只能对方法名定义。而且定义时IDE无法判断输入,容易出错,所以不推荐。

2)  基于tx/aop命名空间:

<aop:config proxy-target-class="true">
		<aop:pointcut expression="execution(* com.paditang.service.*.*(..))" id="serviceMethod"/>
		<aop:advisor pointcut-ref="serviceMethod" advice-ref="txAdvice"/><!-- 定义切点和增强 -->
	</aop:config>
	
	<tx:advice id = "txAdvice" transaction-manager="transactionManager">
		<tx:attributes>
			<tx:method name="*"/>
		</tx:attributes>
	</tx:advice>


2、基于注解

只需要在service实现类上增加@Transactional注解,并在Spring配置中

<!-- 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

	<!-- 配置基于注解的声明式事务 -->
	<tx:annotation-driven transaction-manager="transactionManager" />


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章