前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识。通过这次的学习发觉Spring的事务配置只要把思路理清,还是比较好掌握的。
总结如下:
Spring配置文件中关于事务配置总是由三个组成部分,分别是DataSource、TransactionManager和代理机制这三部分,无论哪种配置方式,一般变化的只是代理机制这部分。
DataSource、TransactionManager这两部分只是会根据数据访问方式有所变化,比如使用Hibernate进行数据访问时,DataSource实际为SessionFactory,TransactionManager的实现为HibernateTransactionManager。
具体如下图:
根据代理机制的不同,总结了五种Spring事务的配置方式,配置文件如下:
第一种方式:每个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:context
="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-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
-->
<
bean
id
="userDaoTarget"
class
="com.bluesky.spring.dao.UserDaoImpl"
>
<
property
name
="sessionFactory"
ref
="sessionFactory"
/>
</
bean
>
<
bean
id
="userDao"
class
="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
>
<!--
配置事务管理器
-->
<
property
name
="transactionManager"
ref
="transactionManager"
/>
<
property
name
="target"
ref
="userDaoTarget"
/>
<
property
name
="proxyInterfaces"
value
="com.bluesky.spring.dao.GeneratorDao"
/>
<!--
配置事务属性
-->
<
property
name
="transactionAttributes"
>
<
props
>
<
prop
key
="*"
>
PROPAGATION_REQUIRED
</
prop
>
</
props
>
</
property
>
</
bean
>
</
beans
>
第二种方式:所有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:context
="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-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
="transactionBase"
class
="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"
lazy-init
="true"
abstract
="true"
>
<!--
配置事务管理器
-->
<
property
name
="transactionManager"
ref
="transactionManager"
/>
<!--
配置事务属性
-->
<
property
name
="transactionAttributes"
>
<
props
>
<
prop
key
="*"
>
PROPAGATION_REQUIRED
</
prop
>
</
props
>
</
property
>
</
bean
>
<!--
配置DAO
-->
<
bean
id
="userDaoTarget"
class
="com.bluesky.spring.dao.UserDaoImpl"
>
<
property
name
="sessionFactory"
ref
="sessionFactory"
/>
</
bean
>
<
bean
id
="userDao"
parent
="transactionBase"
>
<
property
name
="target"
ref
="userDaoTarget"
/>
</
bean
>
</
beans
>
第三种方式:使用拦截器
<?
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:context
="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-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
="*"
>
PROPAGATION_REQUIRED
</
prop
>
</
props
>
</
property
>
</
bean
>
<
bean
class
="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
>
<
property
name
="beanNames"
>
<
list
>
<
value
>
*Dao
</
value
>
</
list
>
</
property
>
<
property
name
="interceptorNames"
>
<
list
>
<
value
>
transactionInterceptor
</
value
>
</
list
>
</
property
>
</
bean
>
<!--
配置DAO
-->
<
bean
id
="userDao"
class
="com.bluesky.spring.dao.UserDaoImpl"
>
<
property
name
="sessionFactory"
ref
="sessionFactory"
/>
</
bean
>
</
beans
>
第四种方式:使用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:context
="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-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"
>
<
context:annotation-config
/>
<
context:component-scan
base-package
="com.bluesky"
/>
<
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
>
<
tx:advice
id
="txAdvice"
transaction-manager
="transactionManager"
>
<
tx:attributes
>
<
tx:method
name
="*"
propagation
="REQUIRED"
/>
</
tx:attributes
>
</
tx:advice
>
<
aop:config
>
<
aop:pointcut
id
="interceptorPointCuts"
expression
="execution(* com.bluesky.spring.dao.*.*(..))"
/>
<
aop:advisor
advice-ref
="txAdvice"
pointcut-ref
="interceptorPointCuts"
/>
</
aop:config
>
</
beans
>
第五种方式:全注解
<?
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:context
="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context-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"
>
<
context:annotation-config
/>
<
context:component-scan
base-package
="com.bluesky"
/>
<
tx:annotation-driven
transaction-manager
="transactionManager"
/>
<
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
>
</
beans
>
此时在DAO上需加上@Transactional注解,如下:
package
com.bluesky.spring.dao;
import
java.util.List;
import
org.hibernate.SessionFactory;
import
org.springframework.beans.factory.annotation.Autowired;
import
org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import
org.springframework.stereotype.Component;
import
com.bluesky.spring.domain.User;
@Transactional
@Component(
"
userDao
"
)
public
class
UserDaoImpl
extends
HibernateDaoSupport
implements
UserDao {
public
List
<
User
>
listUsers() {
return
this
.getSession().createQuery(
"
from User
"
).list();
}
}
注:1、一个类中一个方法调用另一个方法有事务的方法,事务是不会起作用的。也就是说:最底层的方法如果设为@Transactional,那么所有调用它的方法必须也是@Transactional,否则事务不会起作用。
2、默认情况下,@Transactional 方法中,在抛出unchecked异常时,数据库会回滚;在抛出checked异常时,数据库不会回滚。当然也可以特别引起指定数据库回滚的异常,如:
@Transactional(rollBackFor=Exception.class) : method1对于Exception类的异常都会回滚
public void method1(){
......
}
@Transactional(propagation=Propagation.NOT_SUPPORTED) :method2取消了事务控制
public void method2(){
.....
}
3、 在需要事务管理的地方加@Transactional 注解。@Transactional 注解可以被应用于接口定义和接口方法、类定义和类的 public 方法上。
4、@Transactional 注解只能应用到 public 可见度的方法上。 如果你在 protected、private 或者 package-visible 的方法上使用 @Transactional 注解,它也不会报错, 但是这个被注解的方法将不会展示已配置的事务设置。
5.、注意仅仅 @Transactional 注解的出现不足于开启事务行为,它仅仅 是一种元数据。必须在配置文件中使用配置元素,才真正开启了事务行为。
6、 通过 元素的 "
proxy-target-class" 属性值来控制是基于接口的还是基于类的代理被创建。如果 "proxy-target-class" 属值被设置为 "true",那么基于类的代理将起作用(这时需要CGLIB库cglib.jar在CLASSPATH中)。如果 "proxy-target-class" 属值被设置为 "false" 或者这个属性被省略,那么标准的JDK基于接口的代理将起作用。
标准的JDK基于接口的代理将起作用
proxy-target-class="false"/>
基于类的代理将起作用 ,同时 cglib.jar必须在CLASSPATH中
proxy-target-class="true"/>
非JTA事务(即非分布式事务), 事务配置的时候 ,需要指定dataSource属性(非分布式事务,事务是在数据库创建的链接上开启。)
JTA事务(非分布式事务), 事务配置的时候 ,不能指定dataSource属性(分布式事务,是有全局事务来管理数据库链接的)
7、注解@Transactional cglib与java动态代理最大区别是代理目标对象不用实现接口,那么注解要是写到接口方法上,要是使用cglib代理,这是注解事物就失效了,为了保持兼容注解最好都写到实现类方法上。
8、 Spring团队建议在具体的类(或类的方法)上使用 @Transactional 注解,而不要使用在类所要实现的任何接口上。在接口上使用 @Transactional 注解,只能当你设置了基于接口的代理时它才生效。因为注解是 不能继承 的,这就意味着如果正在使用基于类的代理时,那么事务的设置将不能被基于类的代理所识别,而且对象也将不会被事务代理所包装。