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
="transactionBaseProxy"
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
="transactionBaseProxy"
>
<
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 只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.
2 默认情况下,一个有事务方法, 遇到RuntiomeException 时会回滚 . 遇到 受检查的异常 是不会回滚 的. 要想所有异常都回滚,要加上 @Transactional( rollbackFor={Exception.class,其它异常}) .
@Transactional 的所有可选属性如下:
属性 |
类型 |
默认值 |
说明 |
propagation |
Propagation枚举 |
REQUIRED |
事务传播属性 (下有说明) |
isolation |
isolation枚举 |
DEFAULT |
事务隔离级别 (另有说明) |
readOnly |
boolean |
false |
是否只读 |
timeout |
int |
-1 |
超时(秒) |
rollbackFor |
Class[] |
{} |
需要回滚的异常类 |
rollbackForClassName |
String[] |
{} |
需要回滚的异常类名 |
noRollbackFor |
Class[] |
{} |
不需要回滚的异常类 |
noRollbackForClassName |
String[] |
{} |
不需要回滚的异常类名 |
事务的隔离级别 有如下可选:
可以去看spring源码 : org.springframework.transaction.annotation.Isolation
我的"隔离级别"相关文章 有不明白的,可以去看看.
DEFAULT |
采用数据库默认隔离级别 |
READ_UNCOMMITTED |
请看"隔离级别"相关文章 |
READ_COMMITTED |
请看"隔离级别"相关文章 |
REPEATABLE_READ |
请看"隔离级别"相关文章 |
SERIALIZABLE |
请看 "隔离级别"相关文章 |
事务的传播属性 ,有如下可选
可以去看spring源码 : org.springframework.transaction.annotation.Propagation
REQUIRED |
业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为. |
SUPPORTS |
如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行. |
MANDATORY |
只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常 |
REQUIRES_NEW |
业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行. |
NOT_SUPPORTED |
声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行. |
NEVER |
声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行. |
NESTED |
如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务, 这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效. |
|
隔离级别:
数据库提供了四种事务隔离级别, 不同的隔离级别采用不同的锁类开来实现.
在四种隔离级别中, Serializable的级别最高, Read Uncommited级别最低.
大多数数据库的默认隔离级别为: Read Commited,如Sql Server , Oracle.
少数数据库默认的隔离级别为Repeatable Read, 如MySQL InnoDB存储引擎
即使是最低的级别,也不会出现 第一类 丢失 更新问题 .
- Read Uncommited :读未提交数据( 会出现脏读,不可重复读,幻读 ,避免了 第一类丢失 更新 )
- Read Commited :读已提交的数据(会出现不可重复读,幻读)
- Repeatable Read :可重复读(会出现幻读)
- Serializable :串行化
丢失 更新 :
当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题。每个事务都不知道其它事务的存在。最后的更新将重写由其它事务所做的更新,这将导致数据丢失。
例:
事务A和事务B同时修改某行的值,
1.事务A将数值改为1并提交
2.事务B将数值改为2并提交。
这时数据的值为2,事务A所做的更新将会丢失。
解决办法:对行加锁,只允许并发一个更新事务。
脏读: 一个事务读到另一个事务未提交的更新数据
例:
1.Mary的原工资为1000, 财务人员将Mary的工资改为了8000(但未提交事务)
2.Mary读取自己的工资 ,发现自己的工资变为了8000,欢天喜地!
3.而财务发现操作有误,回滚了事务,Mary的工资又变为了1000, 像这样,Mary记取的工资数8000是一个脏数据。
不可重复读: 在同一个事务中,多次读取同一数据,返回的结果有所不同. 换句话说就是,后续读取可以读到另一个事务已提交的更新数据. 相反"可重复读"在同一事务多次读取数据时,能够保证所读数据一样,也就是后续读取不能读到另一事务已提交的更新数据.
例:
1.在事务1中,Mary 读取了自己的工资为1000,操作并没有完成
2.在事务2中,这时财务人员修改了Mary的工资为2000,并提交了事务.
3.在事务1中,Mary 再次读取自己的工资时,工资变为了2000
解决办法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。
幻读: 一个事务读取到另一个事务已提交的insert数据.
例:
第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时 (此时第一事务还未提交) ,第二个事务向表中插入一行新数据。这时第一个事务再去读取表时,发现表中还有没有修改的数据行,就好象发生了幻觉一样。