首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

尝试在代码中动态切换spring的事宜策略

2012-08-29 
尝试在代码中动态切换spring的事务策略用过spring的人都熟悉spring的事务管理机制,并且spring支持jta分布

尝试在代码中动态切换spring的事务策略
    用过spring的人都熟悉spring的事务管理机制,并且spring支持jta分布式事务,那么,我一直在想,能否在代码中动态的切换事务类型呢?如果当前要操作的多个表在同一个数据源上,就直接本地

事务好了,如果当前操作的表分别在不同的数据源上,就切换成spring里配置好的jta事务。也就是说,事务还是用spring的声明式事务,但是用代码手动的选取用哪一个,能不能这样呢??
    我做了个测试,在测试中,配置了两个spring配置文件,一个是使用本地事务,并且只有一个数据源,另一个是jta事务,并且有两个数据源,它们分别如下:

applicationContext-sys.xml(使用本地事务,并且只有一个数据源)

<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"><bean id="propertyConfigurer" value="${driverClassName}" /><property name="url" value="${url}" /><property name="username" value="${username}" /><property name="password" value="${password}" /></bean><bean id="sessionFactory"ref="dataSource" /><property name="hibernateProperties"><props><prop key="hibernate.dialect">${dialect}</prop><prop key="hibernate.default_batch_fetch_size">10</prop><prop key="hibernate.max_fetch_depth">0</prop><prop key="hibernate.show_sql">${show_sql}</prop></props></property><property name="mappingDirectoryLocations"><list><value>classpath*:/com/zhangweilin/po/</value></list></property></bean><bean id="txManager"ref="sessionFactory" /><property name="nestedTransactionAllowed" value="true" /></bean><tx:annotation-driven transaction-manager="txManager" /><aop:config><aop:pointcut id="commonServiceOperation"expression="execution(* com.zhangweilin.service.*Service.*(..))" /><aop:advisor pointcut-ref="commonServiceOperation"advice-ref="txAdvice" /></aop:config><tx:advice id="txAdvice" transaction-manager="txManager"><tx:attributes><tx:method name="init*" rollback-for="Exception" /><tx:method name="save*" rollback-for="Exception" /><tx:method name="create*" rollback-for="Exception" /><tx:method name="login*" rollback-for="Exception" /><tx:method name="add*" rollback-for="Exception" /><tx:method name="update*" rollback-for="Exception" /><tx:method name="modify*" rollback-for="Exception" /><tx:method name="delete*" rollback-for="Exception" /><tx:method name="remove*" rollback-for="Exception" /><tx:method name="clear*" rollback-for="Exception" /><tx:method name="register*" rollback-for="Exception" /><tx:method name="*" read-only="true" /></tx:attributes></tx:advice><!-- <bean id="ehcacheManager"value="/WEB-INF/classes/ehcache.xml" /></bean> --><bean id="jdbcTemplate" ref="dataSource"/></bean><bean id="dao" ref="sessionFactory" /><property name="jdbcTemplate" ref="jdbcTemplate" /></bean> </beans>



另一个:applicationContext-test-jotm.xml(jta事务,并且有两个数据源)


