尝试在代码中动态切换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" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:conf/jdbc.properties</value>
</list>
</property>
</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driverClassName}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" 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"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" 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"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="/WEB-INF/classes/ehcache.xml" />
</bean>
-->

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>


<bean id="dao" class="com.zhangweilin.core.DaoImpl">
<property name="sessionFactory" 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" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource">
<property name="dataSource">
<!--内部XA数据源 -->
<bean class="org.enhydra.jdbc.standard.StandardXADataSource" 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&characterEncoding=UTF-8" />
</bean>
</property>
<property name="user" value="root" />
<property name="password" value="123456" />
</bean>


<bean id="h2datasource" class="org.enhydra.jdbc.pool.StandardXAPoolDataSource">
<property name="dataSource">
<!--内部XA数据源 -->
<bean class="org.enhydra.jdbc.standard.StandardXADataSource" 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&characterEncoding=UTF-8" />
</bean>
</property>
<property name="user" value="zwllxs" />
<property name="password" value="123456" />
</bean>

<bean id="sessionFactory1" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" 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" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" 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" class="com.zhangweilin.core.DaoImpl">
<property name="sessionFactory" ref="sessionFactory1" />
</bean>

<bean id="dao2" class="com.zhangweilin.core.DaoImpl">
<property name="sessionFactory" ref="sessionFactory2" />
</bean>

<bean id="loginLogService2" class="com.zhangweilin.service.impl.LoginLogServiceImpl">
<property name="dao" ref="dao1" />
</bean>

<bean id="articleService2" class="com.zhangweilin.service.impl.ArticleServiceImpl">
<property name="dao" ref="dao2" />
<property name="loginLogService" ref="loginLogService2" />
</bean>

<bean id="jotm" class="org.springframework.transaction.jta.JotmFactoryBean" />
<!-- jtatransactionmanager容器 -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransaction" 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里的事务的方案更好
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值