Spring分布式事务在service中无法动态切换数据源

hi_kevin 2013-08-09 09:37:22
Spring分布式事务在service中无法切换数据源
项目采用的是struts2+spring+ibatis架构,下面是关键部分代码:

applicationContext.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:context="http://www.springframework.org/schema/context"
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.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
default-autowire="byName" default-lazy-init="false">

<context:component-scan base-package="com.ssi.*" />
<!-- 属性文件读入 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath*:jdbc.properties</value>
</list>
</property>
</bean>
<!-- JTA 数据源配置 -->
<bean id="center" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/center</value>
</property>
<property name="xaDataSourceClassName">
<value>${jta.driver.className}</value>
</property>
<property name="xaProperties">
<props>
<prop key="url">${center.jdbc.driver.url}</prop>
<prop key="user">${center.sql.user.name}</prop>
<prop key="password">${center.sql.user.password}</prop>
</props>
</property>
<property name="testQuery" value="select 1" />
<property name="poolSize">
<value>${poolsize}</value>
</property>
<property name="maxPoolSize">
<value>${maxPoolSize}</value>
</property>
<property name="borrowConnectionTimeout"><value>${borrowConnectionTimeout}</value></property>
</bean>

<bean id="db1" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/db1</value>
</property>
<property name="xaDataSourceClassName">
<value>${jta.driver.className}</value>
</property>
<property name="xaProperties">
<props>
<prop key="url">${db1.jdbc.driver.url}</prop>
<prop key="user">${company.sql.user.name}</prop>
<prop key="password">${company.sql.user.password}</prop>
</props>
</property>
<property name="testQuery" value="select 1" />
<property name="poolSize">
<value>${poolsize}</value>
</property>
<property name="maxPoolSize">
<value>${maxPoolSize}</value>
</property>
<property name="borrowConnectionTimeout"><value>${borrowConnectionTimeout}</value></property>
</bean>
<bean id="db2" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName">
<value>mysql/db2</value>
</property>
<property name="xaDataSourceClassName">
<value>${jta.driver.className}</value>
</property>
<property name="xaProperties">
<props>
<prop key="url">${db2.jdbc.driver.url}</prop>
<prop key="user">${company.sql.user.name}</prop>
<prop key="password">${company.sql.user.password}</prop>
</props>
</property>
<property name="testQuery" value="select 1" />
<property name="poolSize">
<value>${poolsize}</value>
</property>
<property name="maxPoolSize">
<value>${maxPoolSize}</value>
</property>
<property name="borrowConnectionTimeout"><value>${borrowConnectionTimeout}</value></property>
</bean>

<bean id="dataSource" class="com.ssi.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="db1" value-ref="db1" />
<entry key="db2" value-ref="db2" />
<entry key="center" value-ref="center" />
</map>
</property>
<property name="defaultTargetDataSource" ref="center" />
</bean>

<!-- 配置sqlMapclient -->
<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
<property name="configLocation" value="classpath:ibatis-sqlmap-config.xml" />
<property name="dataSource" ref="dataSource" />
</bean>



<!-- 支持 @AspectJ 标记-->
<aop:aspectj-autoproxy proxy-target-class="true"/>

<!-- 配置JTA的事务管理器 -->
<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<property name="forceShutdown" value="true" />
</bean>
<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>
<bean id="springTransactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
</bean>
<!-- 配置通知 -->
<tx:advice id="txAdvice" transaction-manager="springTransactionManager">
<tx:attributes>
<tx:method name="*" rollback-for="Exception,RuntimeException,com.ssi.exception.SystemException" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>



<!-- 以AspectJ方式 定义 AOP -->

<aop:config>
<aop:advisor pointcut="execution(* com.ssi.service..*Service*.*(..))" advice-ref="txAdvice" />
</aop:config>

</beans>



UserServiceImpl.java:
public class UserServiceImpl implements IUserService {
@Resource
private IUserDao userDao;
public void addUser(User user) throws Exception{
DbContextHolder.setDbType("db1");
userDao.addUser(user);
DbContextHolder.setDbType("db2");
userDao.addUser(user);
DbContextHolder.setDbType("center");
userDao.addUser(user);
}

public void addUser2(User user,int status) throws Exception{
userDao.addUser(user);
if(status==1){
System.out.println(1/0);
}
}
}

UserDaoImpl.java:
@Repository("userDao")
public class UserDaoImpl extends SqlMapClientDaoSupport implements IUserDao {
@Override
public Integer addUser(User user) throws Exception{
return (Integer) this.getSqlMapClient().insert("User.insert", user);
}

}


DynamicDataSource.java:
public class DynamicDataSource extends AbstractRoutingDataSource {

static Logger log = Logger.getLogger(DynamicDataSource.class);

protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbType();
}

}


DbContextHolder.java:
public class DbContextHolder {
private static final ThreadLocal contextHolder = new ThreadLocal();

public static void setDbType(String dbType) {
contextHolder.set(dbType);
}

public static String getDbType() {
return (String) contextHolder.get();
}

public static void clearDbType() {
contextHolder.remove();
}

}

三个数据库:dbcenter、db1、db2 表结构均相同

脚本:
DROP TABLE IF EXISTS `tb_user`;