<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd"><!-- jotm配置 --><bean id="mysqldatasource" destroy-method="shutdown">                  <property name="transactionManager" ref="jotm" />                  <property name="driverName" value="com.mysql.jdbc.Driver" />                  <property name="url" value="jdbc:mysql://172.16.18.75:3306/?useUnicode=true&amp;characterEncoding=UTF-8" />              </bean>          </property>          <property name="user" value="root" />          <property name="password" value="123456" />      </bean>          <bean id="h2datasource" destroy-method="shutdown">                  <property name="transactionManager" ref="jotm" />                  <property name="driverName" value="com.mysql.jdbc.Driver" />                  <property name="url" value="jdbc:mysql://localhost:3306/pay?useUnicode=true&amp;characterEncoding=UTF-8" />              </bean>          </property>          <property name="user" value="zwllxs" />          <property name="password" value="123456" />      </bean>          <bean id="sessionFactory1" ref="mysqldatasource"/>          <property name="mappingDirectoryLocations"><list><value>classpath*:/com/zhangweilin/po/</value></list></property>        <property name="hibernateProperties">              <props>                  <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>                  <prop key="hibernate.show_sql">true</prop>                  <prop key="hibernate.jdbc.batch_size">50</prop>                  <prop key="hibernate.cache.use_query_cache">true</prop>                  <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>              </props>          </property>          <!--         <property name="jtaTransactionManager">              <ref bean="jotm" />          </property>         -->      </bean>           <bean id="sessionFactory2" ref="h2datasource"/>              <property name="mappingDirectoryLocations"><list><value>classpath*:/com/zhangweilin/po/</value></list></property>            <property name="hibernateProperties">                  <props>                      <prop key="hibernate.dialect">org.hibernate.dialect.MySQLInnoDBDialect</prop>                      <prop key="hibernate.show_sql">true</prop>                      <prop key="hibernate.jdbc.batch_size">50</prop>                      <prop key="hibernate.cache.use_query_cache">true</prop>                      <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop>                  </props>              </property>               <!--         <property name="jtaTransactionManager">              <ref bean="jotm" />          </property>         -->      </bean>          <bean id="dao1" ref="sessionFactory1" /></bean>     <bean id="dao2" ref="sessionFactory2" /></bean> <bean id="loginLogService2" ref="dao1" /></bean><bean id="articleService2" ref="dao2" /><property name="loginLogService" ref="loginLogService2" /></bean><bean id="jotm" />      <!-- jtatransactionmanager容器 -->      <bean id="transactionManager" ref="jotm" />      </bean>           <tx:advice id="txAdvice2" transaction-manager="transactionManager">          <tx:attributes>              <tx:method name="add*" propagation="REQUIRED" />              <tx:method name="upd*" propagation="REQUIRED" />              <tx:method name="del*" propagation="REQUIRED" />  <tx:method name="*" read-only="true" />        </tx:attributes>      </tx:advice>          <aop:config>          <aop:pointcut id="interceptorPointCuts" expression="execution(* com.zhangweilin.service.*Service.*(..))" />          <aop:advisor id="jotmAdvisor" advice-ref="txAdvice2" pointcut-ref="interceptorPointCuts" />      </aop:config> </beans>



再附上关键的service:
ArticleService.java:

/** * 文章 * @author zhangweilin * */public interface ArticleService extends Service<Article>{    /**     * 测试同时添加分布在不同库上的Article和LoginLog;     * @param article     * @throws Exception      */    public void addArticle(Article article,String name) throws Exception;            public void setName(String name);        public String getName();}


ArticleServiceImpl.java:


