Spring高级程序设计 16 事务管理

来源:互联网 发布:字符串转换成java 集合 编辑:程序博客网 时间:2024/06/05 00:12
2分析事务属性 
众所周知的ACID属性: 
原子性(atomicity)、一致性(consistency)、隔离性(isolation)以及持久性(durability)。我们无法控制一致性、原子性以及持久性,但可以控制超时,设置事务的只读性以指定隔离级别。 
Spring在TransactionDefinition接口封装了所有这些设置。 


探索TransactionDefinition接口:
view plaincopy to clipboardprint?
  1. package org.springframework.transaction;  
  2.   
  3. public interface TransactionDefinition {  
  4.   
  5.     int getPropagationBehavior();  
  6.   
  7.     int getIsolationLevel();  
  8.   
  9.     int getTimeout();  
  10.   
  11.     boolean isReadOnly();  
  12.   
  13.     String getName();  
  14. }  


getTimeout:返回一个事务必须完成的时间限制。 

isReadOnly:表示事务是否只读。 

getIsolationLevel:他对其他事务所看到的数据变化进行控制。 
事务隔离级别: 
隔离级别 说明 
ISOLATION_DEFAULT 默认级别(对大多数数据库来说就是ISOLATION_READ_COMMITTED) 
ISOLATION_READ_UNCOMMITTED 最低的隔离级别。事实上我们不应该隔离级别,因为在事务完成前,其他事务可以看到该事务所修改的数据。而在其他事务提交前,该事务也可以看到其他事务所做的修改。 
ISOLATION_READ_COMMITTED 大多数数据库的默认级别。在事务完成前,其他事务无法看到该事务所修改的数据。遗憾的是,在该事务提交后,你就可以查看其他事务插入活更新的数据。这意味着在事务的不同点上,如果其他事务修改数据,你会看到不同的数据。 
ISOLATION_REPEATABLE_READ 该隔离级别确保如果在事务中查询了某个数据集,你至少还能再次查询到相同的数据集,即使其他事务修改了所查询的数据。然而如果其他事务插入了新数据,你就可以查询到该新插入的数据。 
ISOLATION_SERIALIZABLE 代价最大、可靠性最高的隔离级别,所有的事务都是俺顺序一个接一个的执行。 

getPropagationBehavior:指定了当代码请求一个新的事务时Spring所做的事情。
传播行为指: 
传播行为 说明 
PROPAGATION_REQUIRED 当前如果有事务,Spring就会使用该事务;否则会开始一个新事务。 
PROPAGATION_SUPPORTS 当前如果有事务,Spring就会使用该事务;否则不会开启一个新事务。 
PROPAGATION_MANDATORY 当前如果有事务,Spring就会使用该事务;否则会抛出异常。 
PROPAGATION_REQUIRES_NEW Spring总会开始一个新事务。如果当前有事务,则该事务挂起。 
PROPAGATION_NOT_SUPPORTED Spring不会执行事务中的代码。代码总是在非事务环境下执行,如果当期有事务,则该事务挂起。 
PROPAGATION_NEVER 即使当前有事务,Spring也会在飞事务环境下执行。如果当前有事务,则抛出异常。 
PROPAGATION_NESTED 如果当前有事务,则在嵌套事务中执行。如果没有,那么执行情况与PROPAGATION_REQUIRED一样。 





使用TransactionStatus接口:
view plaincopy to clipboardprint?
  1. package org.springframework.transaction;  
  2.   
  3. public interface TransactionStatus extends SavepointManager {  
  4.   
  5.     boolean isNewTransaction();  
  6.   
  7.     boolean hasSavepoint();  
  8.   
  9.     void setRollbackOnly();  
  10.   
  11.     boolean isRollbackOnly();  
  12.   
  13.     boolean isCompleted();  
  14. }  

setRollbackOnly:将一个事务表示为不可提交的。 




PlatformTransactionManager的实现: 
使用TransactionDefinition和TransactionStatus接口,创建并管理事务。
 
DataSourceTransactionManager控制着从DataSource中获得的JDBC Connection上的事务执行; 
HibernateTransactionManager控制着Hibernate session上的事务执行; 
JdoTransactionManager管理着JDO事务; 
JtaTransactionManager将事务管理委托给JTA。 

