`

Spring多数据源解决方案(研究,不确信是正确)

阅读更多

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

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

开发环境: tomcat4.1,webwork2.2.4,spring2.0.4,hibernate3.1,osworkflow2.8,mysql5.0.19 由于正式发布的应用服务器是weblogic8.1,所以没有采用jdk5环境以及struts2

准备:

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

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

步骤:

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

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

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

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

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

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

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

首先建立一个类,HibernateSupport ,存放当前请求线程所需sessionFactory

java 代码
 
  1. public class HibernateSupport {  
  2.     public static final String HIBERNATE_SESSIONIDKEY = "com.mit.hibernatesupport.factory.id";  
  3.     private static final Logger logger = Logger.getLogger(HibernateSupport.class);  
  4.     private static ApplicationContext applicationContext ;  
  5.     private static boolean singleSession=true;  
  6.     private static Map factorybeanset;  
  7.     private static ThreadLocal switchhistory;//在切换不同sessionFactory的时候用到  
  8.     private static ThreadLocal idset;//记录当前默认的托管用户id,在实际使用中,这个是可以取消掉的  
  9.     private static ThreadLocal curfactory;////当前正在使用的sessionFactory  
  10.     private static ThreadLocal trace;//一个sessionFactory集合,用来记录这次线程调用了那些sessionFactory  
  11.     static  
  12.     {  
  13.         idset = new ThreadLocal();  
  14.         curfactory = new ThreadLocal();  
  15.         trace = new ThreadLocal();  
  16.         switchhistory = new ThreadLocal();  
  17.     }  
  18.       
  19.     /** 
  20.      * set current sessionfactory for the Request 
  21.      * @param  ServletContext 
  22.      * @param  the factory's id defined in courser.xml 
  23.      */  
  24.     public static synchronized void setCurrent(ServletContext context,Object id)  
  25.     {  
  26.         if (idset.get()==null)  
  27.         {  
  28.             idset.set(id);  
  29.             if (factorybeanset.containsKey(id))  
  30.             {  
  31.                 if (applicationContext==null)  
  32.                 {  
  33.                      applicationContext =   
  34.                     WebApplicationContextUtils  
  35.                         .getWebApplicationContext(context);  
  36.                 }  
  37.                 curfactory.set((SessionFactory)applicationContext  
  38.                         .getBean((String)factorybeanset.get(id)));  
  39.                 putTrace(idset.get(),(SessionFactory)curfactory.get());  
  40.             }  
  41.         }  
  42.     }  
  43.       
  44.     /** 
  45.      * put the sessionfactory to tracemap 
  46.      * @see COPenSessionInViewFilter release sessionfactory in tracemap  
  47.      * @param  the factory's id defined in courser.xml 
  48.      * @param  hibernate's sessionfactory 
  49.      */  
  50.     private static void putTrace(Object id ,SessionFactory factory)  
  51.     {  
  52.         Map tracemap = null;  
  53.         if (trace.get()==null)  
  54.         {  
  55.             tracemap = new HashMap();  
  56.             trace.set(tracemap);  
  57.         }  
  58.         else  
  59.         {  
  60.             tracemap = (Map)trace.get();  
  61.         }  
  62.         if (!tracemap.containsKey(id))  
  63.         {  
  64.             tracemap.put(id, factory);  
  65.         }  
  66.     }  
  67.       
  68.     /** 
  69.      * switch current sessionfactory  
  70.      * @param  the factory's id defined in courser.xml 
  71.      */  
  72.     public static synchronized void swtichFactory(Object id)  
  73.     {  
  74.         if (!idset.get().equals(id)  )  
  75.         {  
  76.             if (factorybeanset.containsKey(id))  
  77.             {  
  78.                 SessionFactory oldfactory = (SessionFactory)curfactory.get();         
  79.                 SessionFactory newfactory = (SessionFactory)applicationContext  
  80.                 .getBean((String)factorybeanset.get(id));  
  81.                 curfactory.set(newfactory);  
  82.                 pushHistory(oldfactory);  
  83.                 putTrace(id,newfactory);  
  84.                 bindSessionFactory(newfactory);  
  85.             }  
  86.         }  
  87.     }  
  88.       
  89.     /** 
  90.      * restore sessionfactory from queue of switchhistory 
  91.      */  
  92.     public static synchronized void restoreFactory()  
  93.     {  
  94.         SessionFactory factory = popHistory();  
  95.         if (factory!=null)  
  96.         {  
  97.             curfactory.set(factory);  
  98.         }  
  99.     }  
  100.     /** 
  101.      * push old sessionfactory to swithhistory after swtichFactory 
  102.      * @param hibernate's sessionfactory 
  103.      */  
  104.     private static void pushHistory(SessionFactory sessionfactory)  
  105.     {  
  106.         LinkedList list = null;  
  107.         if (switchhistory.get()==null)  
  108.         {  
  109.             list = new LinkedList();  
  110.             switchhistory.set(list);  
  111.         }  
  112.         else  
  113.         {  
  114.             list = (LinkedList)switchhistory.get();  
  115.         }  
  116.         list.add(0,sessionfactory);  
  117.           
  118.     }  
  119.     /** 
  120.      * pop sessionfactory in queue 
  121.      */  
  122.     private static SessionFactory popHistory()  
  123.     {  
  124.         if (switchhistory.get()!=null)  
  125.         {  
  126.             LinkedList list = (LinkedList)switchhistory.get();  
  127.             if (list.size()>0)  
  128.             {  
  129.                 SessionFactory factory = (SessionFactory)list.getFirst();  
  130.                 list.removeFirst();  
  131.                 return factory;  
  132.             }  
  133.         }  
  134.         return null;  
  135.     }  
  136.       
  137.     public static Map getTraceMap()  
  138.     {  
  139.         if (trace.get()!=null)  
  140.         {  
  141.             return (Map)trace.get();  
  142.         }  
  143.         return null;  
  144.     }  
  145.       
  146.     public static SessionFactory getCurrentFactory()  
  147.     {  
  148.         return (SessionFactory)curfactory.get();  
  149.     }  
  150.       
  151.     public static synchronized void release()  
  152.     {  
  153.         idset.set(null);  
  154.         curfactory.set(null);  
  155.         switchhistory.set(null);  
  156.         trace.set(null);  
  157.     }  
  158.       
  159.     /** 
  160.      * °ó¶¨sessionFactoryµ½springµÄ×ÊÔ´¹ÜÀí 
  161.      * @param hibernate's sessionfactory 
  162.      */  
  163.     private static synchronized boolean bindSessionFactory(SessionFactory sessionFactory)  
  164.     {  
  165.         boolean participate=false;;  
  166.         if (singleSession) {  
  167.             // single session mode  
  168.             if (TransactionSynchronizationManager.hasResource(sessionFactory)) {  
  169.                 // Do not modify the Session: just set the participate flag.  
  170.                 participate = true;  
  171.             }  
  172.             else {  
  173.                 logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");  
  174.                 Session session = getSession(sessionFactory);  
  175.                 if (!TransactionSynchronizationManager.hasResource(sessionFactory))  
  176.                 {     
  177.                     TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));  
  178.                 }  
  179.             }  
  180.         }  
  181.         else {  
  182.             // deferred close mode  
  183.             if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {  
  184.                 // Do not modify deferred close: just set the participate flag.  
  185.                 participate = true;  
  186.             }  
  187.             else {  
  188.                 SessionFactoryUtils.initDeferredClose(sessionFactory);  
  189.             }  
  190.         }  
  191.         return participate;  
  192.     }  
  193.       
  194.     //see SessionFactoryUtils  
  195.     private static  Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {  
  196.         Session session = SessionFactoryUtils.getSession(sessionFactory, true);  
  197.         FlushMode flushMode = FlushMode.COMMIT;  
  198.         if (flushMode != null) {  
  199.             session.setFlushMode(flushMode);  
  200.         }  
  201.         return session;  
  202.     }  
  203.   
  204.     public static synchronized void initSessionFactory(Map res,Class loadclass)  
  205.     {  
  206.         factorybeanset =res;  
  207.     }  
  208.   
  209.       
  210. }  
HibernateSupport这个类其他方法可以不管,暂时关注setCurrent这个方法

java 代码
  1. if (idset.get()==null)
  2. {
  3. idset.set(id);
  4. if (factorybeanset.containsKey(id)) //factorybeanset包含的就是我自己系统配置中那一部分,key就是id,,value就是sessionFactory 在spring环境中的beanName
  5. {
  6. if (applicationContext==null)
  7. {
  8. applicationContext =WebApplicationContextUtils.getWebApplicationContext(context);
  9. }
  10. curfactory.set((SessionFactory)applicationContext.getBean((String)factorybeanset.get(id)));//设置当前的sessionFactory
  11. putTrace(idset.get(),(SessionFactory)curfactory.get());//put到当前线程的一个记录集
  12. }
  13. }
然后,就要修改spring关于hibernate的一些支持类了,当然,也可以选择重新写一套dao支持类,呵呵,不过,显然,在spring基础上做一些小修改代价更小 HibernateAccessorHibernateTemplate的基类)以及HibernateTransactionManager都是靠注入方式获取一个sessionFactory,显然,这套不适合了,修改之

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

java 代码
  1. private SessionFactory sessionFactory;
  2. publicvoid setSessionFactory(SessionFactory sessionFactory) {
  3. this.sessionFactory = sessionFactory;
  4. }

去掉HibernateAccessorHibernateTransactionManager中的上述两段代码,当然,也别忘了毙掉两个类中的

afterPropertiesSet方法中那些检查代码

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

如果想变更sessionFactoryUtils,奉劝算了吧..

然后可以做测试了,首先,部署测试的daoservice,先是事务部署

xml 代码
  1. <beanid="transactionManager"
  2. class="com.mit.web.hibernate.CHibernateTransactionManager"/>
  3. <beanid="transres"class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
  4. <propertyname="properties">
  5. <props>
  6. <propkey="load*">PROPAGATION_REQUIRED,readOnlyprop>
  7. <propkey="save*">PROPAGATION_REQUIREDprop>
  8. <propkey="delete*">PROPAGATION_REQUIREDprop>
  9. <propkey="find*">PROPAGATION_REQUIRED,readOnlyprop>
  10. <propkey="query*">PROPAGATION_REQUIRED,readOnlyprop>
  11. <propkey="create*">PROPAGATION_REQUIREDprop>
  12. <propkey="set*">PROPAGATION_REQUIRED,readOnlyprop>
  13. <propkey="execute*">PROPAGATION_REQUIREDprop>
  14. props>
  15. property>
  16. bean>
  17. <beanid="transactionInterceptor"class=span

家里网络环境不好,本来想修改下里面的代码格式,结果成了半截子,我转到回复里面来写把3
事务配置如下:

Java代码 复制代码
  1.  <bean id="transactionManager"     
  2.          class="com.mit.web.hibernate.CHibernateTransactionManager"/>   
  3.   
  4. <bean id="transres" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">   
  5.     <property name="properties">   
  6.         <props>   
  7.             <prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>   
  8.             <prop key="save*">PROPAGATION_REQUIRED</prop>   
  9.             <prop key="delete*">PROPAGATION_REQUIRED</prop>   
  10.             <prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>   
  11.             <prop key="query*">PROPAGATION_REQUIRED,readOnly</prop>   
  12.             <prop key="create*">PROPAGATION_REQUIRED</prop>   
  13.             <prop key="set*">PROPAGATION_REQUIRED,readOnly</prop>   
  14.             <prop key="execute*">PROPAGATION_REQUIRED</prop>   
  15.         </props>   
  16.     </property>   
  17. </bean>   
  18.   
  19. <bean id="transactionInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">     
  20.        <property name="transactionManager" ref="transactionManager"/>     
  21.      <property name="transactionAttributeSource"><ref local="transres"/></property>   
  22.    </bean>     
  23.      
  24.    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">     
  25.  <property name="beanNames"><value>*Service</value></property>   
  26.        <property name="interceptorNames">     
  27.            <list>     
  28.                <value>transactionInterceptor</value>     
  29.            </list>     
  30.        </property>     
  31.    </bean>     
  32.     
  33.    <bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">     
  34.      <property name="transactionInterceptor" ref="transactionInterceptor"/>     
  35.    </bean>   
	 <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代码 复制代码
  1. <bean id="personDao" class="com.mit.web.action.PersonDaoImpl"/>    
  2. <bean id="personService" class="com.mit.web.action.PersonServiceImpl">    
  3.     <property name="personDao">    
  4.      <ref bean="personDao"/>    
  5.     </property>    
  6.  </bean>    
  7.     
  8. <bean id="departDao" class="com.mit.web.action.DepartDaoImpl"/>    
  9. <bean id="departService" class="com.mit.web.action.DepartServiceImpl">    
  10.     <property name="personService">    
  11.         <ref bean="personService"/>    
  12.     </property>    
  13.     <property name="departDao">    
  14.         <ref bean="departDao"/>    
  15.     </property>    
  16. </bean>   
<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> 


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

Java代码 复制代码
  1. public class PersonDaoImpl extends CHibernateDaoSupport implements PersonDao;    
  2. public class PersonServiceImpl implements PersonService;    
  3. public class DepartDaoImpl extends CHibernateDaoSupport implements DepartDao;    
  4. public class DepartServiceImpl implements DepartService;   
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代码 复制代码
  1. public class DepartServiceImpl implements DepartService {    
  2. private DepartDao dao;    
  3. private PersonService service;    
  4.  ..............................    
  5.  public void executeTest(Depart depart,Person person)    
  6.  {    
  7.     dao.save(depart);    
  8.     HibernateSupport.switchFactory("test");    
  9.     service.save(person);    
  10.     HibernateSupport.restoreFactory();    
  11. }    
  12.  ..............................   
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对象

Java代码 复制代码
  1. private ThreadLocal lazytrace = new ThreadLocal();  
private ThreadLocal lazytrace = new ThreadLocal();


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

Java代码 复制代码
  1. private static class LazyTransactionStatus   
  2. {   
  3.     private java.util.List transliat;   
  4.     private int transnum;//代表未提交事务数量   
  5.        
  6.     public LazyTransactionStatus()   
  7.     {   
  8.         transliat= new java.util.ArrayList();   
  9.         transnum=0;   
  10.     }   
  11.        
  12.     public void newLazy()   
  13.     {   
  14.         transnum++;   
  15.     }   
  16.        
  17.     public void push(TransactionStatus trasobj)   
  18.     {      
  19.         objmap.add(trasobj);   
  20.     }   
  21.        
  22.     public void removeLazy(TransactionStatus trasobj)   
  23.     {   
  24.         transnum--;   
  25.     }   
  26.        
  27.     public boolean canCommit()   
  28.     {   
  29.         if (transnum<1)   
  30.         {   
  31.             return true;   
  32.         }   
  33.         else  
  34.             return false;   
  35.     }   
  36.        
  37.     public java.util.List getTransactionObjs()   
  38.     {   
  39.         return transliat;   
  40.     }   
  41. }   
	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代码 复制代码
  1. public final boolean isCommit(TransactionStatus status)   
  2. {   
  3.     if (lazytrace.get()!=null)   
  4.     {   
  5.         LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();   
  6.         lazystatus.removeLazy(status);   
  7.         return lazy.canCommit();   
  8.     }   
  9.     return true;   
  10. }   
  11.   
  12. protected void begin(Object transaction, TransactionDefinition definition)   
  13. {   
  14.     doBegin(transaction,definition);   
  15.     LazyTransactionStatus lazystatus = null;   
  16.     if (lazytrace.get()==null)   
  17.     {   
  18.         lazystatus = new LazyTransactionStatus();   
  19.         lazytrace.set(lazystatus);   
  20.     }   
  21.     else  
  22.     {   
  23.         lazystatus = (LazyTransactionStatus)lazytrace.get();   
  24.     }   
  25.     lazystatus.newLazy();   
  26. }   
  27.   
  28. public final void registerTraceStatus(TransactionStatus status)   
  29. {   
  30.     LazyTransactionStatus lazystatus = null;   
  31.     if (lazytrace.get()==null)   
  32.     {   
  33.         lazystatus = new LazyTransactionStatus();   
  34.         lazytrace.set(lazystatus);   
  35.     }   
  36.     else  
  37.     {   
  38.         lazystatus = (LazyTransactionStatus)lazytrace.get();   
  39.     }   
  40.     lazystatus.push(status);   
  41. }  
	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代码 复制代码
  1. public final void commit(TransactionStatus txstatus) throws TransactionException {   
  2.     this.registerTraceStatus(txstatus);   
  3.     if (this.isCommit(txstatus))   
  4.     {   
  5.         int error = 0;   
  6.         LazyTransactionStatus lazystatus = (LazyTransactionStatus)lazytrace.get();   
  7.         List statuslist = lazystatus.getTransactionObjs();   
  8.         for (int i=0;i<statuslist.size();i++)   
  9.         {   
  10.             try  
  11.             {   
  12.                 TransactionStatus status = (TransactionStatus)statuslist.get(i);   
  13.                 if (status.isCompleted()) {   
  14.                     error++;   
  15.                     continue;   
  16.                     //throw new IllegalTransactionStateException(   
  17.                     //      "Transaction is already completed - do not call commit or rollback more than once per transaction");   
  18.                 }   
  19.            
  20.                 DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;   
  21.                 if (defStatus.isLocalRollbackOnly()) {   
  22.                     if (defStatus.isDebug()) {   
  23.                         logger.debug("Transactional code has requested rollback");   
  24.                     }   
  25.                     processRollback(defStatus);   
  26.                     error++;   
  27.                     continue;   
  28.                 }   
  29.                 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {   
  30.                     if (defStatus.isDebug()) {   
  31.                         logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");   
  32.                     }   
  33.                     processRollback(defStatus);   
  34.                     // Throw UnexpectedRollbackException only at outermost transaction boundary   
  35.                     // or if explicitly asked to.   
  36.                     if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {   
  37.                         //throw new UnexpectedRollbackException(   
  38.                                 //"Transaction rolled back because it has been marked as rollback-only");   
  39.                         error++;   
  40.                         continue;   
  41.                     }   
  42.                     continue;   
  43.                 }   
  44.                    
  45.                 processCommit(defStatus);   
  46.             }   
  47.             catch (Exception ex)   
  48.             {   
  49.                 error++;   
  50.                 ex.printStackTrace();   
  51.                 continue;   
  52.             }   
  53.                
  54.         }   
  55.         lazytrace.set(null);   
  56.         if (error>0)   
  57.             throw new IllegalTransactionStateException(   
  58.                         "Not commit all transaction");   
  59.     }   
  60. }  
	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两种情况,测试都通过了,呵呵

xml 代码
  1. <hibernatesupport>
  2. <itemid="mit"bean="mitSessionFactory"/>
  3. <itemid="test"bean="testSessionFactory"/>
  4. <hibernatesupport>
xml 代码
 
  1. <bean id="mitDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
  2.       <property name="driverClass">  
  3.        <value>com.mysql.jdbc.Driver</value>  
  4.       </property>  
  5.       <property name="jdbcUrl">  
  6.        <value>jdbc:mysql://127.0.0.1:3306/mitflow</value>  
  7.       </property>  
  8.       <property name="user">  
  9.        <value>root</value>  
  10.       </property>  
  11.     ...........................  
  12.  </bean>  
  13.   
  14. <bean id="mitSessionFactory"  
  15.   class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  
  16.   <property name="mappingResources">  
  17.         ..................................  
  18.     </props>  
  19.   </property>  
  20.     
  21.    <property name="dataSource">  
  22.      <ref local="mitDataSource" />  
  23.    </property>  
  24.  </bean>  

 

分享到:
评论

相关推荐

    Spring多数据源解决方案

    Spring多数据源解决方案是针对大型应用中数据分片和分布式数据库管理的需求而设计的一种策略。在这样的场景下,为了提高系统的可扩展性和性能,数据通常会被分散到多个数据库实例上。这种架构如图1所示,每个数据库...

    基于Spring多数据源实例

    在IT行业中,数据库是系统的核心组成部分,特别是...通过理解上述步骤和原理,开发者可以更好地设计和实施适用于各种业务场景的多数据源解决方案。在实际操作中,应根据项目需求灵活调整,以达到最佳的性能和可维护性。

    java spring 多数据源

    在Java Spring框架中,多数据源的实现是一个重要的特性,特别是在大型企业级应用中,它允许应用程序连接到多个数据库,从而实现数据隔离、负载均衡或读写分离等高级功能。本教程将深入探讨如何在Spring中配置和管理...

    spring多数据源

    在许多实际项目中,我们可能需要连接并操作多个数据库,比如主从数据库、读写分离、不同环境的数据隔离等,这时就需要用到Spring的多数据源支持。 Spring多数据源允许我们在一个应用中同时管理多个数据库连接,通过...

    Spring多数据源分布式事务管理

    在大型分布式系统中,往往需要处理多个数据源,这就涉及到了Spring多数据源的配置和管理。同时,为了保证数据的一致性,分布式事务的管理也是必不可少的。在这个场景下,Atomikos作为一款开源的JTA(Java ...

    spring多数据源动态切换方案

    "Spring多数据源动态切换方案"是解决这一问题的关键技术,它允许应用程序根据业务需求动态选择数据源,提高了系统的灵活性和可扩展性。下面我们将详细探讨如何在Spring中实现多数据源的配置及动态切换。 首先,我们...

    mybatis spring 多数据源

    标签中的"源码"可能指的是理解MyBatis和Spring框架内部如何处理多数据源的关键代码,这有助于我们更深入地定制和优化解决方案。"工具"可能是指像Druid、HikariCP这样的数据库连接池,它们在多数据源配置中起着至关...

    springboot多数据源即分布式事务解决方案

    SpringBoot作为一款轻量级的框架,提供了便捷的多数据源配置和分布式事务管理方案,使得开发者能够高效地管理和操作不同的数据库。本文将详细探讨SpringBoot如何实现多数据源以及分布式事务。 首先,我们要理解什么...

    Spring多数据源配置

    Spring多数据源配置,支持mysql、oracle等多个数据源同时存在的情况

    spring boot mybatis多数据源最简解决方案

    本文将深入探讨如何实现Spring Boot结合MyBatis的多数据源最简解决方案。 首先,我们来理解多数据源的需求。在大型分布式系统中,通常采用数据库主从复制或者分库分表策略来提高系统的可扩展性和性能。主从模式可以...

    spring boot 2多数据源,里面有hibernate和mybatis的多数据源代码

    在Spring Boot 2框架中,实现多数据源的配置是一项重要的任务,特别是在大型企业级应用中,可能需要连接到不同的数据库来满足不同业务的需求。在这个项目中,我们有两个主要的数据访问技术:Hibernate和MyBatis,...

    Spring配置多个数据源

    4. **配置多数据源事务管理器** 为了处理涉及多个数据源的事务,我们需要使用`PlatformTransactionManager`的实现,例如`DataSourceTransactionManager`,并指定对应的数据源: ```java @Bean(name = ...

    Spring Boot多数据源(支持Spring声明式事务切换和回滚).pdf

    《Spring Boot多数据源(支持Spring声明式事务切换和回滚)》 Spring Boot多数据源技术是构建高效、灵活的多租户SaaS架构的关键。在本文中,我们将深入探讨如何实现动态数据源切换,支持Spring声明式事务管理,并讨论...

    Spring+Hibernate多数据源

    接着,描述中的"Spring+Hibernate多数据源的整合实现demo"意味着这是一个实际操作的示例,它可能包含了一个或多个配置文件和Java代码,展示了如何在Spring Boot或者传统的Spring环境下配置和使用多数据源。在Spring ...

    spring动态数据源+mybatis分库分表

    "spring动态数据源+mybatis分库分表"是一个针对大型数据库场景的解决方案,它利用Spring框架的动态数据源功能和MyBatis的SQL映射能力,实现数据库的透明化分片。以下是这个主题的详细知识点: 1. **Spring动态数据...

    springboot多数据源即分布式事务解决方案,添加对多线程的支持

    综上所述,Spring Boot通过其强大的框架能力,为开发者提供了实现多数据源操作、分布式事务管理和多线程支持的解决方案。开发者只需进行适当的配置和编码,就能在复杂的业务场景中确保数据的完整性和一致性。在实际...

    spring 动态多数据源配置代码

    下面将详细介绍Spring动态多数据源配置的相关知识点。 1. **为什么要使用多数据源**: 在实际项目中,可能需要连接到不同的数据库,例如,一个用于存储主业务数据,另一个用于日志记录或数据分析。通过多数据源...

    Spring Boot+Jpa多数据源配置Demo(支持不同数据库)

    确保所有配置正确后,编写单元测试或集成测试以验证多数据源的配置是否正常工作。使用`@TestPropertySource`或`@ActiveProfiles`注解来指定测试时使用的配置文件,以便在不同数据源之间切换。 总结来说,Spring ...

    Springcloud 多数库 多数据源整合,查询动态切换数据库

    本主题聚焦于在Spring Cloud环境中实现多数据库和多数据源的整合,并且能够动态切换查询的数据库。这是一个复杂但至关重要的需求,特别是在大型企业级应用中,可能需要根据业务逻辑或用户权限连接到不同的数据库。 ...

    spring整合mybatis多数据源

    总的来说,Spring整合MyBatis的多数据源切换是解决复杂数据库应用场景的有效手段。通过灵活的数据源路由,我们可以轻松地实现读写分离、数据库分片等高级架构,提升系统的可扩展性和性能。理解并掌握这一技术对于...

Global site tag (gtag.js) - Google Analytics