spring多数据源事务配置

Lindp 2014-09-13 09:34:11
我们项目现在需要做一个数据同步的功能,使用两个数据源链接两个不同地址相同结构的数据库(MySQL),我配置了AbstractRoutingDataSource来切换数据源。
使用定时器每小时调用A数据库数据保存到B中。其中需要配置事务,如果A出错AB都要回滚。这一步不知道是不是需要使用分布式事务(Atomikos)?

context.xml:

<bean id="dataSourceLocal" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="dsLocal" />
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="url">${jdbc.local.url}</prop>
<prop key="user">${jdbc.local.username}</prop>
<prop key="password">${jdbc.local.password}</prop>
</props>
</property>
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="borrowConnectionTimeout" value="30" />
<property name="testQuery" value="select 1" />
<property name="maintenanceInterval" value="60" />
</bean>

<bean id="dataSourceRemote" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" destroy-method="close">
<property name="uniqueResourceName" value="dsRemote" />
<property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" />
<property name="xaProperties">
<props>
<prop key="url">${jdbc.remote.url}</prop>
<prop key="user">${jdbc.remote.username}</prop>
<prop key="password">${jdbc.remote.password}</prop>
</props>
</property>
<property name="minPoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="borrowConnectionTimeout" value="30" />
<property name="testQuery" value="select 1" />
<property name="maintenanceInterval" value="60" />
</bean>

<!-- 多数据源配置 -->
<bean id="dynamicDataSource" class="com.ht.ourally.common.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry value-ref="dataSourceLocal" key="dataSourceLocal"></entry>
<entry value-ref="dataSourceRemote" key="dataSourceRemote"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSourceRemote"></property>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dynamicDataSource" />
<property name="entityInterceptor" ref="dorado.unByteCodeProxyInterceptor" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.ht.ourally.*.entity</value>
<value>com.ht.mobile.*.entity</value>
<value>com.ht.alipay.entity</value>
</list>
</property>
</bean>

<bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" init-method="init" destroy-method="close">
<property name="forceShutdown">
<value>true</value>
</property>
</bean>

<bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
<property name="transactionTimeout" value="300" />
</bean>

<!-- spring 事务管理器 -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="atomikosTransactionManager" />
<property name="userTransaction" ref="atomikosUserTransaction" />
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" rollback-for="Exception,RuntimeException" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>

<aop:config>
<aop:advisor pointcut="execution(* com.ht.ourally.common.task..*.*(..))" advice-ref="txAdvice" />
</aop:config>


<!-- 使用annotation定义事务,对于要加入事物的类,只需对该类加 @Transactional -->
<!-- <tx:annotation-driven transaction-manager="transactionManager" /> -->

<context:annotation-config />
<context:component-scan base-package="com.ht" />
<task:annotation-driven/>


DynamicDataSource:

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getCustomerType();
}
}

DBContextHolder:

public class DBContextHolder {

public static final String DATASOURCE_LOCAL = "dataSourceLocal";

public static final String DATASOURCE_REMOTE = "dataSourceRemote";

private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

public static void setCustomerType(String customerType) {
contextHolder.set(customerType);
}

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

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


TakeRemoteTask定时器:

@Scheduled(cron = "*/5 * * * * ?")
public void main() {
System.out.println("==========================抓取远程数据库数据开始");
try {
this.doBaseCompany();
System.out.println("==========================抓取远程数据库数据结束");
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* @Title: doBaseCompany
* @Description: TODO 物业企业任务
* @author lindapeng
* @return
*/
private boolean doBaseCompany() {
if (!Boolean.valueOf(prop.get("BaseCompany").toString()))
return true;
System.err.println(DBContextHolder.getCustomerType());
DBContextHolder.setCustomerType(DBContextHolder.DATASOURCE_LOCAL); // 切换数据源到本地
System.err.println(DBContextHolder.getCustomerType());
SysRole role = new SysRole();
role.setCompanyId("lindp1");
role.setRoleName("lindp1");
sysRoleService.addSysRole(role);

System.err.println(DBContextHolder.getCustomerType());
DBContextHolder.setCustomerType(DBContextHolder.DATASOURCE_REMOTE); // 切换数据源到远程
System.err.println(DBContextHolder.getCustomerType());
SysRole role2 = new SysRole();
role2.setCompanyId("lindp2");
role2.setRoleName("lindp2");
sysRoleService.addSysRole(role2);
return true;
}


我以这种配置运行有两种问题:
1.切换数据源无效,lindp1和lindp2都会保存到远程数据库中
2.去除事务配置后切换数据源正常,只是没有事务了
...全文
4736 16 打赏 收藏 转发到动态 举报
写回复
用AI写文章
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
达兔哥 2017-03-08
  • 打赏
  • 举报
回复
为什么 多个数据源在同一个Service下进行了回滚,只会有1个能回滚,是不是spring 默认只会代理一个?
qq_35577069 2016-09-26
  • 打赏
  • 举报
回复
梦醒无尘,请问您,如果写在controller层,怎么保证事务的原子性
梦醒无尘 2015-12-23
  • 打赏
  • 举报
回复
网上看到的---------------------------------------- 可以通过继承AbstractRoutingDataSource实现多数据源动态切换,但是需要注意事务的处理。当使用@Transactional注释时该注释会去getConnection(),如果在这之前没有手动切换数据源则拿到的连接无法确定。所以必须将@Transactional延迟到切换了数据源后再声明,目前的解决方案是:在controller层切换数据源,controller中调用的service方法上声明@Transactional。 这也证明了你的第二种方式猜测正确,
星幻凌然 2015-12-10
  • 打赏
  • 举报
回复
这个问题到处没解决啊,坑爹了
肖老板 2015-11-11
  • 打赏
  • 举报
回复
碰到了同样的问题,蛋疼中。。。。
  • 打赏
  • 举报
回复
这种声明式事务有点难吧..除非用EJB 多数据源事务如果不用EJB还是编程式估计会好一点
yl784248831 2014-09-22
  • 打赏
  • 举报
回复
可以试试spring Hessian
源码空间 2014-09-22
  • 打赏
  • 举报
回复
注入两个SessionFactory
zy_think123 2014-09-16
  • 打赏
  • 举报
回复
但是总体来说总比你配置错误好点
Lindp 2014-09-15
  • 打赏
  • 举报
回复
引用 6 楼 zy353003874 的回复:
注入两个SessionFactory你觉得可以么?
我现在就在想这种方式,创建两个Dao分别注入不同SessionFactory。这种方式不知道可行不可行,使用这种方式无端添加我的工作量,我需要去添加第二个Dao
zy_think123 2014-09-15
  • 打赏
  • 举报
回复
注入两个SessionFactory你觉得可以么?
Lindp 2014-09-15
  • 打赏
  • 举报
回复
引用 2 楼 ZuxiangHuang 的回复:
使用JTA事务, 用jpa 配置 两个<xa-datasource> 数据源,指定jta事务,
分布式事务我配置过,使用Atomikos 可是总是有问题啊。是我配置的有问题吗?我上面把使用Atomikos配置分布式事务的问题描述了一下。
Lindp 2014-09-15
  • 打赏
  • 举报
回复
引用 3 楼 zhang222jie 的回复:
我使用的是jboss7,你说问题都可以解决
换个服务器就能解决我的问题?请问怎么解决啊 ?
zhang222jie 2014-09-14
  • 打赏
  • 举报
回复
我使用的是jboss7,你说问题都可以解决
zuxianghuang 2014-09-13
  • 打赏
  • 举报
回复
使用JTA事务, 用jpa 配置 两个<xa-datasource> 数据源,指定jta事务,
Lindp 2014-09-13
  • 打赏
  • 举报
回复
我在项目中发现了另一种写法,不知道这种写法对性能会不会有影响或者会有其他的问题。 请大家帮忙给看下,谢谢。上代码

@Component
public class TakeRemoteTask extends BaseAction {

	@Resource
	private SessionFactory sessionFactory; // 注入SessionFactory

	private static Properties prop = new Properties();

	static {
		try {
			FileInputStream fileInputStream = new FileInputStream(new File(TakeRemoteTask.class.getResource("/").getPath() + "/take.properties"));
			prop.load(fileInputStream);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @Title: main
	 * @Description: TODO 抓取远程数据库数据主任务
	 * @author lindapeng
	 */
	@Scheduled(cron = "*/5 * * * * ?")
	public void main() {
		System.out.println("==========================抓取远程数据库数据开始");
		try {
			this.doBaseCompany();
			System.out.println("==========================抓取远程数据库数据结束");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * @Title: doBaseCompany
	 * @Description: TODO 物业企业任务
	 * @author lindapeng
	 * @return
	 * @throws Exception 
	 */
	private boolean doBaseCompany() throws Exception {
		if (!Boolean.valueOf(prop.get("BaseCompany").toString()))
			return true;
		Session session = null;
		Transaction transaction = null;
		
		Session sessionR = null;
		Transaction transactionR = null;
		try {
			DBContextHolder.setCustomerType(DBContextHolder.DATASOURCE_LOCAL); // 切换本地DataSource
			session = sessionFactory.openSession(); // 打开一个本地Session
			transaction = session.beginTransaction();
			SysRole role1 = new SysRole();
			role1.setCompanyId("lindp1");
			role1.setRoleName("lindp1");
			session.save(role1);
			
			DBContextHolder.setCustomerType(DBContextHolder.DATASOURCE_REMOTE); // 切换远程DataSource
			sessionR = sessionFactory.openSession(); // 打开一个远程Session
			transactionR = sessionR.beginTransaction();
			SysRole role2 = new SysRole();
			role2.setCompanyId("lindp2");
			role2.setRoleName("lindp2");
			sessionR.save(role2);
			
			// 同时提交
			transaction.commit();
			transactionR.commit();
		} catch (Exception e) {
			// 同时回滚
			transaction.rollback();
			transactionR.rollback();
			e.printStackTrace();
		} finally {
			session.close();
			sessionR.close();
		}
		
//		System.err.println(roleService.findSysRoleListByMap(null).size());
//		
//		DBContextHolder.setCustomerType(DBContextHolder.DATASOURCE_REMOTE);
//		System.err.println(roleService.findSysRoleListByMap(null).size());
		return true;
	}
}

81,092

社区成员

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

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