例如: 
JDBC:
view plaincopy to clipboardprint?
  1. <!-- 声明事务处理器 -->  
  2. <bean id="transactionManager"  
  3.     class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  4.     <property name="dataSource" ref="dataSource"></property>  
  5. </bean>  
  6.   
  7. <!-- 声明事务通知 -->  
  8. <tx:advice id="bookShopTx"  
  9.     transaction-manager="transactionManager">  
  10.     <tx:attributes>  
  11.         <tx:method name="purchase"   
  12.             propagation="REQUIRES_NEW"  
  13.             isolation="READ_COMMITTED"  
  14.             rollback-for="java.lang.ArithmeticException"/>  
  15.     </tx:attributes>  
  16. </tx:advice>  
  17.   
  18. <!-- 声明事务通知需要通知哪些类的那些方法, 即: 那些方法受事务管理 -->  
  19. <aop:config>  
  20.     <!-- 声明切入点 -->  
  21.     <aop:pointcut expression="execution(* cn.partner4java.spring.transaction.BookShopService.*(..))"   
  22.         id="txPointCut"/>  
  23.           
  24.     <!-- 把切入点和事务通知联系起来: 既声明一个增强器 -->  
  25.     <aop:advisor advice-ref="bookShopTx" pointcut-ref="txPointCut"/>  
  26. </aop:config>  

Hibernate:
view plaincopy to clipboardprint?
  1. <bean id="sessionFactory"  
  2.     class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  3.     <property name="configLocation" value="classpath:hibernate.cfg.xml"></property>  
  4.     <property name="dataSource" ref="dataSource"></property>      
  5. </bean>  
  6.   
  7. <bean id="transactionManager"  
  8.     class="org.springframework.orm.hibernate3.HibernateTransactionManager">  
  9.     <property name="sessionFactory" ref="sessionFactory"></property>      
  10. </bean>  
  11.   
  12. <!-- 事务通知 -->  
  13. <tx:advice id="txAdvice" transaction-manager="transactionManager">  
  14.     <tx:attributes>  
  15.         <tx:method name="new*" propagation="REQUIRED" isolation="DEFAULT" />  
  16.         <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" />  
  17.         <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" />  
  18.         <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" />  
  19.         <tx:method name="bulk*" propagation="REQUIRED" isolation="DEFAULT" />  
  20.         <tx:method name="load*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
  21.         <tx:method name="get*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
  22.         <tx:method name="query*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
  23.         <tx:method name="find*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
  24.         <tx:method name="is*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
  25.           
  26.         <tx:method name="*" propagation="SUPPORTS" isolation="DEFAULT" />  
  27.     </tx:attributes>  
  28. </tx:advice>  
  29. <aop:config>  
  30.         <aop:advisor pointcut="execution(* *..*service*.*(..))" advice-ref="txAdvice" />  
  31. </aop:config>   
  32.   
  33. <context:component-scan base-package="com.bytter"></context:component-scan>  
  34.   
  35. <tx:annotation-driven/>  

JPA:
view plaincopy to clipboardprint?
  1. <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">  
  2.     <property name="dataSource" ref="dataSource" />  
  3.     <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />  
  4.     <property name="loadTimeWeaver">  
  5.           <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>  
  6.     </property>  
  7. </bean>  
  8.       
  9.   <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">  
  10.        <property name="entityManagerFactory" ref="entityManagerFactory"/>  
  11.   </bean>  
  12.     
  13.   <tx:annotation-driven transaction-manager="transactionManager"/>  




3对一个事务管理示例的探索 
使用事务操作方式有3种基本方式: 
可以使用声明式事务,只需声明某个方法需要事务就行了; 
可以使用源码级的元数据来说明某个方法需要一个事务; 
还可以用编写事务代码的方式来实现。 






6AOP事务管理 

1、使用基于注解的AOP事务管理 
<tx:annotation-driven transaction-manager="transactionManager"/>
 
<aop:aspectj-autoproxy /> 


探索tx:annotation-driven标签: 
<tx:annotation-driven/>标签是注解驱动的事务管理支持的核心。 

<tx:annotation-driven/>标签的属性: 
transaction-manager:指定到现有的PlatformTransactionManager bean的引用,通知会使用该引用。default="transactionManager" 
mode:指定Spring事务管理框架创建通知bean的方式。可用的值有proxy和aspectj。前者是默认值,表示通知对象是个JDK代理;后者表示Spring AOP会使用AspectJ创建代理。 
order:指定创建的切面的顺序。只要目标对象有多个通知就可以使用该属性。 
proxy-target-class:该属性如果为true就表示你想要代理目标类而不是bean所实现的所有接口。default="false" 

