Spring入门5.事务管理机制 20131126
代码下载 : 链接: http://pan.baidu.com/s/1kYc6c 密码: 233t
回顾之前的知识,Spring 最为核心的两个部分IoC和AOP,我们已经学习过了,其中IoC是Spring的基础核心,AOP是建立在IoC之上的,难点是AOP的理解和配置方式。其实AOP编程是一种编程模型,他是站在动态的角度思考程序的运行过程。Spring本身通过IoC容器实现了AOP的思想,但是AspectJ是一种更加专业的主流的AOP编程思想的实现,他站在Java代码级别上去实现了AOP的编程思想,Spring借鉴了AspectJ的AOP,将他整合在Spring中,使用注解的方式于AspectJ的融合。
这一节学习Spring中的事务管理:对于事务的理解就是整个操作时原子性的,要么全部做完,要么一点也不做。Spring对于事务处理有着良好的支持,Spring不仅提供了和底层事务员源无关的事物抽象,还提供了声明式事务,这样使代码开发人员从事务处理的底层代码中解脱出来,专注于业务的处理过程
1.Spring事务策略
JavaEE中事务策略有全局事务和局部事务。全局事务一般由应用服务器管理,需要底层应用服务器的JTA支持,EJB事务就是建立在JTA的基础之上,而JTA又需要通过JNDI获取,这样就意味着无论用户的应用是跨多个事务性资源的使用还是单一的事物性资源的使用,EJB都要求使用全局事务加以处理,这样基于EJB的事务就无法脱离应用服务器的环境。
局部事务是基于单一事务性资源的,通常和底层的持久化技术有关,比如:当采取JDBC时,需要使用Connection对象操作事务;当使用Hibernate持久化技术的是偶,就需要使用session对象操作事务。当使用局部事务的时候,应用服务器不需要参与事务管理的,因此不保证扩多个事务性资源的事物的正确性,但是绝大多数的事物是基于单一事务性资源,只有很少的应用需要使用到多事务性资源的JTA事务。
在单一事务性资源的情况之下,Spring直接使用底层的数据源管理事务。在面对多个事务性资源的时候,Spring会寻求JavaEE应用服务器的支持,通过引用服务器的JNDI资源完成JTA事务。(当然也可以脱离JavaEE应用服务器的支持使用JTA事务,比如使用JOTM项目,Spring也可以完成JTA的配置)
1.1PlatformTransactionManager
Spring的事务策略是通过PlatformTransactionManager接口来体现的,该接口是Spring事务策略的核心,他根据TransactionDefinition提供的事务属性配置信息创建事务,并使用TransactionStatus来描述事务激活的状态。
在PlatformTransactionManager接口中定义的方法,没有雨JNDI绑定,可以向Spring普通的bean一样对待PlatformTransactionManager的实现类,接口方法如下:
Transaction getTransaction(TransactionDefinition def);根据事务定义信息从事务环境中返回一个已经存在的事务,或者是创建一个新的事务,并且使用TransactionStatus来描述这个事务的状态。
void commit(TransactionStatus status);根据事务的状态提交事务,如果事务已经被标识为rollback-only,该方法将执行一个回滚事务的操作。
void rollback(TransactionStatus status);将事务回滚,当commit方法抛出异常的时候,rollback方法将会被隐式的调用,回滚事务。
更进一步,Spring会将事务的管理委托给底层的持久化实现框架完成,因此Spring为不同的持久化框架提供了PlatformTransactionManager接口的实现类:
org.springframework.orm.jpa.JpaTransactionManager: 使用JPA进行持久化,使用这个事务管理器;
org.springframework.orm.hibernate3.HibernateTransactionManager:使用的是Hibernate3进行持久化,使用这一个事务管理器
org.springframework.jdbc.datasource.DataSourceTransactionManager使用Spring JDBC或者是iBatis等数据源的持久化技术,使用这一个事务管理器
org.springframework.orm.toplink.ToplinkTransactionManager: 使用Toplink进行持久化的时候使用
org.springframework.transaction.jta.JtaTransactionManager:具有多个数据源的全局事务使用
这样就可以通过Spring提供的高级抽象对不同种类的事物实现相同方式的代理,而不必关心具体的实现。
1.2TransactionDefinition
TransactionDefinition定义的Spring兼容的事务属性,这些属性对于事务管理的控制的若干方法进行配置没比如事务隔离级别、事务传播、事务超时和制度状态(都是新的名词,不要害怕,我也是第一次接触这些名词)
事务隔离级别:当前的事物和其他事物的隔离程度。比如当前事务能否看到其他事物未提交的数据。TransactionDefinition定义的事务隔离级别:
ISOLATION_READ_UNCOMMITED:读未提交,一个事务在执行过程中可以看到其他事物没有提交的新插入记录,而且能够看到其他事务,没有提交对已有记录的更新。
ISOLATION_READ_COMMITED: 读已提交
ISOLATION_REPEATABLE_READ:可重复读,一个事务在执行过程中可以看到其他事务已经提交的新插入记录,但是不能够看到其他事务对已有记录的更新。
ISOLATION_SERIALIZABLE:序列化读,一个事务只可以操作在该事务提交之间已经提交的数据,并且可以在该事务中操作这些数据。
ISOLATION_DEFAULT:表示使用底层数据库默认的隔离级别。
事务传播:一般在事务中执行的代码都会在当前事务中运行。但是如果一个事务上下文已经存在,有几个选项可以指定该事务性方法的执行行为。比如可以简单地在现有事务上下文中运行或者挂起现有事务,创建一个新的事务。
Spring在TransactionDefinition接口中规定了7中类型的事务传播行为,它规定了事务方法和事务方法发生嵌套调用时事务如何进行传播:
PROPAGATION_REQUIRED
PROPAGATION_SUPPORTS:
PROPAGATION_MANDATONRY
PROPAGATION_REQUIRES_NEW
PROPAGATION_NOT_SUPPORTED
PROPAGATION_NEVER
PROPAGATION_NESTED
通过TransactionDefinition接口中的getPropagationBehavior()方法可以获得事务的传播性
事务超时:表示事务在超时之前可以运行多久,也就是事务的最长续航时间。如果事务一直处于未提交或者是回滚状态,那么当超出时间的时候,事务将会自动回滚事务。通过TransactionDefinition接口中的getTimeout()获得事务的超时时间。
只读状态:只读事务是不会修改任何数据的,只读事务对于优化十分重要,通过TransactionDefinition的isReadOnly()接口判断一个事务是否是只读的事务。
1.3TransactionStatus
代表一个事务的运行状态。事务管理器通过该接口获得事务的运行期状态,也可以通过该接口间接的回滚事务,他相比于在抛出异常在回滚事务的方式更加具有可控性。
boolean hasSavePoint()
boolean isNewTransaction()
boolean isCompleted(); 已经回滚或者是提交
boolean isRollbackOnly();
void setRolbackOnly()
一点提醒:实际开发中对于PlatformTransactionManager、TransactionDefinition、TransactionStatus三个接口,开发者不会直接使用,而是通过配置的方式使用Spring提供的子类。了解这些知识点有利于帮助我们理解Spring中的事务机制。
2.使用XML的方式配置声明式事务
事务管理的方式有两种:编程式和声明式。编程式事务管理需要在代码中手动编写和事务相关的操作代码,而对于声明式事务则无需再Java程序中编写任何和事务相关的代码。
大多数的Spring选择的是声明式的事物管理功能,这种方式对于代码的侵入性比较小,可以让事务管理的逻辑完全从业务逻辑中移除,比较轻量级。Spring声明式事务管理是通过Spring AOP实现的,通过事务的声明性信息,Spring负责将事务管理Advice逻辑动态织入到业务方法对应的连接点上。这些逻辑包括获取线程绑定资源、开始事务、提交回滚事务,进行异常转换等。
例子:以JDBC数据源为例
2.1配置数据源以及事务管理对象
使用XML配置DBCP数据源,同时配置Spring提供的事物管理对象。
<!-- 数据源配置信息 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="221256"/>
<property name="initialSize" value="5"/>
<property name="maxActive" value="100"/>
<property name="maxIdel" value="30"/>
<property name="maxWait" value="1000"/>
</bean>
<!-- 事务管理器配置信息 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
2.2配置事务的Advice
在Spring中事务的Advice是有<tx:advice/>元素进行配置的。在该元素中,可以针对每一个方法或者是每一批方法配置事务的Advice
<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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
default-lazy-init="true">
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 事务属性定义 -->
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="add*" rollback-for="Exception"/>
<tx:method name="update*" />
<tx:method name="del*"/>
</tx:attributes>
</tx:advice>
在上述配置中,首先是引入命名空间的声明,然后使用<tx:advice>元素配置事务的Advice,在<tx:advice>中使用<tx:attributes>元素通过配置<tx:method/>子元素为一批方法指定所需要的语义,包括事务传播属性、事务隔离级别、事务超时等等。
<tx:advice>中id是事务Advice的标识;transaction-manager是指定已经配置的事物管理器,如果事务管理器的id是transactionManager,则可以省略。配置<tx:advice>元素的重点是配置<tx:method>子元素,拓为一批方法指定了所需的事务原语,比如事务传播属性、事务隔离属性、事务超时属性、只读属性、对于指定异常回滚和指定异常不回滚,下面是<tx:method>的各种属性:
name 可以使用通配符
propagation 默认是REQUIRED,可选的值有REQUIRED SUPPRTS MANDATORY REQUIRES_NEW NOT_SUPPORTED NEVER NESTED 表示的是传播
isolation: 默认是DEFAULT 表示事务的隔离级别
timeout defalur 是-1,单位秒,表示永不超时
read-only: default false 只读事务,便于查询优化
rollback-for: 所有运行期异常回滚,可以有多个Exception,逗号隔开
no-rollback-for: 所有检查性异常不回滚
2.3配置事务增强切面
通过事务Advice Aspect的配置实现方法级别的事务管理
<!-- 通过AOP配置事务增强切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.yang.service.USerService.*(..))" id="allMethods"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allMethods"/>
</aop:config>
上述配置,配置了一个id是allMethods的切入点,匹配UserService下面的所有方法,然后使用一个<aop:advice>把切入点和事务增强txAdvice绑定在一起,当allMethods执行的时候,txAdvice定义的增强将被织入特定的连接点。
其实在标准的AOP中是没有Advisor的概念的,Advisor实际上是表示的切面的概念,它同时包含横切代码和连接点信息。<aop:advisor>的作用就是讲增强和切入点绑定在一起,保证增强所包含的很企鹅逻辑在特定的连接点被织入。
2.4运行结果
使用事务机制,向数据库中插入相同的两行记录,这样就会存在两行数据都无法出入导数据库中,体现事务的原子性操作。
UserDao.java
public interface UserDao {
public void add();
}
UserDaoImpl.java
public class UserDaoImpl implements UserDao {
DataSource dataSource;// 数据库连接接口,用于初始化JdbcTemplate,他是Spring中的JDBC的核心类之一
public DataSource getDataSource(){
return this.dataSource;
}
public void setDataSource(DataSource dataSource){
this.dataSource = dataSource;
}
@Override
public void add() {
// 因为该函数是基于事务机制执行的,所以执行的时候会发生错误,两条记录都不会插入到数据库中。
JdbcTemplate template = new JdbcTemplate(this.dataSource);
template.execute("insert into user (id, username,password) values ( 3, 'yangtengfei','34567')");
template.execute("insert into user (id, username,password) values ( 4, 'yangtengfei','456789')");
}
}
同时需要在applicationContext.xml配置文件中使用IoC的机制生命userDao和userService的Bean:
<!-- 配置UserService Class -->
<bean id="userDao" class="com.yang.dao.impl.UserDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userService" class="com.yang.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
代码整理:
UserDao.java//数据库的接口
UserDaoImpl.java//数据库接口实现类
UserService.java//用户的服务接口
UserServiceImpl.java//用户Service接口的实现类
applicationContext.xml//配置文件
TestMain.java//项目入口函数
同时需要添加JAR包的支持:Spring3 common mysql AspectJ的包。
最重要的是配置文件: 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: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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"
default-lazy-init="true">
<aop:aspectj-autoproxy/>
<!-- 数据源配置信息 -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="221256"/>
<property name="initialSize" value="5"/>
<property name="maxActive" value="100"/>
<property name="maxIdle" value="30"/>
<property name="maxWait" value="1000"/>
</bean>
<!-- 配置UserService Class -->
<bean id="userDao" class="com.yang.dao.impl.UserDaoImpl">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userService" class="com.yang.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
<!-- 事务管理器配置信息 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="txManager">
<!-- 事务属性定义 -->
<tx:attributes>
<tx:method name="get*" read-only="true"/>
<tx:method name="add*" rollback-for="Exception"/>
<tx:method name="update*" />
<tx:method name="del*"/>
</tx:attributes>
</tx:advice>
<!-- 通过AOP配置事务增强切面 -->
<aop:config>
<aop:pointcut expression="execution(* com.yang.service.UserService.*(..))" id="allMethods"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="allMethods"/>
</aop:config>
</beans>
YangTengfei
2013.11.27