spring下hibernate多数据库解决方案,以及跨库事务的尝试

spring下hibernate多数据库解决方案,以及跨库事务的尝试

相关文章: 

    spring下hibernate多数据库解决方案,以及跨库事务的尝试(二)
    Spring+Hibernate组合使用时的奇怪问题
    webwork2.2.1整合spring1.2.5遇到的问题




开发目的:一个协同平台项目,多托管用户,单门户系统,每个托管用户对应一个单一数据库,要求根据登陆用户的单位信息,自动选择操作数据库;同时,涉及跨库操作(比如跨库查询,跨库单据发送);同时事务处理必须支持这种多数据库模式,支持一些逻辑性不强的跨库事务,比如一些数据的发送和接收等<o:p></o:p>

当然,如果说跨库操作只涉及到数据的发送和接受的话,也可以通过构建专门web service以及通信线程来处理,<o:p></o:p>

开发环境: tomcat4.1,webwork<st1:chsdate month="12" islunardate="False" day="30" year="1899" w:st="on" isrocdate="False">2.2.4</st1:chsdate>,spring2.0.4,hibernate3.1,osworkflow2.8,mysql5.0.19 由于正式发布的应用服务器是weblogic8.1,所以没有采用jdk5环境以及struts2<o:p></o:p>

准备:<o:p></o:p>

问题一 由于有跨库操作,而且这种跨库操作无法预知,有的跨库操作完全是在逻辑运行中决定的,比如A托管用户或则C、D向B托管用户发了订单 ,B回复,这个回复是根据订单发送者来说的,具体到后台操作,是无法事先预知针对具体哪个后台物理数据库操作的.所以,也就是说,存在在业务执行过程中切换数据库的情况,传统的到注入设置dao类 sessionFactory、靠filter以及Interceptor设置线程安全的sessionFactory都无法完全达到设计目的<o:p></o:p>

问题二 事务,本来,我打算用JtaTransactionManager的,除了JtaTransactionManager,在开始时也实在没想到什么好的办法, 难道,JtaTransactionManager是唯一选择么?<o:p></o:p>

步骤:<o:p></o:p>

、 因为问题一,所以系统在资源方面是多sessionFactory并存的方式,也就是多少个托管用户多少个sessionFactory,当然,事实上,这种应用型项目本身并发访问量不会太高(什么,很高么,总比不过广告联盟吧,哈哈).不用担心多了几个sessionFactory会对系统或则数据库造成多大影响.<o:p></o:p>

<o:p>
xml 代码


    <bean id="mitDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"> 
          <property name="driverClass"> 
           <value>com.mysql.jdbc.Driver</value> 
          </property> 
          <property name="jdbcUrl"> 
           <value>jdbc:mysql://127.0.0.1:3306/mitflow</value> 
          </property> 
          <property name="user"> 
           <value>root</value> 
          </property> 
        ........................... 
     </bean> 
     
    <bean id="mitSessionFactory" 
      class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"> 
      <property name="mappingResources"> 
            .................................. 
        </props> 
      </property> 
       
       <property name="dataSource"> 
         <ref local="mitDataSource" /> 
       </property> 
     </bean> 


然后复制,粘贴,修改jdbcUrl,象网站编辑一样..

假设,我配置了两个sessionFatory ,一个是mitSessionFactory,一个是testSessionFactory,如下:

xml 代码

    <hibernatesupport>
    <item id="mit" bean="mitSessionFactory"/>
    <item id="test" bean="testSessionFactory"/>
    < hibernatesupport>

这个我自己系统配置的一部分,系统会解析他,从而知晓究竟存在多少个sessionFactory,item's XmlNode中的id可以理解会托管客户的客户单位<o:p></o:p>

编号,当然,这个配置完全可以忽略,直接从ApplicationContext中一样可以获取到这样的信息<o:p></o:p>

在客户登陆的时候,系统要记录下该客户所属托管单位,然后通过上面的id找到bean's name ,最后获取这个sessionFactory,托管单位信息一般<o:p></o:p>

都是个编号而已跟己方系统的托管用户管理相结合,一般是保存这个编号在session里面,也可以象asp.net一样,记录在安全凭证里,还不知道JAVA方面有没有类似实现,个人认为asp.net这个方法很值得采用,虽然MS号称安全系数+++++这个观点值得怀疑<o:p></o:p>