探索@Transactional注解: 
你可以指定传播、隔离级别、超时以及允许和不允许的异常。 
@Transactional注解的属性: 
propagation:指定事务定义中使用的传播 
isolation:设定事务的隔离级别 
timeout:指定事务的超市(秒) 
readOnly:指定事务的超时 
noRollbackFor:目标方法可抛出的异常所构成的数组,但通知仍会提交事务 
rollbackFor:异常所构成的数组,如果目标方法抛出了这些异常,通知就会回滚事务 



基于注解的事务管理小结: 
如果定义在类上,那么所有的方法都使用相同的方式,有些read就会抱怨给太多的东西了。 
如果在每个方法上都定义注解,那么就会很麻烦。 
(可以使用XML AOP事务管理能更好的处理这种情况) 







2、使用XML AOP事务管理 
<tx:advice/>标签,该标签会创建一个事务处理通知。
view plaincopy to clipboardprint?
  1. <tx:advice id="txAdvice" transaction-manager="transactionManager">  
  2.     <tx:attributes>  
  3.         <tx:method name="bulk*" propagation="REQUIRED" isolation="DEFAULT" />  
  4.         <tx:method name="load*" propagation="REQUIRED" isolation="DEFAULT" read-only="true"/>  
  5.     </tx:attributes>  
  6. </tx:advice>  
  7. <aop:config>  
  8.         <aop:advisor pointcut="execution(* *..*Service*.*(..))" advice-ref="txAdvice" />  
  9. </aop:config>  
  10.   
  11. 或  
  12.   
  13. <aop:config>  
  14.     <aop:pointcut id="allServiceMethods"  
  15.                   expression="execution(* com.apress.prospring2.ch16.services.*.*(..))"/>  
  16.     <aop:advisor advice-ref="defaultTransactionAdvice"  
  17.                  pointcut-ref="allServiceMethods"/>  
  18. </aop:config>  
  19.   
  20. <tx:advice id="defaultTransactionAdvice" transaction-manager="transactionManager">  
  21.     <tx:attributes>  
  22.         <tx:method  
  23.                 name="*"  
  24.                 isolation="DEFAULT"  
  25.                 propagation="REQUIRED"  
  26.                 no-rollback-for="java.lang.RuntimeException"  
  27.                 timeout="100"/>  
  28.         <tx:method  
  29.                 name="get*"  
  30.                 read-only="true"/>  
  31.     </tx:attributes>  
  32. </tx:advice>  








3、tx:advice标签简介 
id是该advice bean的标识,而transaction-manager则必须引用一个PlatformTransactionManager bean。 
还可以通过<tx:attributes>标签定制<tx:advice>标签所创建的通知的行为。 

<tx:method/>标签的属性:
 
name:方法名的匹配模式,通知根据该模式寻找匹配的方法。 
propagation:设定事务定义所用的传播级别。 
isolation:设置事务的隔离级别。 
timeout:指定事务的超时(秒)。 
read-only:该属性为true指示事务是只读的 
no-rollback-for:以逗号分隔的异常类的列表,目标方法可以跑出这些异常而不会导致通知执行回滚 
rollback-for:以逗号分隔的异常类的列表,当目标方法跑出这些异常时会导致通知执行回滚。默认情况下,该列表为空,因此不在no-rollback-for列表中的任何运行时异常都会导致回滚。 






8实现你自己的事务同步 

将介绍如何实现你自己的事务同步,只要是活动的事务状态发生变化就会收到TransactionSynchronizationManager的回调。 

书中的demo: 
使用TransactionSynchronizationManager注册了TransactionSynchronization回调,同时MyTransactionSynchronizationAdapter会根据事务的完成状态去调用MySession.beginTransaction()、MySession.commit()或MySession.rollback()方法。
 

