一、Spring中的事务
Spring没有直接管理事务,但它对事务管理提供了强大支持,它有很多可供选择的事务管理器,进而将事务管理的责任委托给使用JDBC、JTA或持久化机制的某个特定平台的事务实现。例如使用JTA事务管理器,只需要将class修改为相应的类即可:
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"></bean>
Spring的Dao框架对事务提供了强大的支持。它包括有两种事物管理,即:
(a)编程式事物管理(programmatic tansaction management)
(b)声明式事物管理(Declarative tansaction management)
所谓编程式事物管理,就是把事物管理以代码的形式编写到你的应用中要使用事物管理的地方,灵活性较强。而声明式事物管理是以配置文件的形式在xml文件中定义,好处是不具有代码入侵性,当不需要事物管理时,可以直接修改配置文件,而不用修改代码。
下面对声明式事物管理进行说明.
二、事务配置说明
配置文件dataSource.xml中一般为如下形式:
<!-- 配置JDBC -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 事务配置 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="dataSource"></bean>
<!-- 开启事务行为 -->
<tx:annotation-driven transaction-manager="txManager"/>
<!-- 事务管理 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="query*" propagation="NOT_SUPPORTED"/>
<tx:method name="get*" propagation="NOT_SUPPORTED"/>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="txPointCut" expression="execution(* com.ecm.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
<tx:annotation-driven/>标签是注解驱动的事务管理支持的核心,用于开启事务行为。
<tx:annotation-driven/>标签的属性:
transaction-manager:指定到现有的PlatformTransactionManager bean的引用,通知会使用该引用。default="transactionManager"
mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理。
order:指定创建的切面的顺序。只要目标对象有多个通知就可以使用该属性。
proxy-target-class:决定是基于接口的还是基于类的代理被创建。如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理将起作用。如果目标对象没有实现任何接口,这将自动设置该属性为true。
aop:config的proxy-target-class导致ClassCastException:http://qurtyy.blog.163.com/blog/static/574436812010102310381784/
三、Aop配置说明
<!-- 事务管理 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<!-- 注意:read-only属性仅在使用事务时起作用,所以下面NOT_SUPPORTED不配置 -->
<tx:method name="query*" propagation="NOT_SUPPORTED"/>
<tx:method name="*"/>
</tx:attributes>
</tx:advice>
<aop:config proxy-target-class="true">
<aop:pointcut id="txPointCut" expression="execution(* com.ecm.service..*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
加上 proxy-target-class="true" 使得 spring 可以使用 cglib 对类也可以动态代理 .
3.1、对aop-config中expression表达式进行解释
(* com.xx.xx.dao.impl..*.*(..))
第一个*表示匹配所有类型的返回值
第二个*表示匹配所有的类(前面的..表示impl包及其所有子包)
第三个*表示匹配类中所有的方法
最后括号中..表示匹配方法所有参数
3.2、tx:advice标签简介
id是该advice bean的标识,而transaction-manager则必须引用一个PlatformTransactionManager bean。
还可以通过<tx:attributes>标签定制<tx:advice>标签所创建的通知的行为。
3.3、<tx:method/>标签的属性
name:方法名的匹配模式,通知根据该模式寻找匹配的方法。
propagation:设定事务定义所用的传播级别。
isolation:设置事务的隔离级别。
timeout:指定事务的超时(秒)。在多事务并行情况下,为了保证正确性,有些事务的操作会有延迟,甚至死锁。设置事务超时时间,可以避免事务的长时间等待。设置事务超时时间也要配合PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED使用。
read-only:该属性为true指示事务是只读的,应用这项属性时,底层的数据库可以对读取进行最优化,但要配合PROPAGATION_REQUIRED,PROPAGATION_REQUIRES_NEW或PROPAGATION_NESTED使用,即只能在事务中使用。
(a)如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持SQL执行期间的读一致性;
(b)如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询SQL必须保证整体的读一致性,否则,在前条SQL查询之后,后条SQL查询之前,数据被其他用户改变,则该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持,read-only="true"表示该事务为只读事务,比如上面说的多条查询的这种情况可以使用只读事务,由于只读事务不存在数据的修改,因此数据库将会为只读事务提供一些优化手段,例如Oracle对于只读事务,不启动回滚段,不记录回滚log。
no-rollback-for:以逗号分隔的异常类的列表,目标方法可以跑出这些异常而不会导致通知执行回滚
rollback-for:以逗号分隔的异常类的列表,当目标方法跑出这些异常时会导致通知执行回滚。默认情况下,该列表为空,因此不在no-rollback-for列表中的任何异常都会导致回滚。
3.3.1、Transactional的异常控制:
(1)默认是Check Exception 不回滚,unCheck Exception回滚。
(2)可以在rollbackFor 和 noRollbackFor 进行配置,使Check Exception也进行回滚。
(3)如果rollbackFor 和 noRollbackFor两个都有同样的异常,那么遇到该异常,还是回滚。
(4)rollbackFor 和noRollbackFor 配置也许不会含盖所有异常,对于遗漏的按照Check Exception 不回滚,unCheck Exception回滚
例如:不配置rollbackFor 和 noRollbackFor ,为默认时:
http://blog.sina.com.cn/s/blog_89ca421401016bmg.html
测试情形1:
<!--datasource.xml-->
<tx:method name="submit*"/>
//java代码
throw new Exception();
该情形事务不会回滚
测试情形2:
<!--datasource.xml-->
<tx:method name="submit*" rollback-for="Exception"/>
//java代码
throw new Exception();
该情形事务会回滚
3.3.2、隔离等级(isolation level)
在一个应用应用程序中,可能有多个事务在运行,这时就会产生一些问题:
(1)dirty read
一个事物更新了数据库中的某些数据,另一个事物读取了这些数据,这时前一个事物由于某些原因回滚了,那么第二个事物读取的数据就是“脏数据”。
(2)non-repeatable read
一个事物需要两次查询同一数据,但两次查询中间可能有另外一个事物更改了这个数据,导致前一个事物两次读出的数据不一致。
(3)phantom read
一个事物两次查询同一个表,但两次查询中间可能有另外一个事物又向这个表中插入了一些新数据,导致前一个事物的两次查询不一致。
为了解决以上问题,Spring的事物管理定义了一些隔离级别,所谓“隔离”,即对数据的锁定。
上述参数也是在org.springframework.transaction.TransactionDefinition接口中定义的(类型是public static final,值为-1,1,2,4,8)。上述参数中最常用的是ISOLATION_DEFAULT。
3.3.3、隔离等级(2)
getIsolationLevel:他对其他事务所看到的数据变化进行控制。
事务隔离级别:
(1)ISOLATION_DEFAULT:默认级别(对大多数数据库来说就是ISOLATION_READ_COMMITTED)
(2)ISOLATION_READ_UNCOMMITTED:最低的隔离级别。事实上我们不应该隔离级别,因为在事务完成前,其他事务可以看到该 事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。
(3)ISOLATION_READ_COMMITTED:大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾 的是,在该事务提交后,你就可以查看其他事务插入活更新的数据。这意味着在事务的不同点上,如果其他事务修改数据,你会看到不同的数据。
(4)ISOLATION_REPEATABLE_READ :该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。
(5)ISOLATION_SERIALIZABLE:代价最大、可靠性最高的隔离级别,所有的事务都是俺顺序一个接一个的执行。
3.3.4、getPropagationBehavior(传播行为):指定了当代码请求一个新的事务时Spring所做的事情
(1)PROPAGATION_REQUIRED:当前如果有事务,Spring就会使用该事务;否则会开始一个新事务。
(2)PROPAGATION_SUPPORTS:当前如果有事务,Spring就会使用该事务;否则不会开启一个新事务。
(3)PROPAGATION_MANDATORY:当前如果有事务,Spring就会使用该事务;否则会抛出异常。
(4)PROPAGATION_REQUIRES_NEW:Spring总会开始一个新事务。如果当前有事务,则该事务挂起。
(5)PROPAGATION_NOT_SUPPORTED:Spring不会执行事务中的代码。代码总是在非事务环境下执行,如果当期有事务,则该事务挂起。
(6)PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
(7)PROPAGATION_NESTED:如果当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与
PROPAGATION_REQUIRED一样。当使用PROPAGATION_NESTED时,底层的数据源必须基于JDBC3.0,并且实现者需要支持保存点事务机制。