<o:p> </o:p>
首先建立一个类,HibernateSupport ,存放当前请求线程所需sessionFactory

java 代码


    public class HibernateSupport { 
        public static final String HIBERNATE_SESSIONIDKEY = "com.mit.hibernatesupport.factory.id"; 
        private static final Logger logger = Logger.getLogger(HibernateSupport.class); 
        private static ApplicationContext applicationContext ; 
        private static boolean singleSession=true; 
        private static Map factorybeanset; 
        private static ThreadLocal switchhistory;//在切换不同sessionFactory的时候用到 
        private static ThreadLocal idset;//记录当前默认的托管用户id,在实际使用中,这个是可以取消掉的 
        private static ThreadLocal curfactory;////当前正在使用的sessionFactory 
        private static ThreadLocal trace;//一个sessionFactory集合,用来记录这次线程调用了那些sessionFactory 
        static 
        { 
            idset = new ThreadLocal(); 
            curfactory = new ThreadLocal(); 
            trace = new ThreadLocal(); 
            switchhistory = new ThreadLocal(); 
        } 
         
        /**
         * set current sessionfactory for the Request
         * @param  ServletContext
         * @param  the factory's id defined in courser.xml
         */ 
        public static synchronized void setCurrent(ServletContext context,Object id) 
        { 
            if (idset.get()==null) 
            { 
                idset.set(id); 
                if (factorybeanset.containsKey(id)) 
                { 
                    if (applicationContext==null) 
                    { 
                         applicationContext =  
                        WebApplicationContextUtils 
                            .getWebApplicationContext(context); 
                    } 
                    curfactory.set((SessionFactory)applicationContext 
                            .getBean((String)factorybeanset.get(id))); 
                    putTrace(idset.get(),(SessionFactory)curfactory.get()); 
                } 
            } 
        } 
         
        /**
         * put the sessionfactory to tracemap
         * @see COPenSessionInViewFilter release sessionfactory in tracemap 
         * @param  the factory's id defined in courser.xml
         * @param  hibernate's sessionfactory
         */ 
        private static void putTrace(Object id ,SessionFactory factory) 
        { 
            Map tracemap = null; 
            if (trace.get()==null) 
            { 
                tracemap = new HashMap(); 
                trace.set(tracemap); 
            } 
            else 
            { 
                tracemap = (Map)trace.get(); 
            } 
            if (!tracemap.containsKey(id)) 
            { 
                tracemap.put(id, factory); 
            } 
        } 
         
        /**
         * switch current sessionfactory 
         * @param  the factory's id defined in courser.xml
         */ 
        public static synchronized void swtichFactory(Object id) 
        { 
            if (!idset.get().equals(id)  ) 
            { 
                if (factorybeanset.containsKey(id)) 
                { 
                    SessionFactory oldfactory = (SessionFactory)curfactory.get();        
                    SessionFactory newfactory = (SessionFactory)applicationContext 
                    .getBean((String)factorybeanset.get(id)); 
                    curfactory.set(newfactory); 
                    pushHistory(oldfactory); 
                    putTrace(id,newfactory); 
                    bindSessionFactory(newfactory); 
                } 
            } 
        } 
         
        /**
         * restore sessionfactory from queue of switchhistory
         */ 
        public static synchronized void restoreFactory() 
        { 
            SessionFactory factory = popHistory(); 
            if (factory!=null) 
            { 
                curfactory.set(factory); 
            } 
        } 
        /**
         * push old sessionfactory to swithhistory after swtichFactory
         * @param hibernate's sessionfactory
         */ 
        private static void pushHistory(SessionFactory sessionfactory) 
        { 
            LinkedList list = null; 
            if (switchhistory.get()==null) 
            { 
                list = new LinkedList(); 
                switchhistory.set(list); 
            } 
            else 
            { 
                list = (LinkedList)switchhistory.get(); 
            } 
            list.add(0,sessionfactory); 
             
        } 
        /**
         * pop sessionfactory in queue
         */ 
        private static SessionFactory popHistory() 
        { 
            if (switchhistory.get()!=null) 
            { 
                LinkedList list = (LinkedList)switchhistory.get(); 
                if (list.size()>0) 
                { 
                    SessionFactory factory = (SessionFactory)list.getFirst(); 
                    list.removeFirst(); 
                    return factory; 
                } 
            } 
            return null; 
        } 
         
        public static Map getTraceMap() 
        { 
            if (trace.get()!=null) 
            { 
                return (Map)trace.get(); 
            } 
            return null; 
        } 
         
        public static SessionFactory getCurrentFactory() 
        { 
            return (SessionFactory)curfactory.get(); 
        } 
         
        public static synchronized void release() 
        { 
            idset.set(null); 
            curfactory.set(null); 
            switchhistory.set(null); 
            trace.set(null); 
        } 
         
        /**
         * °ó¶¨sessionFactoryµ½springµÄ×ÊÔ´¹ÜÀí
         * @param hibernate's sessionfactory
         */ 
        private static synchronized boolean bindSessionFactory(SessionFactory sessionFactory) 
        { 
            boolean participate=false;; 
            if (singleSession) { 
                // single session mode 
                if (TransactionSynchronizationManager.hasResource(sessionFactory)) { 
                    // Do not modify the Session: just set the participate flag. 
                    participate = true; 
                } 
                else { 
                    logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter"); 
                    Session session = getSession(sessionFactory); 
                    if (!TransactionSynchronizationManager.hasResource(sessionFactory)) 
                    {    
                        TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session)); 
                    } 
                } 
            } 
            else { 
                // deferred close mode 
                if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) { 
                    // Do not modify deferred close: just set the participate flag. 
                    participate = true; 
                } 
                else { 
                    SessionFactoryUtils.initDeferredClose(sessionFactory); 
                } 
            } 
            return participate; 
        } 
         
        //see SessionFactoryUtils 
        private static  Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException { 
            Session session = SessionFactoryUtils.getSession(sessionFactory, true); 
            FlushMode flushMode = FlushMode.COMMIT; 
            if (flushMode != null) { 
                session.setFlushMode(flushMode); 
            } 
            return session; 
        } 
     
        public static synchronized void initSessionFactory(Map res,Class loadclass) 
        { 
            factorybeanset =res; 
        } 
     
         
    } 

