一、基础知识普及
声明式事务的事务属性:
一:传播行为
二:隔离级别
三:只读提示
四:事务超时间隔
五:异常:指定除去RuntimeException其他回滚异常。
传播行为:
所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。
spring的事务传播规则:
| 传播行为 | 意义 |
| PROPAGATION_REQUIRED | 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。 |
| PROPAGATION_REQUIRES_NEW | 创建一个新的事务,如果当前存在事务,则把当前事务挂起。 |
| PROPAGATION_SUPPORTS | 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。 |
| PROPAGATION_NOT_SUPPORTED | 以非事务方式运行,如果当前存在事务,则把当前事务挂起。 |
| PROPAGATION_NEVER | 以非事务方式运行,如果当前存在事务,则抛出异常 |
| PROPAGATION_MANDATORY | 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。 |
| PROPAGATION_NESTED | 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。 |
这里需要指出的是,前面的六种事务传播行为是 Spring 从 EJB 中引入的,他们共享相同的概念。而 PROPAGATION_NESTED是 Spring 所特有的。以 PROPAGATION_NESTED 启动的事务内嵌于外部事务中(如果存在外部事务的话),此时,内嵌事务并不是一个独立的事务,它依赖于外部事务的存在,只有通过外部的事务提交,才能引起内部事务的提交,嵌套的子事务不能单独提交。如果熟悉 JDBC 中的保存点(SavePoint)的概念,那嵌套事务就很容易理解了,其实嵌套的子事务就是保存点的一个应用,一个事务中可以包括多个保存点,每一个嵌套子事务。另外,外部事务的回滚也会导致嵌套子事务的回滚。
隔离级别:
隔离级别是指若干个并发的事务之间的隔离程度:
spring的事务隔离级别:
| 隔离级别 | 含义 |
| ISOLATION_DEFAULT | 这是默认值,表示使用底层数据库的默认隔离级别。对大部分数据库而言,通常这值就是ISOLATION_READ_COMMITTED。 |
| ISOLATION_READ_UNCOMMITTED | 该隔离级别表示一个事务可以读取另一个事务修改但还没有提交的数据。该级别不能防止脏读和不可重复读,因此很少使用该隔离级别。 |
| ISOLATION_READ_COMMITTED | 该隔离级别表示一个事务只能读取另一个事务已经提交的数据。该级别可以防止脏读,这也是大多数情况下的推荐值。 |
| ISOLATION_REPEATABLE_READ | 该隔离级别表示一个事务在整个过程中可以多次重复执行某个查询,并且每次返回的记录都相同。即使在多次查询之间有新增的数据满足该查询,这些新增的记录也会被忽略。该级别可以防止脏读和不可重复读。 |
| ISOLATION_SERIALIZABLE | 所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 |
只读提示:
SessionFlush设置为none (readOnly);
事务的只读属性是指,对事务性资源进行只读操作或者是读写操作。所谓事务性资源就是指那些被事务管理的资源,比如数据源、 JMS 资源,以及自定义的事务性资源等等。如果确定只对事务性资源进行只读操作,那么我们可以将事务标志为只读的,以提高事务处理的性能。在 TransactionDefinition 中以 boolean 类型来表示该事务是否只读。
事务超时间隔:
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒。
声明式事务使用范例:
二、声明式事务配置的5中方式(方式四最常用)
方式一、基于Spring1.x,使用TransactionProxyFactoryBean为每个Bean生成一个代理。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 配置DAO省略 -->
<!-- *******业务逻辑层(是对各个DAO层的正面封装)主要用到<<门面模式>>****** -->
<bean id="fundService"
class="com.jack.fund.service.serviceimpl.FundService">
<property name="producedao">
<ref bean="fundProduceDAO" />
</property>
</bean>
<bean id="fundServiceProxy"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 配置事务管理器 -->
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<!-- 此属性指定目标类本本身是否是代理的对象,如果目标类没有实现任何类,就设为true代表自己 可省略不写 -->
<property name="proxyTargetClass">
<value>false</value>
</property>
<!-- 代理接口 可省略不写 -->
<property name="proxyInterfaces">
<value>com.jack.fund.service.IFundService</value>
</property>
<!-- 目标bean -->
<property name="target">
<ref bean="fundService" />
</property>
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
</beans>
可以看出它适用于你的库表比较少的情况下,针对每一个功能模块配置一个业务代理服务。如果模块多大话,就显得代码有点多了,发现他们只是稍微一点不一样。这时我们就应该想到继承的思想。用第二种方法。
方式二、基于Spring1.x,使用TransactionProxyFactoryBean为所有Bean共享一个代理基类。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 利用继承的思想简化配置,要把abstract="true" -->
<bean id="transactionBase" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init="true" abstract="true">
<!-- 配置事务管理器 -->
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<!-- 而具体的模块可以简单的这样配置。只要指明它的parent(父类)就可以了。父类一般把abstract="true",因为在容器加载的时候不需要初始化,等到用的时候再有它的子类调用的时候,再去初始化。 -->
<!-- 配置DAO省略 -->
<!-- *******业务逻辑层(是对各个DAO层的正面封装)主要用到<<门面模式>>****** -->
<bean id="fundService"
class="com.jack.fund.service.serviceimpl.FundService">
<property name="producedao">
<ref bean="fundProduceDAO" />
</property>
</bean>
<bean id="fundServiceProxy" parent="transactionBase" >
<property name="target">
<ref bean="fundService" />
</property>
</bean>
</beans>
这样配置的话,如果有多个像fundService这样模块时,可以少些很多重复的代码。
方式三、基于SpringAOP的拦截器(主要利用BeanNameAutoProxyCreator自动创建事务代理)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager" ref="transactionManager" />
<!-- 配置事务属性 -->
<property name="transactionAttributes">
<props>
<prop key="delete*">PROPAGATION_REQUIRED</prop>
<prop key="add*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<!-- 匹配所有的Service或者某个具体Service -->
<value>*Service</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>transactionInterceptor</value>
</list>
</property>
</bean>
<!-- 配置DAO省略 -->
<!-- *******业务逻辑层(是对各个DAO层的正面封装)主要用到<<门面模式>>****** -->
<bean id="fundService"
class="com.jack.fund.service.serviceimpl.FundService">
<property name="producedao">
<ref bean="fundProduceDAO" />
</property>
</bean>
</beans>
方式四、基于Spring2.x,使用tx标签配置的拦截器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="configLocation" value="classpath:hibernate.cfg.xml" />
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
</bean>
<!-- 定义事务管理器(声明式的事务) -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<!-- 定义JDBC的事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
-->
<!-- 定义切面 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" rollback-for="java.lang.Exception"/>
<!--其他方法设为如下传播属性 -->
<tx:method name="*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
</tx:attributes>
</tx:advice>
<!-- 定义切点和织入 -->
<aop:config>
<aop:pointcut id="interceptorPointCuts"
expression="execution(* com.spring.service.*.*(..))" />
<!--mgrService和empService省略 -->
<aop:pointcut id="leePointcut"
expression="bean(mgrService) || bean(empService)" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="interceptorPointCuts" />
<aop:advisor advice-ref="txAdvice"
pointcut-ref="leePointcut" />
<!-- 将authority转换成切面Bean
切面Bean的新名称为:authorityAspect -->
<aop:aspect id="authorityAspect" ref="authority">
<!-- 定义一个Around增强处理,直接指定切入点表达式
以切面Bean中的authority()方法作为增强处理方法 -->
<aop:around pointcut="execution(* org.crazyjava.message.service.impl.*Impl.*Message*(..))"
method="authority"/>
</aop:aspect>
</aop:config>
<!-- 定义一个普通Bean实例,该Bean实例将进行权限控制 -->
<bean id="authority"
class="org.crazyjava.message.authority.AuthorityInterceptor"/>
</beans>
关于权限拦截的类AuthorityInterceptor定义如下:
public class AuthorityInterceptor
{
//进行权限检查的方法
public Object authority(ProceedingJoinPoint jp)
throws java.lang.Throwable
{
HttpSession sess = null;
//获取被拦截方法的全部参数
Object[] args = jp.getArgs();
//遍历被拦截方法的全部参数
for (int i = 0 ; i < args.length ; i++ )
{
//找到HttpSession类型的参数
if (args[i] instanceof HttpSession) sess =
(HttpSession)args[i];
}
//取出HttpSession里的user属性
Integer userId = (Integer)sess.getAttribute("user");
//如果HttpSession里的user属性不为null,且大于0
if ( userId != null && userId > 0)
{
//继续处理
return jp.proceed(args);
}
else
{
//如果还未登录,抛出异常
throw new MessageException("您还没有登陆,请先登陆系统再执行该操作");
}
}
}
方式五、注解式声明事务
步骤一、在spring配置文件中引入<tx:>命名空间
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
步骤二、具有@Transactional 注解的bean自动配置为声明式事务支持
<!-- 事务管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="sessionFactory"/>
</property>
</bean>
<!-- 对标注@Transaction注解的Bean进行事务管理 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
步骤三、在接口或类的声明处 ,写一个@Transactional.
要是只在接口上写, 接口的实现类就会继承下来、接口的实现类的具体方法,可以覆盖类声明处的设置
@Transactional //类级的注解、适用于类中所有的public的方法
@Transactional
public class TestPOAOImpl extends POAOBase implements TestPOAO
{
@Transactional(isolation = Isolation.READ_COMMITTED)
public void test1()
{
String sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解赵云',30)";
execute(sql);
sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解张飞',26)";
execute(sql);
int a = 9 / 0; //异常
sql = "INSERT INTO sy_test (NAME,AGE) VALUES('注解关羽',33)";
execute(sql);
System.out.println("走完了");
}
//execute() 方法略...
}
1、使用说明(狠重要)
如果@Transactional 修饰Bean类,表明这些事务设置对整个Bean类起作用;如修饰的是Bean类的某个方法,表明这些事务设置支队该方法有效。
使用@Transactional时,可以指定如下方法:
a、isolation:用于指定事务的隔离级别。默认为底层事务的隔离级别。
b、noRollbackFor:指定遇到指定异常时强制不回滚事务。
c、noRollbackForClassName:指定遇到指定多个异常时强制不回滚事务。该属性可以指定多个异常类名。
d、propagation:指定事务的传播属性。
e、readOnly:指定事务是否只读。
f、rollbackFor:指定遇到指定异常时强制回滚事务。
g、rollbackForClassName:指定遇到指定多个异常时强制回滚事务。该属性可以指定多个异常类名。
h、timeout:指定事务的超时时长。
具体设置如下:
事物超时设置:
@Transactional(timeout=30) //默认是30秒
事务隔离级别:
@Transactional(isolation = Isolation.READ_UNCOMMITTED)读取未提交数据(会出现脏读, 不可重复读) 基本不使用
@Transactional(isolation = Isolation.READ_COMMITTED)读取已提交数据(会出现不可重复读和幻读)@Transactional(isolation = Isolation.REPEATABLE_READ)可重复读(会出现幻读)@Transactional(isolation = Isolation.SERIALIZABLE)串行化
MYSQL: 默认为REPEATABLE_READ级别SQLSERVER: 默认为READ_COMMITTED
事务传播属性:
@Transactional(propagation=Propagation.REQUIRED) //如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) //容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) //不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) //必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) //必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) //如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
@Transactional(propagation=Propagation.NESTED)
遇到异常回滚:
@Transactional(rollbackFor=java.lang.Exception) //指定回滚,遇到异常
遇到异常不回滚:
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期
注意的几点:
1 @Transactional 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2 、用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException("注释");)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception("注释");)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚:要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .如果让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
如下:
@Transactional(rollbackFor=Exception.class) //指定回滚,遇到异常Exception时回滚
public void methodName() {
throw new Exception("注释");
}
@Transactional(noRollbackFor=Exception.class)//指定不回滚,遇到运行期例外(throw new RuntimeException("注释");)会回滚
public ItimDaoImpl getItemDaoImpl() {
throw new RuntimeException("注释");
}
关务事务的隔离级别的说明:
数据库系统提供了四种事务隔离级别供用户选择。不同的隔离级别采用不同的锁类型来实现,在四种隔离级别中,Serializable的隔离级别最高,Read Uncommited的隔离级别最低。大多数据库默认的隔离级别为Read Commited,如SqlServer,当然也有少部分数据库默认的隔离级别为Repeatable Read ,如Mysql
Read Uncommited:读未提交数据(会出现脏读,不可重复读和幻读)。
Read Commited:读已提交数据(会出现不可重复读和幻读)
Repeatable Read:可重复读(会出现幻读)
Serializable:串行化
脏读:一个事务读取到另一事务未提交的更新新据。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。换句话说就是,后续读取可以读到另一事务已提交的更新数据。相反,“可重复读”在同一事务中多次读取数据时,能够保证所读数据一样,也就是,后续读取不能读到另一事务已提交的更新数据。
幻读:一个事务读取到另一事务已提交的insert数据。
这些事务隔离级别可以去看spring源码 : org.springframework.transaction.annotation.Isolation
(用时,导入org.springframework.transaction.annotation.Isolation,再在Transactional括号里用如isolation = Isolation.DEFAULT)
关于事务传播属性的说明:
REQUIRED: 业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为.
SUPPORTS:如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行.
MANDATORY:只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常
REQUIRES_NEW:业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行.
NOT_SUPPORTED:声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行.
NEVER:声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效.
这些事务传播属性可以去看spring源码 : org.springframework.transaction.annotation.Propagation
(用时,导入org.springframework.transaction.annotation.Propagation,再在Transactional括号里用如propagation = Propagation.REQUIRED)
常用事务注释罗列:
@Transactional (propagation = Propagation.REQUIRED,readOnly=true) //readOnly=true只读,不能更新,删除
@Transactional (propagation = Propagation.REQUIRED,timeout=30)//设置超时时间
@Transactional (propagation = Propagation.REQUIRED,isolation=Isolation.DEFAULT)//设置数据库隔离级别
//事务传播属性
@Transactional(propagation=Propagation.REQUIRED) //如果有事务,那么加入事务,没有的话新建一个(不写的情况下)
@Transactional(propagation=Propagation.NOT_SUPPORTED) //容器不为这个方法开启事务
@Transactional(propagation=Propagation.REQUIRES_NEW) //不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务
@Transactional(propagation=Propagation.MANDATORY) //必须在一个已有的事务中执行,否则抛出异常
@Transactional(propagation=Propagation.NEVER) //必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)
@Transactional(propagation=Propagation.SUPPORTS) //如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.
@Transactional 的所有可选属性如下:
属性 类型 默认值 说明
propagation Propagation枚举 REQUIRED 事务传播属性 (下有说明)
isolation isolation枚举 DEFAULT 事务隔离级别 (另有说明)
readOnly boolean false 是否只读
timeout int -1 超时(秒)
rollbackFor Class[] {} 需要回滚的异常类
rollbackForClassName String[] {} 需要回滚的异常类名
noRollbackFor Class[] {} 不需要回滚的异常类
noRollbackForClassName String[] {} 不需要回滚的异常类名
其实他们还可以混合使用,方式三和方式四的混合使用:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<!-- 其他省略
********************
AOP通知
********************
-->
<bean id="exception" class="com.etc.advisor.ExceptionAdvisor"></bean>
<bean id="tx" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<constructor-arg index="0">
<ref bean="tm"/>
</constructor-arg>
<constructor-arg index="1">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</constructor-arg>
</bean>
<aop:config proxy-target-class="true">
<aop:pointcut id="p1" expression="(execution(* com.etc.action.*.execute()))" />
<aop:pointcut id="p2" expression="(execution(* com.etc.*.*.*(..)))" />
<aop:pointcut id="p3" expression="(execution(* com.etc.service.*.*(..)))" /><!-- service的所有方法 -->
<aop:advisor advice-ref="tx" pointcut-ref="p1"/>
<aop:advisor advice-ref="exception" pointcut-ref="p2"/>
</aop:config>
</beans>
三、Spring事务配置示例
步骤一、配置文件(applicationContext.xml):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
<bean id="userservice" class="com.sunflower.yuan.serviceimp.UserServiceImp">
<property name="jdbcTemplate">
<ref bean="jdbcTemplate"/>
</property>
</bean>
<bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/mytest"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="datasource"/>
</property>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource">
<ref bean="mydatasource"/>
</property>
</bean>
<bean id="transactionAttributeSource" class="org.springframework.transaction.interceptor.
NameMatchTransactionAttributeSource">
<property name="properties">
<props>
<prop key="getMoney">
PROPAGATION_REQUIRED,ISOLATION_DEFAULT,-NoMoneyException
</prop>
</props>
</property>
</bean>
<!-- 声明式事务管理的代理类 -->
<bean id="userTransaction"
class="org.springframework.transaction.interceptor. TransactionProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>com.sunflower.yuan.servicedao.UserService</value>
</list>
</property>
<property name="target">
<ref bean="userservice"/>
</property>
<property name="transactionManager">
<ref bean="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<ref bean="transactionAttributeSource"/>
</property>
</bean>
</beans>
声明式事务管理也是用动态代理模式来实现的,思想和AOP是一样的。主要是配置org.springframework.transaction.interceptor.TransactionProxyFactoryBean类。这个配置和AOP的配置非常相似。可以将事务属性单独写在org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource中,然后再用<property name="transactionAttributeSource">引入,也可以直接用<property name="transactionAttributes">配置事务属性。
<prop key="getMoney">标签中的key="getMoney"表示对getMoney方法进行事务管理,也可使用通配符get*,表示对所有get前缀的方法进行事务管理。
事务属性的配置用","隔开,如下所示:
PROPAGATION, ISOLATION, readonly, -Exception, +Exception
PROPAGATION: 传播行为
ISOLATION: 隔离级别
readonly: 是否为只读事务(可选)
(+/-)Exception: 回滚规则。如果是"-",遇到指定的Exception,事务回滚,如果是"+",事务不回滚。
UserService.java:
public interface UserService {
public void getMoney(int money, int id) throws NoMoneyException;
}
UserServiceImp.java:
package com.sunflower.yuan.serviceimp;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import com.sunflower.yuan.Exception.NoMoneyException;
import com.sunflower.yuan.servicedao.UserService;
public class UserServiceImp implements UserService {
private JdbcTemplate jdbcTemplate;
public JdbcTemplate getJdbcTemplate() {
return jdbcTemplate;
}
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public void getMoney(int getMoney, int id) throws NoMoneyException {
String sql = "select money from user where id=?";
Object[] params = new Object[] { new Integer(id) };
MyRowCallbackHandler rowCallBack = new MyRowCallbackHandler();
jdbcTemplate.query(sql, params, rowCallBack);
if (rowCallBack.getMoney() > getMoney) {
sql = "update user set money=? where id=?";
Object[] params2 = new Object[] {
rowCallBack.getMoney() - getMoney, new Integer(id) };
jdbcTemplate.update(sql, params2);
throw new NoMoneyException("余额不足");
}
}
class MyRowCallbackHandler implements RowCallbackHandler {
private int money = 0;
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
@Override
public void processRow(ResultSet rs) throws SQLException {
this.money = rs.getInt("money");
}
}
}
这里要注意一点,要进行事务管理的方法,必须在方法外进行异常的抛出,这样事务管理器才能接收到,然后进行事务的回滚。如果用try-catch处理异常,将不会进行事务回滚。