Spring中的事务属性介绍以及声明式事务管理

来源:互联网 发布:通讯录管理系统c语言 编辑:程序博客网 时间:2024/06/15 11:55

 Spring中的事务管理主要有编程式事务管理和声明式事务管理,这里主要介绍声明式事务管理。在Spring中,主要是通过AOP来完成声明式的事务管理。要了解Spring对事务的支持,首先要了解事务的属性。在Spring中事务属性主要分以下几方面:
    1、传播行为(Propagation behavior)
       可以找到相对应的常数与说明,列出下列几个:
       PROPAGATION_MANDATORY:方法必须在一个现存的事务中进行,否则丢出异常
       PROPAGATION_NESTED:在一个嵌入的事务中进行
       PROPAGATION_NEVER:不应在事务中进行,如果有则丢异常
       PROPAGATION_NOT_SUPPORTED:不应再事务中进行,如果有就暂停现存的事务
       PROPAGATION_REQUIRED:支持现在的事务,如果没有就建立一个新的事务
       PROPAGATION_REQUIRES_NEW:建立一个新的事务,如果现存一个事务就暂停它
       PROPAGATION_SUPPORTS:支持现在的事务,如果没有就以非事务的方式执行
   
    2、隔离层级(Isolation level)
       在一个应用程序中,可能有多个事务在同时进行,这些事务应当彼此之间互不知道另一个事务的存在,比如现在整个应用程序就只有一个事务存在,由于事务彼此之间独立,若读取的是同一个数据的话,就容易发生问题,比如:
       Dirty read(脏读):某个事务已经更新了一份数据,另一份事务在此时读取了同一份数据,由于某些原因,前一个事务回滚了,则后一个事务读取的数据则是错误的。
       Non-repeatable read(非重复读):在一个事务的两次查询中事务不一致,可能是因为两次查询过程中间插入了一个事务更新的原有数据。
       Phantom read(幻象读):在一个事务的两次查询中数据笔数不一致。

解决以上问题的方法之一,就是在某个事务进行过程中锁定正在更新或查询的数据,但是这样会造成效率上的问题,别的事务必须等待当前事务解锁后才能进行。然而,根据需求的不同,并不用在事务进行时完全的锁定数据,隔离层级可以让您根据实际的需求,对数据的锁定进行设置。一下是几个隔离层级的参数说明:

       ISOLATION_DEFAULT:使用底层数据库预设的隔离层级
       ISOLATION_READ_COMMITTED:运行事务读取其他事务已经提交的数据字段,可以防止脏读问题
       ISOLATION_READ_UNCOMMITTED:运行事务读取其他并行事务还没有提交的数据,会发生脏读、非重复读、幻象读等问题
       ISOLATION_REPEATABLE_READ:要求多次读取的数据必须相同,除非事务本身更新数据,可以防止脏读、非重复读等问题
       ISOLATION_SERIALIZABLE:完整的隔离层级,防止所有问题,会锁定数据对应的表,有效率问题

事实上,对于事务的传播特性,可以设置对应的隔离层级。在Spring中,我们用的最多的就是PROPAGATIOIN_REQUIRED这种传播行为。这个意思是,如果应用程序中已经存在一个事务了,当另一个事务进来时,会加入到这个事务中,如果没有事务存在,则开启一个新的事务。

请看一下对事务传播特性设置的配置文件

Java代码 复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2.   
  3. <beans xmlns="http://www.springframework.org/schema/beans"  
  4.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.         xmlns:aop="http://www.springframework.org/schema/aop"  
  6.         xmlns:tx="http://www.springframework.org/schema/tx"  
  7.         xsi:schemaLocation="   
  8.             http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  9.             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd   
  10.             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">   
  11.   
  12.     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">   
  13.         <property name="configLocation">   
  14.             <value>classpath:hibernate.cfg.xml</value>   
  15.         </property>      
  16.     </bean>   
  17.        
  18.     <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">   
  19.         <property name="sessionFactory" ref="sessionFactory" />   
  20.     </bean>   
  21.        
  22.     <tx:advice id="txAdvice" transaction-manager="transactionManager">   
  23.         <tx:attributes>   
  24.             <tx:method name="insert*" propagation="REQUIRED" />   
  25.             <tx:method name="del*" propagation="REQUIRED"/>   
  26.             <tx:method name="update*" propagation="REQUIRED"/>   
  27.             <tx:method name="*" read-only="true"/>   
  28.         </tx:attributes>   
  29.     </tx:advice>    
  30.        
  31.     <aop:config>   
  32.         <aop:pointcut id="allDaoMethod" expression="execution (* org.whatisjava.dao..*.*(..))"/>   
  33.         <aop:advisor advice-ref="txAdvice" pointcut-ref="allDaoMethod"/>   
  34.     </aop:config>   
  35.     </beans>  


       