HibernateSupport这个类其他方法可以不管,暂时关注setCurrent这个方法

java 代码

    if (idset.get()==null)
    {
    idset.set(id);
    if (factorybeanset.containsKey(id)) //factorybeanset包含的就是我自己系统配置中那一部分,key就是id,,value就是sessionFactory 在spring环境中的beanName
    {
    if (applicationContext==null)
    {
    applicationContext =WebApplicationContextUtils.getWebApplicationContext(context);
    }
    curfactory.set((SessionFactory)applicationContext.getBean((String)factorybeanset.get(id)));//设置当前的sessionFactory
    putTrace(idset.get(),(SessionFactory)curfactory.get());//put到当前线程的一个记录集
    }
    }

然后,就要修改spring关于hibernate的一些支持类了,当然,也可以选择重新写一套dao支持类,呵呵,不过,显然,在spring基础上做一些小修改代价更小<o:p></o:p> HibernateAccessor (HibernateTemplate的基类)以及HibernateTransactionManager都是靠注入方式获取一个sessionFactory,显然,这套不适合了,修改之

多sessionFactory好做,配置在spring或则单独拿出来处理都可以,但是spring的HibernateDaoSupport 必须绑定一个sessionFactory,当然,我们完全可以写一个自己的HibernateDaoSupport ,但是既然用了spring的事务管理而又不想多花时间,还是将就改改用吧

java 代码

    private SessionFactory sessionFactory;
    publicvoid setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
    }

<o:p></o:p>

去掉HibernateAccessor 和HibernateTransactionManager中的上述两段代码,当然,也别忘了毙掉两个类中的<o:p></o:p>

afterPropertiesSet方法中那些检查代码<o:p></o:p>

然后 ,ant打包就可以了,如果不想修改spring的代码,也可以单独把这几个类提出来另建jar包,我是单独提出来新建的,比如HibernateTransactionManager我改名成CHibernateTransactionManager,其他类似,但是包名必须是org.springframework.orm.hibernate3 ,谁也不想这么做,可是谁让sessionFactoryUtils中一个closexxxx方法没定义成public了??<o:p></o:p>

如果想变更sessionFactoryUtils,奉劝算了吧..<o:p></o:p>

