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

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

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

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

xml 代码
  1. <hibernatesupport>
  2. <item id="mit" bean="mitSessionFactory"/>
  3. <item id="test" bean="testSessionFactory"/>
  4. < 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 代码
 
  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 基础上做一些小修改代价更小 <o:p></o:p> HibernateAccessor HibernateTemplate 的基类)以及 HibernateTransactionManager 都是靠注入方式获取一个 sessionFactory ,显然,这套不适合了,修改之

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

java 代码
  1. private SessionFactory sessionFactory;
  2. publicvoid setSessionFactory(SessionFactory sessionFactory) {
  3. this.sessionFactory = sessionFactory;
  4. }
<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>

然后可以做测试了,首先,部署测试的daoservice,先是事务部署<o:p></o:p>

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值