CREATE TABLE `tb_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`userName` varchar(20) DEFAULT NULL,
`password` varchar(60) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

单元测试:
public class JunitTest{  
public ApplicationContext cxt;
@Test
public void init() throws Exception{
cxt = new ClassPathXmlApplicationContext(new String[] {"applicationContext.xml"});
testInsertUser1();
testInsertUser2();
}


private void testInsertUser1() throws Exception{
IUserService userService = (IUserService)cxt.getBean("userService");
User user = new User();
user.setUserName("user1");
user.setPassword("0");
userService.addUser(user);
}

private void testInsertUser2() throws Exception{
IUserService userService = (IUserService)cxt.getBean("userService");
User user = new User();
user.setUserName("user2");
user.setPassword("0");

DbContextHolder.setDbType("db1");
userService.addUser2(user,0);
DbContextHolder.setDbType("db2");
userService.addUser2(user,0);
DbContextHolder.setDbType("center");
userService.addUser2(user,1);
}
}


结果:

testInsertUser1中在service方法中切换数据源,结果不会切换,始终都会将数据保存在db1中(不是很明白)

testInsertUser2中在单元测试方法中切换数据源,会切换,但事务不会回滚,即db1和db2会保存成功,db3不会成功(这点会明白:英文事务配置在service中,在单元测试中切换数据源事务不会回滚)


疑问:如何在service方法中动态切换数据源,并且保证事务?
...全文
867 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
hi_kevin 2014-04-11
  • 打赏
  • 举报
回复
可以参考一下这个 http://blog.csdn.net/hi_kevin/article/details/9839775
yxnloveyang 2014-04-07
  • 打赏
  • 举报
回复
你好,你的问题解决了吗?
pyliuhui 2014-02-17
  • 打赏
  • 举报
回复
楼主这个问题解决了吗?同样的问题,google了好几天都没有解决方案啊。
lgl123ok 2013-08-14
  • 打赏
  • 举报
回复
不知道楼主这个问题解决了没,我这边也遇到了同样的问题,JOTM已经配置,但是在service中的一个事务中切换数据源失败。麻烦交流下
Bumpking 2013-08-09
  • 打赏
  • 举报
回复
我觉得aop做数据源切换比较靠谱。
小丑哥_V5 2013-08-09
  • 打赏
  • 举报
回复
可以参考下这个文章 http://blog.csdn.net/boy00fly/article/details/4772066
hi_kevin 2013-08-09
  • 打赏
  • 举报
回复
引用 1 楼 shadowsick 的回复:
要先理解事务,例如你的事务是放在service... 你切换数据库时为了保证事务的完整性,你应该在进入service之前切换掉数据源,因为如果你是在service方法中切换数据源那是不可行的,因为这个时候spring已经打开了一个事务,他会阻止你切换,所以你应在这之前切换,然后进入service方法,这样spring又给你新切换的数据源加上事务了...
如果外面切换的话就用不上分布式事务了,即db1和db2中插入成功,dbcenter中插入失败,则整个事务就不回滚了
小丑哥_V5 2013-08-09
  • 打赏
  • 举报
回复
要先理解事务,例如你的事务是放在service... 你切换数据库时为了保证事务的完整性,你应该在进入service之前切换掉数据源,因为如果你是在service方法中切换数据源那是不可行的,因为这个时候spring已经打开了一个事务,他会阻止你切换,所以你应在这之前切换,然后进入service方法,这样spring又给你新切换的数据源加上事务了...
hsweb 企业后台管理基础框架业务功能现在:权限管理: 权限资源-角色-用户.配置管理: kv结构,自定义配置.可通过此功能配置数据字典.脚本管理: 动态脚本,支持javascript,groovy,java动态编译执行.表单管理: 动态表单,可视化设计表单,自动生成数据库以及系统权限.无需重启直接生效.模块设置: 配合动态表单实现表格页,查询条件自定义.数据库维护: 在线维护数据库,修改表结构,执行sql.数据源管理: 配置多数据源.代码生成器: 在线生成代码,打包下载.可自定义模板.定时任务: 配置定时任务,使用动态脚本编写任务内容.系统监控: 监控系统资源使用情况.缓存监控: 监控缓存情况.访问日志: 记录用户每次操作情况未来:组织架构管理: 地区-机构-部门-职务-人员.工作流管理: activiti工作流,在线配置流程,配合动态表单实现自定义流程.邮件代收: 代收指定邮箱的邮件框架功能全局restful json,前后分离.通用dao,service,controller类,增删改查直接继承即可.通用mybatis配置文件,支持多种条件查询自动生成,支持自动生成insert,update,delete语句,支持和查询相同的各种条件.实现用户,权限管理;基于aop,注解,精确到按钮的权限控制.动态表单功能,可在前端设计表单,动态生成数据库表,提供统一的增删改查接口.在线代码生成器,可自定义模板.动态数据源,支持数据源热加载,热切换,支持分布式事务.数据库支持 mysql,oracle,h2.websocket支持.定时调度支持,可在页面配置定时任务,编写任务脚本执行。演示示例:demo.hsweb.me测试用户:test (test2,test3,test4....) 密码:123456演示项目源码:hsweb-demo技术选型第三方:MVC:spring-boot. 开箱即用,学习成本低,部署方便(main方法运行).ORM:mybatis. 配置灵活,简单方便.JTA:atomikos. 分布式事务,多数据源事务全靠他.Cache:spring-cache. 统一接口,注解使用,simple,redis... 自动切换.Scheduler:quartz. 开源稳定,支持集群.自家:hsweb-commons :通用工具类hsweb-easy-orm :为动态表单设计的orm框架hsweb-expands-compress :文件压缩,解压操作hsweb-expands-office :office文档操作( excel读写,模板导出,word模板导出)hsweb-expands-request: 请求模拟(http,ftp)hsweb-expands-script:动态脚本,动态编译执行java,groovy,javascript,spel,ognl....hsweb-expands-shell:shell执行hsweb-expands-template:各种模板引擎 标签:hsweb

81,091

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