然后可以做测试了,首先,部署测试的dao和service,先是事务部署<o:p></o:p>
xml 代码

    <bean id="transactionManager"
    class="com.mit.web.hibernate.CHibernateTransactionManager"/>
    <bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
    <property name="properties">
    <props>
    <prop key="load*">PROPAGATION_REQUIRED,readOnlyprop>
    <prop key="save*">PROPAGATION_REQUIREDprop>
    <prop key="delete*">PROPAGATION_REQUIREDprop>
    <prop key="find*">PROPAGATION_REQUIRED,readOnlyprop>
    <prop key="query*">PROPAGATION_REQUIRED,readOnlyprop>
    <prop key="create*">PROPAGATION_REQUIREDprop>
    <prop key="set*">PROPAGATION_REQUIRED,readOnlyprop>
    <prop key="execute*">PROPAGATION_REQUIREDprop>
    props>
    property>
    bean>
    <bean id="transactionInterceptor" class=




"其实,也有解决方法,就是延迟提交,具体实现方式以后讲述"

以下存属个人理解:
其实JtaTransactionManager做的正是这一点, 在spring中通过TransactionSynchronizer来实现的:service.save()并不会commit,而是将当前Transaction放入一个ThreadLocal的Transaction list里,到结束时一起处理.

  
的确,我的确是按照Transaction list这个思路做的,通过新建一个PlatformTransactionManager类,大部分参照AbstractPlatformTransactionManager的内容,只是在这两个类的commit和rollback里面做点控制就可以了
返回顶楼
      
hsqldb里的transaction是row transaction
就像command pattern的undo,而且还有一个MEMENTO,入在log里.

dist transaction的机制应该差不多,只是加了个message queue


事务配置如下:

     <bean id="transactionManager"   
             class="com.mit.web.hibernate.CHibernateTransactionManager"/> 
     
    <bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource"> 
        <property name="properties"> 
            <props> 
                <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop> 
                <prop key="save*">PROPAGATION_REQUIRED</prop> 
                <prop key="delete*">PROPAGATION_REQUIRED</prop> 
                <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop> 
                <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop> 
                <prop key="create*">PROPAGATION_REQUIRED</prop> 
                <prop key="set*">PROPAGATION_REQUIRED,readOnly</prop> 
                <prop key="execute*">PROPAGATION_REQUIRED</prop> 
            </props> 
        </property> 
    </bean> 
     
    <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">   
           <property name="transactionManager" ref="transactionManager"/>   
         <property name="transactionAttributeSource"><ref local="transres"/></property> 
       </bean>   
       
       <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">   
     <property name="beanNames"><value>*Service</value></property> 
           <property name="interceptorNames">   
               <list>   
                   <value>transactionInterceptor</value>   
               </list>   
           </property>   
       </bean>   
      
       <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">   
         <property name="transactionInterceptor" ref="transactionInterceptor"/>   
       </bean>  


我把事务配置在service层,事务管理采用的是我修改后的HibernateTransactionManager ,他不在要求sessionFactory的注入了
以下是测试的dao类和service类配置
Java代码  收藏代码

    <bean id="personDao" class="com.mit.web.action.PersonDaoImpl"/>  
    <bean id="personService" class="com.mit.web.action.PersonServiceImpl">  
        <property name="personDao">  
         <ref bean="personDao"/>  
        </property>  
     </bean>  
      
    <bean id="departDao" class="com.mit.web.action.DepartDaoImpl"/>  
    <bean id="departService" class="com.mit.web.action.DepartServiceImpl">  
        <property name="personService">  
            <ref bean="personService"/>  
        </property>  
        <property name="departDao">  
            <ref bean="departDao"/>  
        </property>  
    </bean>  


具体代码不写了,大概如下:


    public class PersonDaoImpl extends CHibernateDaoSupport implements PersonDao;  
    public class PersonServiceImpl implements PersonService;  
    public class DepartDaoImpl extends CHibernateDaoSupport implements DepartDao;  
    public class DepartServiceImpl implements DepartService;  