模拟一个session类:
view plaincopy to clipboardprint?
  1. package cn.partner4java.myptm;  
  2.   
  3. import java.io.Serializable;  
  4.   
  5. /** 
  6. * 模拟一个session类 
  7. * @author partner4java 
  8. * 
  9. */  
  10. public class MySession {  
  11.     /** 用来标识一个session */  
  12.     private Long sessionId;  
  13.   
  14.     public void save(Serializable entity){  
  15.         System.out.println(sessionId + ":save");  
  16.     }  
  17.       
  18.     public void beginTransaction(){  
  19.         System.out.println(sessionId + ":beginTransaction");  
  20.     }  
  21.       
  22.     public void commit(){  
  23.         System.out.println(sessionId + ":commit");  
  24.     }  
  25.       
  26.     public void rollback(){  
  27.         System.out.println(sessionId + ":rollback");  
  28.     }  
  29.       
  30.     public Long getSessionId() {  
  31.         return sessionId;  
  32.     }  
  33.   
  34.     public void setSessionId(Long sessionId) {  
  35.         this.sessionId = sessionId;  
  36.     }  
  37.   
  38.     @Override  
  39.     public String toString() {  
  40.         return "MySession [sessionId=" + sessionId + "]";  
  41.     }  
  42.   
  43.       
  44. }  

简单模拟SessionFactory:
view plaincopy to clipboardprint?
  1. package cn.partner4java.myptm;  
  2.   
  3. import org.springframework.transaction.support.TransactionSynchronization;  
  4. import org.springframework.transaction.support.TransactionSynchronizationManager;  
  5.   
  6.   
  7. /** 
  8. * 简单模拟SessionFactory<br/> 
  9. * 通判传递的类都为MySessionFactory而不是MySession,通过MySessionFactory获得当前线程的MySession或者开启一个新的MySession 
  10. * @author partner4java 
  11. * 
  12. */  
  13. public class MySessionFactory {  
  14.       
  15.     /** 
  16.      * 如果当前线程存在MySession,就使用该MySession,否者开启一个新的MySession 
  17.      * @return 
  18.      */  
  19.     public MySession getSession(){  
  20.         //传入this,是因为,我们以当前factory类作为键保存的MySession  
  21.         if(TransactionSynchronizationManager.hasResource(this)){  
  22.             return getCurrentSession();  
  23.         }else{  
  24.             return openSession();  
  25.         }  
  26.           
  27.     }  
  28.   
  29.       
  30.     /** 
  31.      * 开启一个新MySession 
  32.      * @return 
  33.      */  
  34.     private MySession openSession() {  
  35.         MySession mySession = new MySession();  
  36.         mySession.setSessionId(System.currentTimeMillis());  
  37.           
  38.         //注册进当前线程管理一个Synchronization  
  39.         TransactionSynchronization transactionSynchronization = new MyTransactionSynchronizationAdapter(this);  
  40.         TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);  
  41.           
  42.         //绑定新开启的一个MySession进当前线程事务管理器  
  43.         TransactionSynchronizationManager.bindResource(this, mySession);  
  44.           
  45.         return mySession;  
  46.     }  
  47.   
  48.     /** 
  49.      * 获取当前线程的MySession 
  50.      * @return 
  51.      */  
  52.     private MySession getCurrentSession() {  
  53.         MySession mySession = (MySession) TransactionSynchronizationManager.getResource(this);  
  54.         return mySession;  
  55.     }  
  56. }  

核心事务同步适配器:
view plaincopy to clipboardprint?
  1. package cn.partner4java.myptm;  
  2.   
  3. import org.springframework.transaction.support.TransactionSynchronizationAdapter;  
  4. import org.springframework.transaction.support.TransactionSynchronizationManager;  
  5.   
  6. /** 
  7. * 核心事务同步适配器<br/> 
  8. * 当方法上面定义了@Transactional注解,那么当每次状态发生时就会调用本同步适配器 
  9. * for transaction synchronization callbacks 
  10. * @author partner4java 
  11. * 
  12. */  
  13. public class MyTransactionSynchronizationAdapter extends  
  14.         TransactionSynchronizationAdapter {  
  15.     private MySessionFactory mySessionFactory;  
  16.   
  17.     public MyTransactionSynchronizationAdapter(MySessionFactory mySessionFactory) {  
  18.         this.mySessionFactory = mySessionFactory;  
  19.     }  
  20.   
  21.   
  22.     @Override  
  23.     public void beforeCommit(boolean readOnly) {  
  24.         //readOnly标识是否是一个只读线程  
  25.         if(!readOnly){  
  26.             MySession mySession = (MySession) TransactionSynchronizationManager.getResource(mySessionFactory);  
  27.             mySession.beginTransaction();  
  28.         }  
  29.     }  
  30.   
  31.     @Override  
  32.     public void afterCompletion(int status) {  
  33.         MySession mySession = (MySession) TransactionSynchronizationManager.getResource(mySessionFactory);  
  34.         if (STATUS_COMMITTED == status) {  
  35.             mySession.commit();  
  36.         }  
  37.         //当然,你还可以定义回滚方法  
  38.     }  
  39.       
  40.       
  41. }  

