spring多数据源分布式事务的分析与解决方案

一、概述

  1、业务背景

  对老系统进行重构合并,导致新系统需要同时对3个数据库进行管理。由于出现跨库业务,需要实现分布式事务。

  2、开发环境

  spring框架版本  4.3.10.RELEASE

  持久层为结合mybatis写的领域模型,如

  

  每一个entity对应数据库的一张表,@DataSource注解(自定义)了对应数据源的key值。所以一个业务中可能存在数据源的切换。

  事务采用注解@Transaction驱动。

二、spring对多数据源的支持

  spring框架通过抽象类AbstractRoutingDataSource来实现多数据源支持。

  AbstractRoutingDataSource中有一个抽象方法determineCurrentLookupKey()。子类实现该方法即可。

  举例如下:

package com.cmcc.cq.xx.common.mybatis;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class DynamicDataSource extends AbstractRoutingDataSource {


    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSourceType();
    }

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

    public static void setDataSourceType(String dataSourceType){
        contextHolder.set(dataSourceType);
    }

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

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

  ThreadLocal保存当前线程的数据源,执行数据库操作之前会通过解析注解@DataSource设置下次操作的数据源类型,即相应的key值(见后文数据源配置)。

  

  到此,在不使用事务的情况下,可以完成多数据源切换。

三、spring分布式事务支持

  1、DataSourceTransactionManager不支持多数据源

  DataSourceTransactionManager事务管理器是一个单数据源事务管理器,在实例化时候会注入一个数据源。

<bean name="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
	<property name="dataSource" ref="dynamicDataSource">
	</property>
</bean>

  注意:虽然我们注入的是一个动态数据源,但是DataSourceTransactionManager不会因为我们使用动态数据源而跟着变化。

  因为DataSourceTransactionManager开始事务时获取数据库connection时并不是每次都通过dataSource来获取,一般都是通过ConnectionHolder类获得。

  

  所以开启事务会报找不到表或视图的异常。

  同时DataSourceTransactionManager也不能实现跨库事务的一致性。

  2、JtaTransactionManager支持分布式事务

  JtaTransactionManager事务管理器只是提供一个接入方式,并没有做相应的实现,目前比较流行的分布式事务第三方实现是atomikos和jotm。其中jotm更新日期是2010年,建议使用atomikos。

  两者配置也是大同小异,以atomikos为例。

  引入maven依赖 

<dependency>  
    <groupId>com.atomikos</groupId>  
    <artifactId>transactions-jdbc</artifactId>  
    <version>3.7.0</version>  
</dependency>

  首先是数据源的配置,必须要使用类com.atomikos.jdbc.AtomikosDataSourceBean,不能再使用其他的连接池替代。xaDataSourceClassName要根据数据库选择相应的类,在数据库对应的驱动包里可以找到。

<bean id="dataSource3" class="com.atomikos.jdbc.AtomikosDataSourceBean" destroy-method="close">  
	    <property name="uniqueResourceName" value="key值"/>  
	    <property name="xaDataSourceClassName" value="oracle.jdbc.xa.client.OracleXADataSource"/>  
	    <property name="xaProperties">  
	        <props>  
	            <prop key="URL">${jdbc.dataSource.url3}</prop>  
	            <prop key="user">${jdbc.dataSource.username3}</prop>  
	            <prop key="password">${jdbc.dataSource.password3}</prop>  
	        </props>  
	    </property>  
	    <property name="minPoolSize" value="${jdbc.dataSource.minimumIdle3}" />  
	    <property name="maxPoolSize" value="${jdbc.dataSource.maximumPoolSize3}" />  
	    <property name="borrowConnectionTimeout" value="${jdbc.dataSource.connectionTimeout3}" />  
	    <property name="testQuery" value="${jdbc.dataSource.connectionTestQuery3}" />  
	    <property name="maintenanceInterval" value="60" />  
	    <property name="maxIdleTime" value="${jdbc.dataSource.idleTimeout3}"></property>
</bean>

  然后是JtaTransactionManager事务管理器配置。

  JtaTransactionManager有两个重要属性需要配置transactionManager和userTransaction。对应的配置类如下:

  

  注:在jotm中只需配置transactionManager属性

  最后是atomikos的配置文件jta.properties。

  配置路径:根路径

  

  jta.properties也可命名为transactions.properties,如果不配置也可以启动项目,因为几乎所有配置项都有默认值。

  transactions.properties配置

  

更多更好的原创https://blog.ccyws.cn
原文地址:https://www.cnblogs.com/qianjun2017/p/8349829.html