测试代码的目的是将两个实体类分别存放到mit和test库,假设默认用户是mit这个托管库的,这个方法就写在DepartServiceImpl类里面
Java代码  收藏代码

    public class DepartServiceImpl implements DepartService {  
    private DepartDao dao;  
    private PersonService service;  
     ..............................  
     public void executeTest(Depart depart,Person person)  
     {  
        dao.save(depart);  
        HibernateSupport.switchFactory("test");  
        service.save(person);  
        HibernateSupport.restoreFactory();  
    }  
     ..............................  


上面代码中将depart对象存到mit库,而将person对象存到test库中.

事务 事务 ,没看见特别事务设置? 其实事务已经在运行了

如前面配置,事务我配置在了service层,那么当executeTest执行时,会启动针对mit库的事务(前面我们改了HibernateTransactionManager的sessionFactory获取方式),

dao.save(depart);//然后保存depart到mit库

HibernateSupport.switchFactory("test");//切换库到test

service.save(person);//调用personService的save方法,这也是个service,所以也会产生一个事务,而此时HibernateSupport.getCurrentFactory返回的sessionFactory已经是test库的了,所以,spring事务管理发现上下文中并没有针对test的事务,于是,会重新启动一个新的事务,这个service.save(person);方法执行完后,这个事务将会提交

HibernateSupport.restoreFactory();//切换回默认数据库

整个方法执行完后,针对mit的事务将会被提交

[color=green]如果service.save(person);发生异常,这两个事务就会被提交,一个简单跨库事务控制完成[color=green]

但是,问题也随之而来,如果在 HibernateSupport.restoreFactory();后,又进行了一些逻辑操作,那么发生异常时,而由于 service.save(person);这个事务已经被提交,也就是说,针对test的事务已经提交不会回滚了,这是个非常严重的问题。。

其实,也有解决方法,就是延迟提交,具体实现方式以后讲述

跨库事务之延迟提交
延迟事务,就是将事务延后提交,延迟的时间由事务管理器掌握。在我的系统中,只有跨库操作涉及到延迟提交,针对这种操作,我设计了一个基本的执行模型。就是如果一个逻辑中存在多个事务,将全部放到逻辑执行完以后提交,那么,既然如此,开始吧
PlatformTransactionManager是spring平台相关事务,比如HibernateTransactionManager都是继承于此类,为了达到延迟提交的目的,可以在AbstractPlatformTransactionManagershang上做修改达到目的
首先,说一下在spring中,通常的一个事务流程,
流程如下:初始化Transaction B,如果发现前面有其他Transaction ,比如 Transaction A,那么挂起TransactionA ,然后启动事务 ,当逻辑执行完后 ,commit,恢复挂起事务A,然后清除同步对象以及其他资源,如果执行发生异常,当然,异常发生后 rollback
延迟提交的设计思想是将事务都暂存在一个threadlocal的LIST里面,等逻辑执行完以后,再统一提交,那么首先在AbstractPlatformTransactionManager中设置一个threadlocal对象


    private ThreadLocal lazytrace = new ThreadLocal(); 


LazyTransactionStatus用来管理需要延迟提交的事务


    private static class LazyTransactionStatus 
    { 
        private java.util.List transliat; 
        private int transnum;//代表未提交事务数量 
         
        public LazyTransactionStatus() 
        { 
            transliat= new java.util.ArrayList(); 
            transnum=0; 
        } 
         
        public void newLazy() 
        { 
            transnum++; 
        } 
         
        public void push(TransactionStatus trasobj) 
        {    
            objmap.add(trasobj); 
        } 
         
        public void removeLazy(TransactionStatus trasobj) 
        { 
            transnum--; 
        } 
         
        public boolean canCommit() 
        { 
            if (transnum<1) 
            { 
                return true; 
            } 
            else 
                return false; 
        } 
         
        public java.util.List getTransactionObjs() 
        { 
            return transliat; 
        } 
    } 


这就是local对象的存储内容.translist存放当前执行中的TransactionStatus实例
TransactionStatus顾名思义,事务状态,包含了事务、挂起资源等一系列信息
然后以事务提交为例
然后在AbstractTransactionManager增加如下两个方法
Java代码  收藏代码

    public final boolean isCommit(TransactionStatus status) 
    { 
        if (lazytrace.get()!=null) 
        { 
            LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get(); 
            lazystatus.removeLazy(status); 
            return lazy.canCommit(); 
        } 
        return true; 
    } 
     
    protected void begin(Object transaction, TransactionDefinition definition) 
    { 
        doBegin(transaction,definition); 
        LazyTransactionStatus lazystatus = null; 
        if (lazytrace.get()==null) 
        { 
            lazystatus = new LazyTransactionStatus(); 
            lazytrace.set(lazystatus); 
        } 
        else 
        { 
            lazystatus = (LazyTransactionStatus)lazytrace.get(); 
        } 
        lazystatus.newLazy(); 
    } 
     
    public final void registerTraceStatus(TransactionStatus status) 
    { 
        LazyTransactionStatus lazystatus = null; 
        if (lazytrace.get()==null) 
        { 
            lazystatus = new LazyTransactionStatus(); 
            lazytrace.set(lazystatus); 
        } 
        else 
        { 
            lazystatus = (LazyTransactionStatus)lazytrace.get(); 
        } 
        lazystatus.push(status); 
    } 


begin ,当一个事务开始时,将LazyTransactionStatus的transnum+1,表示又多了个刚开始,还未提交的事务
registerTraceStatus发生在commit的时候,注册这个事务到LazyTransactionStatus,同时,
注意 transnum表示的是未提交事务数量,所以当事务管理器执行commit表示要提交一个事务后,transnum将减一,如果减一后发现transnum<1,表示所有事务都提交了,那么,将所有事务提交。否则,不提交,继续等待...
关系如下:
begin->transnum+1 表示新事务诞生
registerTraceStatus(发生在commit的时候)->将准备提交的TransStatus放到LazyTransactionStatus,是的,这个事务要提交了,来吧,先注册一下
紧接着
isCommit()->将transnum-1,如果发现transnum小于1 ,表示闹够了,可以都提交滚蛋了
注意 ,transnum与LazyTransactionStatus的translist的链表长度在执行commit的时候是反方向发展的 一个增,一个减
好了,首先是注册事务数量,不用管了,在事务开始时begin方法它自己会调用了,
然后是修改AbstractPlatformTransactionManager的commit方法

Java代码  收藏代码

    public final void commit(TransactionStatus txstatus) throws TransactionException { 
        this.registerTraceStatus(txstatus); 
        if (this.isCommit(txstatus)) 
        { 
            int error = 0; 
            LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get(); 
            List statuslist = lazystatus.getTransactionObjs(); 
            for (int i=0;i<statuslist.size();i++) 
            { 
                try 
                { 
                    TransactionStatus status = (TransactionStatus)statuslist.get(i); 
                    if (status.isCompleted()) { 
                        error++; 
                        continue; 
                        //throw new IllegalTransactionStateException( 
                        //      "Transaction is already completed - do not call commit or rollback more than once per transaction"); 
                    } 
             
                    DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; 
                    if (defStatus.isLocalRollbackOnly()) { 
                        if (defStatus.isDebug()) { 
                            logger.debug("Transactional code has requested rollback"); 
                        } 
                        processRollback(defStatus); 
                        error++; 
                        continue; 
                    } 
                    if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { 
                        if (defStatus.isDebug()) { 
                            logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); 
                        } 
                        processRollback(defStatus); 
                        // Throw UnexpectedRollbackException only at outermost transaction boundary 
                        // or if explicitly asked to. 
                        if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { 
                            //throw new UnexpectedRollbackException( 
                                    //"Transaction rolled back because it has been marked as rollback-only"); 
                            error++; 
                            continue; 
                        } 
                        continue; 
                    } 
                     
                    processCommit(defStatus); 
                } 
                catch (Exception ex) 
                { 
                    error++; 
                    ex.printStackTrace(); 
                    continue; 
                } 
                 
            } 
            lazytrace.set(null); 
            if (error>0) 
                throw new IllegalTransactionStateException( 
                            "Not commit all transaction"); 
        } 
    } 


this.registerTraceStatus(txstatus);//事务提交了,成了嫌疑犯,拖到threadlocal的LazyTransactionStatus监狱里面先关起来
if (isCommit()) //看看监狱的事务是不是满了,如果满了,就可以全放了
LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();
List statuslist = lazystatus.getTransactionObjs();
for (int i=0;i<statuslist.size();i++)
{
                          ........
                         processCommit(defStatus);
//看来真的满了,都放了吧
回滚道理是一样的,不过不用判断了,直接全部放出来让他们滚吧
当然,目前这个实现只是个模型,真要实际应用,还需要做进一步封装,实际做,我用了OpenSessionInViewFilter,我已经做过测试,测试了了OpenSessionInViewFilter中singleSession为true和false两种情况,测试都通过了,呵呵

         
   

猜你喜欢

转载自xgbjmxn.iteye.com/blog/1170227
今日推荐