调用起的DAO:
view plaincopy to clipboardprint?
  1. package cn.partner4java.dao;  
  2.   
  3. public interface HelloDao {  
  4.     public void saveHello();  
  5. }  

view plaincopy to clipboardprint?
  1. package cn.partner4java.dao;  
  2.   
  3. import org.springframework.jdbc.core.support.JdbcDaoSupport;  
  4. import org.springframework.transaction.annotation.Transactional;  
  5.   
  6. import cn.partner4java.myptm.MySessionFactory;  
  7.   
  8. /** 
  9. * 一个hello world dao,起到模拟调用自定义事务同步的作用 
  10. * @author partner4java 
  11. * 
  12. */  
  13. public class HelloDaoImpl extends JdbcDaoSupport implements HelloDao {  
  14.     private MySessionFactory mySessionFactory;  
  15.   
  16.     public void setMySessionFactory(MySessionFactory mySessionFactory) {  
  17.         this.mySessionFactory = mySessionFactory;  
  18.     }  
  19.       
  20.     @Transactional  
  21.     public void saveHello(){  
  22.         mySessionFactory.getSession().save(null);  
  23.         this.getJdbcTemplate().execute("select * from user");  
  24.     }  
  25. }  


配置文件:
view plaincopy to clipboardprint?
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.        xmlns:aop="http://www.springframework.org/schema/aop"  
  4.        xmlns:tx="http://www.springframework.org/schema/tx"  
  5.        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  6.        xsi:schemaLocation="  
  7.             http://www.springframework.org/schema/beans  
  8.             http://www.springframework.org/schema/beans/spring-beans.xsd  
  9.             http://www.springframework.org/schema/tx  
  10.             http://www.springframework.org/schema/tx/spring-tx.xsd  
  11.             http://www.springframework.org/schema/aop  
  12.             http://www.springframework.org/schema/aop/spring-aop.xsd">  
  13.                   
  14.     <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource" destroy-method="close">  
  15.         <property name="driverClassName" value="com.mysql.jdbc.Driver"/>  
  16.         <property name="url" value="jdbc:mysql://localhost:3306/springdb"/>  
  17.         <property name="username" value="root"/>  
  18.         <property name="password" value="123456"/>  
  19.     </bean>  
  20.   
  21.     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">  
  22.         <property name="dataSource" ref="dataSource"/>  
  23.     </bean>  
  24.       
  25.     <tx:annotation-driven transaction-manager="transactionManager"/>  
  26.                   
  27.     <aop:aspectj-autoproxy />  
  28.       
  29.     <bean id="mySessionFactory" class="cn.partner4java.myptm.MySessionFactory"/>  
  30.       
  31.     <bean id="helloDao" class="cn.partner4java.dao.HelloDaoImpl">  
  32.         <property name="dataSource" ref="dataSource"/>  
  33.         <property name="mySessionFactory" ref="mySessionFactory"></property>  
  34.     </bean>  
  35.   
  36. </beans>  


测试:
view plaincopy to clipboardprint?
  1.          ApplicationContext ac = new ClassPathXmlApplicationContext("/META-INF/spring/myptm.xml");  
  2.            
  3.          HelloDao helloDao = (HelloDao) ac.getBean("helloDao");  
  4.          helloDao.saveHello();  
  5. //       后台打印:  
  6. //       1322395163008:save  
  7. //       1322395163008:beginTransaction  
  8. //       1322395163008:commit  

总结:有两个核心的Spring类支持了这个功能,TransactionSynchronization接口,TransactionSynchronizationManager类。 
TransactionSynchronizationManager负责管理当前线程在资源,资源可以主动绑定到TransactionSynchronizationManager中。 
TransactionSynchronization提供了同步调用,当方法上面定义了@Transactional注解,那么当每次状态发生时就会调用本同步适配器。 

PlatformTransactionManager的各种实现也是借助了上面这两个类,你可以查阅一下源码。所以,我们自然而然的也可以自己实现一个PlatformTransactionManager,来管理真正的sessionFactory,然后像其他实现一样,交给Spring,然后再给他声明事务。 
原创粉丝点击