/** * 文章 * @author zhangweilin * */public class ArticleServiceImpl extends ServiceImpl<Article> implements ArticleService{    private LoginLogService loginLogService;    private String name="张伟林";    /**     * @param loginLogService the loginLogService to set     */    public void setLoginLogService(LoginLogService loginLogService)    {        System.out.println("setLoginLogService: "+loginLogService);        this.loginLogService = loginLogService;    }    @Override    public void addArticle(Article article,String name) throws Exception    {         save(article);         LoginLog loginLog=new LoginLog();         loginLog.setAddress(name);         loginLog.setAdmin(null);         loginLog.setIp("11.11.11.11");//         loginLog.setLoginTime(new Date());                  System.out.println(" 添加");         loginLogService.save(loginLog);         System.out.println("操作结束");    }    @Override    public String getName()    {        System.out.println("getName: "+name);        return this.name;    }    @Override    public void setName(String name)    {        System.out.println("setName: "+name);        this.name=name;            }


再附上测试代码:


package com.zhangweilin.test;import java.util.Date;import org.aopalliance.aop.Advice;import org.junit.BeforeClass;import org.junit.Test;import org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor;import org.springframework.beans.factory.BeanFactory;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframework.orm.hibernate3.HibernateTransactionManager;import org.springframework.transaction.PlatformTransactionManager;import org.springframework.transaction.jta.JtaTransactionManager;import org.springframework.transaction.support.AbstractPlatformTransactionManager;import com.zhangweilin.po.Article;import com.zhangweilin.service.ArticleService;import com.zhangweilin.service.LoginLogService;/** * 测试在代码中动态切换spring的事务策略 * @author wlzhang * */public class TestServiceJotm{    private static BeanFactory fac;    private static LoginLogService loginLogService2;    private static ArticleService articleService;    private static HibernateTransactionManager hibernateTransactionManager;    private static PlatformTransactionManager platformTransactionManager;    private static AbstractPlatformTransactionManager abstractPlatformTransactionManager;        /**     * 初始化环境     */    @BeforeClass    public static void init()    {        try        {            fac = new ClassPathXmlApplicationContext("conf\\spring\\applicationContext*.xml");            loginLogService2 = (LoginLogService) fac.getBean("loginLogService2");            articleService = (ArticleService) fac.getBean("articleService2");                   }        catch (Throwable t)        {            t.printStackTrace();        }    }        /**     * 尝试切换事务管理器     */    @Test    public void testChangeTransactionManager()    {        //先测试单例效应        System.out.println("articleService.name: "+articleService.getName());//第一次获取name值        articleService.setName("伟林张");        articleService = (ArticleService) fac.getBean("articleService2");        System.out.println("articleService.name2: "+articleService.getName());//第二次获取name值,如果spring配置采用的单例,那么此获取的就是修改后的name值                //尝试着切换事务管理器        Object jotm=fac.getBean("jotm");        System.out.println("jotm: "+jotm+" , "+jotm.getClass());        Object interceptorPointCuts=fac.getBean("interceptorPointCuts");        System.out.println("interceptorPointCuts: "+interceptorPointCuts+" , "+interceptorPointCuts.getClass());                DefaultBeanFactoryPointcutAdvisor jotmAdvisor=(DefaultBeanFactoryPointcutAdvisor) fac.getBean("jotmAdvisor");        System.out.println("jotmAdvisor: "+jotmAdvisor+" , "+jotmAdvisor.getClass());                hibernateTransactionManager = (HibernateTransactionManager) fac.getBean("txManager");        System.out.println("hibernateTransactionManager: "+hibernateTransactionManager);                platformTransactionManager = (PlatformTransactionManager) fac.getBean("transactionManager");        System.out.println("platformTransactionManager: "+platformTransactionManager);        System.out.println("切换前");        if (platformTransactionManager instanceof JtaTransactionManager)        {            System.out.println("当前事务: JtaTransactionManager");            JtaTransactionManager jtaTransactionManager=(JtaTransactionManager) platformTransactionManager;            System.out.println("转成jtaTransactionManager: "+jtaTransactionManager);            System.out.println("准备切换");            platformTransactionManager=hibernateTransactionManager;  //尝试将hibernate事务管理器赋给jta事务管理器            System.out.println("尝试切换成功:platformTransactionManager: "+platformTransactionManager);                    }        else if (platformTransactionManager instanceof HibernateTransactionManager)        {            System.out.println("当前事务: HibernateTransactionManager");            HibernateTransactionManager jtaTransactionManager=(HibernateTransactionManager) platformTransactionManager;            System.out.println("转成jtaTransactionManager: "+jtaTransactionManager);        }               System.out.println("切换后");        platformTransactionManager = (PlatformTransactionManager) fac.getBean("transactionManager");        System.out.println("platformTransactionManager: "+platformTransactionManager);        if (platformTransactionManager instanceof JtaTransactionManager)        {            //在“切换”后,还是调用的这里。说明切换不成功            System.out.println("当前事务: JtaTransactionManager");            JtaTransactionManager jtaTransactionManager=(JtaTransactionManager) platformTransactionManager;            System.out.println("转成jtaTransactionManager: "+jtaTransactionManager);            System.out.println("准备切换");            platformTransactionManager=hibernateTransactionManager;            System.out.println("尝试切换成功");                    }        else if (platformTransactionManager instanceof HibernateTransactionManager)        {            System.out.println("当前事务: HibernateTransactionManager");            HibernateTransactionManager hibernateTransactionManager=(HibernateTransactionManager) platformTransactionManager;            System.out.println("转成HibernateTransactionManager: "+hibernateTransactionManager);        }                //结果,切换引用是行不通的,而更改现有的单例对象的值是可以的    }        /**     * 切换事务管理器失败,尝试切换 Advisor     */    @Test    public void testChangeAdvisor()    {        DefaultBeanFactoryPointcutAdvisor jotmAdvisor=(DefaultBeanFactoryPointcutAdvisor) fac.getBean("jotmAdvisor");        System.out.println("jotmAdvisor: "+jotmAdvisor+" , "+jotmAdvisor.getClass());        System.out.println("jotmAdvisor: "+jotmAdvisor.getAdviceBeanName());                jotmAdvisor.setAdviceBeanName("txAdvice");        jotmAdvisor=(DefaultBeanFactoryPointcutAdvisor) fac.getBean("jotmAdvisor");        System.out.println("jotmAdvisor: "+jotmAdvisor.getAdviceBeanName());//adviceBeanName名被更改了,。幻想着切换事务的可行性增大了//        Advice advice=jotmAdvisor.getAdvice();    }        /**     * 先不做切换的数据操作,完全符合自己预期     * @throws Exception     */    @Test    public void testArticleService() throws Exception    {        System.out.println("articleService: "+articleService);                Article article=new Article();        article.setAddTime(new Date());        article.setContent("太好了");        article.setTitle("jotm测试下");        article.setType(20);                articleService.addArticle(article,"上海");    }            /**     * Advisor看起来貌似切换成功了,所以正式尝试切换事务来增加数据,测试中保证让数据库端一个表不能正常插入数据,以测试事务的一致性     */    @Test    public void testChangeTrancation()    {        DefaultBeanFactoryPointcutAdvisor jotmAdvisor=(DefaultBeanFactoryPointcutAdvisor) fac.getBean("jotmAdvisor");                System.out.println("使用默认事务:jta事务");                Article article=new Article();        article.setAddTime(new Date());        article.setContent("使用默认事务:jta事务太好了");        article.setTitle("使用默认事务:jta事务_jotm测试下");        article.setType(20);                try        {            articleService.addArticle(article,"使用默认事务:jta事务_上海");        }        catch (Exception e)        {            // TODO Auto-generated catch block            e.printStackTrace();        }                jotmAdvisor.setAdviceBeanName("txAdvice2");  //尝试以更改adviceBeanName的方式修改指定的事务        jotmAdvisor=(DefaultBeanFactoryPointcutAdvisor) fac.getBean("jotmAdvisor");        System.out.println("事务已切换,使用本地事务");                        article=new Article();        article.setAddTime(new Date());        article.setContent("事务已切换,使用本地事务_事务太好了");        article.setTitle("事务已切换,使用本地事务_jotm测试下");        article.setType(20);                try        {            articleService.addArticle(article,"事务已切换,使用本地事务_上海");        }        catch (Exception e)        {            // TODO Auto-generated catch block            e.printStackTrace();        }                /**         *  切换事务失败,事务的一致性并没有符合自己的预期。经过查询文档,关于setAdviceBeanName方法的描述如下,         *  Specify the name of the advice bean that this advisor should refer to.             An instance of the specified bean will be obtained on first access of this advisor&apos;s advice. This advisor will only ever obtain at most one single instance of the advice bean, caching the instance for the lifetime of the advisor.          *  只在第一次访问时才创建,之后不能更改,哎,杯具         */                      }    
   


  不知道有谁能提供更好的测试方案,如果有在代码中切换spring里的事务的方案更好

1 楼 zwllxs 2011-04-15   有没有谁尝试过???  在代码中动态切换当前事务策略?

热点排行