在<tx:method>中的属性设置,对于传播行为、隔离层级、只读、超时等,都有对应的"propagation"、"isolation"、"timeout"、"read-only"等等,这里设置的传播属性是"REQUIRED",则它对应的默认的隔离层级就是"DEFAULT","timeout"默是"-1","read-only"默认是"false"。我们也可以根据这些参数来选取不同的参数设置,比如

Java代码 复制代码
  1. ...   
  2. <tx:advice id="txAdvice" transaction-manager="transactionManager">   
  3.         <tx:attributes>   
  4.             <tx:method name="insert*" propagation="REQUIRED" isolation="SERIALIZABLE"/>   
  5.             <tx:method name="del*" propagation="REQUIRED" isolation="SERIALIZABLE"/>   
  6.             <tx:method name="update*" propagation="REQUIRED" isolation="SERIALIZABLE"/>   
  7.             <tx:method name="*" read-only="true"/>   
  8.         </tx:attributes>   
  9.     </tx:advice>    
  10. ....  




这是基于xml配置文件进行的事务属性的传播控制,也可以基于注解方式的,请看如下代码

Java代码 复制代码
  1. package org.whatisjava.dao.impl;   
  2.   
  3. import java.util.List;   
  4.   
  5. import org.springframework.orm.hibernate3.support.HibernateDaoSupport;   
  6. import org.springframework.transaction.annotation.Propagation;   
  7. import org.springframework.transaction.annotation.Transactional;   
  8. import org.whatisjava.dao.UserDao;   
  9. import org.whatisjava.po.User;   
  10.   
  11. public class UserDaoBean extends HibernateDaoSupport implements UserDao {   
  12.   
  13.     @Transactional(propagation = Propagation.REQUIRED)   
  14.     public void delUser(Integer id) {   
  15.         getHibernateTemplate().delete((User)getHibernateTemplate().get(User.class, id));   
  16.     }   
  17.   
  18.     @Transactional(readOnly = true)   
  19.     public User findUser(Integer id) {   
  20.         return (User)getHibernateTemplate().get(User.class, id);   
  21.     }   
  22.   
  23.     @Transactional(propagation = Propagation.REQUIRED)   
  24.     public void insertUser(User user) {   
  25.         getHibernateTemplate().save(user);   
  26.     }   
  27.   
  28.     @Transactional(readOnly = true)   
  29.     public List listUser() {   
  30.         return getHibernateTemplate().find("from User user");   
  31.     }   
  32.   
  33.     @Transactional(propagation = Propagation.REQUIRED)   
  34.     public void updateUser(User user) {   
  35.         getHibernateTemplate().update(user);   
  36.     }   
  37.   
  38. }  



要想让这些注解配置生效,必须在配置文件里加入最后一行

Java代码 复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>   
  2.   
  3. <beans xmlns="http://www.springframework.org/schema/beans"  
  4.         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  5.         xmlns:aop="http://www.springframework.org/schema/aop"  
  6.         xmlns:tx="http://www.springframework.org/schema/tx"  
  7.         xsi:schemaLocation="   
  8.             http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
  9.             http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd   
  10.             http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">   
  11.   
  12.     <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">   
  13.         <property name="configLocation">   
  14.             <value>classpath:hibernate.cfg.xml</value>   
  15.         </property>      
  16.     </bean>   
  17.        
  18.     <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">   
  19.         <property name="sessionFactory" ref="sessionFactory" />   
  20.     </bean>   
  21.        
  22. <tx:annotation-driven transaction-manager="transactionManager"/>   
  23. </beans>  


     
好了,以上就是Spring中的事务属性的介绍,以及Spring中如何以声明方式管理事务的。



()

PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS--支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY--支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW--新建事务,如果当前存在事务,把当前事务挂起。
PROPAGATION_NOT_SUPPORTED--以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER--以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED--如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

