转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6776256.html
事务管理主要负责对持久化方法进行统一的提交或回滚,Spring进行事务管理即我们无需在代码中显式地书写事务的开启、提交、回滚等操作了,我们只需为Spring指明有哪些方法需要进行事务管理,Spring自动在运行时为那些方法进行事务管理。使用Spring进行事务管理的工作就在于,配置好事务管理器,以及在哪些方法进行管理即可。
一:Spring底层关于事务管理的API
Spring封装了几个API用于管理事务,无论在后面使用哪种事务声明方式,底层都是调用这几个API工作的。了解这些API,对于后面配置事务时要配置哪些标签项就一清二楚了。其中,最重要的是前两个:事务管理器、事务定义信息配置接口。
1:事务管理器:对事务进行配置,比如:哪些方法进行事务管理、回滚信息配置等。在DAO层使用不同的持久层框架时,所用的事务管理器是不同的。在配置时,要根据所用的持久层框架来配置事务管理器,即:配置真正进行事务管理的实现类。主要有以下几种:其中,前两种是常用的。
2:事务定义:主要对事务传播属性、隔离级别、超时时间、是否只读进行配置。
1)事务传播属性:确定如何为持久化方法增加事务行为。主要解决Service层中业务方法的相互调用问题。
事务传播传播属性是针对 当前将要执行的持久化操作方法 所面临的7种情况下 作如何反应,主要分为三种:
第一种,“加入当前事务”,将执行的方法有三种选择:
PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
(上面三种配置的意思是:假设业务方法A中调用了方法B,如果当前有事务(即方法A开启了事务),就把方法B加入当前事务中被管理。如果当前没有事务在执行,则三种配置分别对应了三种行为:新建一个事务来管理方法B、不进行事务管理直接运行方法B、抛出异常)
第二种,“不使用当前事务”:
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起不用。
(即:假设业务方法A中调用了方法B,自己新建一个事务来管理将要执行的持久化方法B,如果当前所处的方法A中已有事务,那就挂起当前事务。)
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起不用。
(即:假设业务方法A中调用了方法B,则方法B不进行事务管理,如果方法A已有事务,则把A的事务挂起。)
PROPAGATION_NEVER--以非事务方式执行,如果当前所处方法中存在事务,则抛出异常。
(即:假设业务方法A中调用了方法B,则持久化方法B不进行事务管理,如果A已有事务,则抛出异常)
第三种,“嵌套事务”:
PROPAGATION_NESTED -- 总是新建一个事务来执行持久化方法。如果方法所处位置已有事务了,就形成了嵌套事务。
(事务传播属性分三类,其中常用的就是三类中的第一个情况)
2)隔离级别配置:隔离级别是为了解决并发事务可能引发脏读、不可重复读、幻读的情况而配置的。
1. ISOLATION_DEFAULT: 这是一个PlatfromTransactionManager默认的隔离级别,使用数据库默认的事务隔离级别,即:把隔离性交给数据库来保证。
2. ISOLATION_READ_UNCOMMITTED: 这是事务最低的隔离级别,它充许令外一个事务可以看到这个事务未提交的数据。这种隔离会产生脏读,不可重复读和幻像读。
3. ISOLATION_READ_COMMITTED: 保证一个事务修改的数据提交后才能被另外一个事务读取。另外一个事务不能读取该事务未提交的数据。可以防止脏读。
4. ISOLATION_REPEATABLE_READ: 这种事务隔离级别可以防止脏读,不可重复读。但是可能出现幻像读。
5. ISOLATION_SERIALIZABLE 这是花费最高代价但是最可靠的事务隔离级别。事务被处理为串行执行。可防止脏读,不可重复读,幻像读。
3)只读属性:如果把事务定义为只读,则该事务中不能进行修改类数据库操作,如:insert/update/delete等。只能进行查询操作。
4)超时时间:事务的存在时间,一般很少用到。
二:Spring事务管理的准备
在Spring中进行事务管理,无论哪种方式,都需要使用连接池。连接池我们一般使用C3P0来进行优化。
首先,在一个properties文件中配置好c3p0的配置信息,如:驱动、数据库url、账户、密码等。
然后,在applicationContext.xml中注册连接池,选择C3P0的ComboPooledDataSourse,配置好id以及一系列属性:
默认的数据源配置:
三:Spring声明式事务管理——基于AspectJ的XML配置,使用AOP思想
1:首先,配置事务管理器,根据持久层框架来选择。如:使用Spring提供的JDBCTemplate框架进行持久化操作,则事务管理器为DataSourseTransactionManager。配置时,需要把dataSourse注入给事务管理器。
<!-- 事务管理器(PlatformTransactionManager) --> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean>
2:然后配置通知:AspectJ提供了基于AOP的事务管理的通知,标签是<tx:advice>。
需要配置:通知的id、所用到的事务管理器id、<tx:attributes>子标签
<tx:attributes>子标签内:配置需要事务管理的方法们
切入点<tx:method name="需要进行事务管理的方法名:可以用正则表达式" 对该方法进行事务定义,包括:事务隔离级别属性、事务传播属性、只读属性、回滚异常和不回滚异常配置等>
<!-- 事务通知 --> <tx:advice id="txAdvice" transaction-manager="txManager"> <!-- 为哪些方法进行事务管理、怎么管理--> <tx:attributes> <!-- 所有get开头的方法的事务定义 --> <tx:method name="get*" propagation="REQUIRED"/>//以正则表达式的形式匹配需要进行事务管理的方法,并指明如何管理事务 <!-- 其他的方法的事务定义--> <tx:method name="*" .../> </tx:attributes> </tx:advice>
3:配置切面
上面配置了的事务通知,通过 正则表达式或精确的方法名 为方法制定了相应的事务。接下来,就要通过切面,把 事务通知 切入到具体的需要事务管理的类中去。
事务通知中定义的method与切面中定义的切入点是怎么匹配的呢?这个过程发生了两次匹配:首先,pointcut表达式根据“返回值 路径.方法名.(参数列表)” 匹配筛选出了一堆持久化操作的方法;然后tx:advice再对这些方法进行匹配筛选:符合 name属性格式的方法名的持久化方法就进行name所处tx:method标签内定义的事务管理。
<aop:config> <aop:pointcut id="pointcut1" expression="execution(返回值 路径.方法名.(参数列表))"/>//切入点筛选出需要进行事务管理的方法,至于如何管理,则在tx:advice中再匹配 <aop:pointcut id="pointcut2" expression="execution(返回值 路径.方法名.(参数列表))"/> <aop:pointcut id="pointcut3" expression="execution(返回值 路径.方法名.(参数列表))"/> <aop:advisor advice-ref="txAdvice1" pointcut-ref="pointcut1"/> <aop:advisor advice-ref="txAdvice2" pointcut-ref="pointcut2"/> <aop:advisor advice-ref="txAdvice3" pointcut-ref="pointcut3"/> </aop:config>
总结:基于AspectJ的XML事务管理的使用过程为:
配置数据源——配置事务管理器——配置事务管理通知为不同名称格式的方法们自定义事务——配置切面把事务管理通知切入到需要事务管理的方法们