附:
jquery中jquery对象取某元素的某属性:
$(obj).attr("xxx")
jquery对象获得dom对象 $(obj).obj()
this一般是dom对象,若要jquery对象则$(this)


()

浅析Spring提供的事务管理方法

2006-03-02 08:32作者:fly29出处:blog责任编辑:方舟
  Spring提供的事务管理可以分为两类:编程式的和声明式的。编程式的,比较灵活,但是代码量大,存在重复的代码比较多;而声明式的比编程式的更灵活方便。本文将讨论这两种事务管理的区别。

  传统的JDBC事务管理

  以往使用JDBC进行数据操作时,一般采用DataSource,从数据源中得到Connection,我们知道数据源是线程安全的,而连接不是线程安全的,所以对每个请求都是从数据源中重新取出一个连接。一般的数据源由容器进行管理,包括连接池。例如TOMCAT,WEBSPHERE,WEBLOGIC等这些J2EE商业容器都提供了这个功能。

  以往的我们使用JDBC在写代码时,事务管理可能会是这样:

Connection conn = null;
try
{
 conn = DBConnectionFactory.getConnection;
 conn.setAutoCommit(false);
 //do something
 conn.commit(); //commit transcation
}
catch(Exception e)
{
 conn.rollback();
 //do sth
}
finally
{
 try
 {
  conn.close();
 }
 catch(SQLException se){ //do sth.}
 //close ResultSet,PreparedStatement,Connection
 //notice:Maybe ocurr Exception when u close rs,pstmt,conn
}

  按照以往的思路来写代码,代码量比较长,而且容易疏忽,忘掉一些try/catch,引发一些异常无法catch,虽然有时候我们会写DBTool类,来关闭这些资源,并且保证在关闭这些资源时,不向外抛异常。

  Spring提供的编程式的事务处理

  Spring提供了几个关于事务处理的类:

  ·TransactionDefinition //事务属性定义
  ·TranscationStatus //代表了当前的事务,可以提交,回滚。
  ·PlatformTransactionManager这个是spring提供的用于管理事务的基础接口,其下有一个实现的抽象类AbstractPlatformTransactionManager,我们使用的事务管理类例如DataSourceTransactionManager等都是这个类的子类。

  我们使用编程式的事务管理流程可能如下:

  1 声明数据源

  2 声明一个事务管理类,例如DataSourceTransactionManager,HibernateTransactionManger,JTATransactionManager等

  3 在我们的代码中加入事务处理代码:

TransactionDefinition td = new TransactionDefinition();
TransactionStatus ts = transactionManager.getTransaction(td);
try
{
 //do sth
 transactionManager.commit(ts);
}
catch(Exception e){transactionManager.rollback(ts);}

  使用spring提供的事务模板TransactionTemplate

void add()
{
 transactionTemplate.execute( new TransactionCallback(){
  pulic Object doInTransaction(TransactionStatus ts)
  { //do sth}
 }
}

  TransactionTemplate也是为我们省去了部分事务提交、回滚代码;定义事务模板时,需注入事务管理对象.

  Spring声明式事务处理

  Spring声明式事务处理也主要使用了ioc,aop思想,提供了TransactionInterceptor拦截器和常用的代理类TransactionProxyFactoryBean,可以直接对组件进行事务代理。

  使用TransactionInterceptor步骤

  1.定义数据源,事务管理类

  2.定义事务拦截器,such as:

<bean id = "transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="transactionAttributeSource">
<value>
com.test.UserManager.*r=PROPAGATION_REQUIRED
</value>
</property>
</bean>

  3.为组件声明一个代理类:ProxyFactoryBean

<bean id="userManager" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces"><value>com.test.UserManager</value></property>
<property name="interceptorNames">
<list>
<idref local="transactionInterceptor"/>
</list>
</property>
</bean>

  使用TransactionProxyFactoryBean:

<bean id="userManager" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager"><ref bean="transactionManager"/></property>
<property name="target"><ref local="userManagerTarget"/></property>
<property name="transactionAttributes">
<props>
<prop key="insert*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>

  TransactionProxyFactoryBean只是为组件的事务代理,如果我们要给组件添加一些业务方面的验证等,可以使用TransactionTemplate加拦截器方式,为组件添加多个拦截器,spring AOP中提供了三类Advice,即前增强,后增强,抛出异常时的增强,可以灵活使用。

()
va Application以及所需要的数据库和数据表,本节将介绍JOTM在Spring中的配置。


JOTM(Java Open
Transaction Manager)是ObjectWeb的一个开源JTA实现,本身也是开源应用程序服务器JOnAS(Java Open Application Server)的一部分,为其提供JTA分布式事务的功能。Spring对JOTM提供了较好的支持,提供了一个org.springframework.transaction.jta.JotmFactoryBean的支持类,在Spring2.0中也包含了JOTM相关的一些library。

jotm的下载地址为http://jotm.objectweb.
org,最新版本为2.0.10.

下载完成后解压缩,然后打开jotm下面conf文件夹,拷贝carol.properties文件到classpath中,并修改这个文件如下

carol.properties
Java代码
  1. do not use CAROL JNDI wrapper     
  2. carol.start.jndi=false     
  3.      
  4. do not start a name server     
  5. carol.start.ns=false     
  6.      
  7. # Naming Factory  
  8. carol.jndi.java.naming.factory.url.pkgs=org.apache.naming  

上面配置文件的目的是不使用JNDI的方式来加载JOTM的配置,当然也可以根据需要选择其它的一些配置,例如JTOM所提供的默认配置。

然后开始在Spring上下文中配置JOTM,在classpath中建立一个ApplicationContext-jotm.xml,配置如下


ApplicationContext-jotm.xml

Java代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">  
  5.   
  6.     <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>  
  7.       
  8.     <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">  
  9.         <property name="userTransaction" ref="jotm" />  
  10.     </bean>  
  11.   
  12.     <bean id="ds1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">  
  13.         <property name="dataSource">  
  14.             <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">  
  15.                 <property name="transactionManager" ref="jotm" />  
  16.                 <property name="driverName" value="com.mysql.jdbc.Driver" />  
  17.                 <property name="url" value="jdbc:MySQL://localhost:3306/test" />  
  18.             </bean>  
  19.         </property>  
  20.         <property name="user" value="root" />  
  21.         <property name="password" value="admin" />  
  22.     </bean>  
  23.       
  24.     <bean id="ds2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">  
  25.         <property name="dataSource">  
  26.             <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">  
  27.                 <property name="transactionManager" ref="jotm" />  
  28.                 <property name="driverName" value="com.mysql.jdbc.Driver" />  
  29.                 <property name="url" value="jdbc:MySQL://localhost:3306/test2" />  
  30.             </bean>  
  31.         </property>  
  32.         <property name="user" value="root" />  
  33.         <property name="password" value="admin" />  
  34.     </bean>  
  35.   
  36.     <bean id="template1" class="org.springframework.jdbc.core.JdbcTemplate">  
  37.         <property name="dataSource" ref="ds1" />  
  38.     </bean>  
  39.       
  40.     <bean id="template2" class="org.springframework.jdbc.core.JdbcTemplate">  
  41.         <property name="dataSource" ref="ds2" />  
  42.     </bean>  
  43.       
  44.     <bean id="dao1" class="com.xa.dao.UserDao1">  
  45.         <property name="jdbcTemplate">  
  46.             <ref bean="template1"></ref>  
  47.         </property>  
  48.     </bean>  
  49.       
  50.     <bean id="dao2" class="com.xa.dao.UserDao2">  
  51.         <property name="jdbcTemplate">  
  52.             <ref bean="template2"></ref>  
  53.         </property>  
  54.     </bean>  
  55.       
  56.     <bean id="userServiceTarget" class="com.xa.service.UserServiceImpl">  
  57.         <property name="dao1" ref="dao1"/>  
  58.         <property name="dao2" ref="dao2"/>  
  59.     </bean>  
  60.   
  61.   
  62.     <bean id="userTest" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">      
  63.         <property name="transactionManager">  
  64.             <ref bean="txManager"/>  
  65.         </property>      
  66.         <property name="target">  
  67.             <ref bean="userServiceTarget"/>  
  68.         </property>  
  69.         <property name="transactionAttributes">          
  70.             <props>  
  71.                 <prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop>              
  72.             </props>  
  73.         </property>  
  74.     </bean>  
  75. </beans>  

上面是一个完整的Spring上下文配置,可以看第一个bean “jotm”,实际上引用了Spring内部所提供的对JOTM支持的工厂类,参考下面的配置代码段
Java代码
  1. <bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean"/>  


随后,配置了
JTA事务管理器,并且在管理器中使用上面所配置的jotm,如下面的代码
Java代码
  1. <bean id="txManager" class="org.springframework.transaction.jta.JtaTransactionManager">  
  2.     <property name="userTransaction" ref="jotm" />  
  3. </bean>  


再接下来就是配置多个数据源了,使用jotm提供的
org.enhydra.jdbc.pool.StandardXAPoolDataSource类,根据类名可以明确地看出它是用以配置多个数据源的啦,配置的代码如下
Java代码
  1. <bean id="ds1" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">  
  2.         <property name="dataSource">  
  3.             <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">  
  4.                 <property name="transactionManager" ref="jotm" />  
  5.                 <property name="driverName" value="com.mysql.jdbc.Driver" />  
  6.                 <property name="url" value="jdbc:MySQL://localhost:3306/test" />  
  7.             </bean>  
  8.         </property>  
  9.         <property name="user" value="root" />  
  10.         <property name="password" value="admin" />  
  11.     </bean>  
  12.       
  13.     <bean id="ds2" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource" destroy-method="shutdown">  
  14.         <property name="dataSource">  
  15.             <bean class="org.enhydra.jdbc.standard.StandardXADataSource" destroy-method="shutdown">  
  16.                 <property name="transactionManager" ref="jotm" />  
  17.                 <property name="driverName" value="com.mysql.jdbc.Driver" />  
  18.                 <property name="url" value="jdbc:MySQL://localhost:3306/test2" />  
  19.             </bean>  
  20.         </property>  
  21.         <property name="user" value="root" />  
  22.         <property name="password" value="admin" />  
  23.     </bean>  

这里配置的两个数据源都连接到本地的mysql,实际上可以连接到不同的db server和不同类型的数据库,已经经过测试,这里为了方便,在本地建立了两个不同的数据库(test,test2)做测试。

随后的配置基本上和普通的Spring上下文配置相同了,根据不同的数据源配置两个jdbcTemplate,两个dao分别引用不同的jdbcTemplate, 将两个dao注入到UserService中, 最后将service纳入事务管理,并在事务代理配置中配置回滚规则,意思为如遇异常,则强制回滚内容。配置如下所示

Java代码
  1. <property name="transactionAttributes">          
  2.     <props>  
  3.         <prop key="insert*">PROPAGATION_REQUIRED,-Exception</prop>              
  4.     </props>  
  5. </property>  


这样,一个使用JOTM
JTA事务的简单应用算大致成型了,最后,写一个JUnit,来测试一下结果
TestXa.java
Java代码
  1. package com.xa;  
  2.   
  3. import org.springframework.context.ApplicationContext;  
  4. import org.springframework.test.AbstractDependencyInjectionSpringContextTests;  
  5.   
  6. import com.xa.service.UserService;  
  7.   
  8. public class TestXa extends AbstractDependencyInjectionSpringContextTests  
  9. {  
  10.     protected String[] getConfigLocations() {  
  11.         return new String[] { "classpath:ApplicationContext-jotm.xml" };  
  12.     }  
  13.   
  14.     public void testInsertBothDatabase() {  
  15.         ApplicationContext ctx = this.getApplicationContext();  
  16.         UserService ut = (UserService)ctx.getBean("userTest");  
  17.         try {  
  18.             ut.insertBothDatabase("1"null);  
  19.         }  
  20.         catch (Exception e) {  
  21.             e.printStackTrace();  
  22.         }  
  23.     }  
  24. }  

在test中,调用了UserService的insertBothDatabase方法,有两个参数,userId和UserName,另外在方法的实现中调用了两个使用不同数据源dao,分别向两个不同的数据库插入输入,而test2数据库的xa_test表中,name字段是不允许为空的,因此,在插入test2数据库时会失败.

运行这个test,然后察看数据库结果:),test和test2数据库中都没有插入成功,看serviceImpl中的代码可以知道,逻辑上dao1会先于dao2执行,但是由于
JTA事务,在dao2插入数据出现异常时整个事务被回滚,由于事务被配置在service层,dao1和dao2都被纳入一个事务进行管理,呵呵。修改一下方法的参数,修改为
Java代码
  1. ut.insertBothDatabase("1""name1");  


然后再试试test看数据库结果,如何?


第三节将简单介绍Atomikos在Spring中的配置和使用。
()

原创粉丝点击