spring-mybatis项目如何配置相同(/不同)类型的多数据源(mysql和oracle)

一、单类型数据源(例如:两个mysql)

1、spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 数据源1 -->
    <bean id="childDataSourceOne" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="clone">
        <!-- 基本属性driverClassName、 url、user、password -->
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <!-- 通常来说,只需要修改initialSize、minIdle、maxActive -->
        <!-- 初始化时建立物理连接的个数,缺省值为0 -->
        <property name="initialSize" value="10"/>
        <!-- 最小连接池数量 -->
        <property name="minIdle" value="10"/>
        <!-- 最大连接池数量,缺省值为8 -->
        <property name="maxActive" value="500"/>

        <!-- 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 -->
        <property name="maxWait" value="60000"/>
        <!--
            有些数据库连接的时候有超时限制(MySQL连接在8小时后断开),或者由于网络中断等原因,连接池的连接会出现失效的情况,这时候可以设置一个testWhileIdle参数为true,
            如果检测到当前连接不活跃的时间超过了timeBetweenEvictionRunsMillis,则去手动检测一下当前连接的有效性,在保证确实有效后才加以使用。
            在检测活跃性时,如果当前的活跃时间大于minEvictableIdleTimeMillis,则认为需要关闭当前连接。当
            然,为了保证绝对的可用性,你也可以使用testOnBorrow为true(即在每次获取Connection对象时都检测其可用性),不过这样会影响性能。
        -->
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒(3600000:为1小时) -->
        <property name="timeBetweenEvictionRunsMillis" value="3600000"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒(300000:为5分钟) -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <!-- 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 -->
        <property name="validationQuery" value="${jdbc.validationQuery}"/>
        <!-- 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。建议配置为true,不影响性能,并且保证安全性。-->
        <property name="testWhileIdle" value="true"/>
        <!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:true -->
        <property name="testOnBorrow" value="false"/>
        <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:false -->
        <property name="testOnReturn" value="false"/>
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat"/>
    </bean>
    <!-- 数据源2 -->
    <bean id="childDataSourceTwo" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="clone">
        <!-- 基本属性driverClassName、 url、user、password -->
        <property name="driverClassName" value="${jdbc2.driver}"/>
        <property name="url" value="${jdbc2.url}"/>
        <property name="username" value="${jdbc2.username}"/>
        <property name="password" value="${jdbc2.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <!-- 通常来说,只需要修改initialSize、minIdle、maxActive -->
        <!-- 初始化时建立物理连接的个数,缺省值为0 -->
        <property name="initialSize" value="10"/>
        <!-- 最小连接池数量 -->
        <property name="minIdle" value="10"/>
        <!-- 最大连接池数量,缺省值为8 -->
        <property name="maxActive" value="500"/>

        <!-- 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 -->
        <property name="maxWait" value="60000"/>
        <!--
            有些数据库连接的时候有超时限制(MySQL连接在8小时后断开),或者由于网络中断等原因,连接池的连接会出现失效的情况,这时候可以设置一个testWhileIdle参数为true,
            如果检测到当前连接不活跃的时间超过了timeBetweenEvictionRunsMillis,则去手动检测一下当前连接的有效性,在保证确实有效后才加以使用。
            在检测活跃性时,如果当前的活跃时间大于minEvictableIdleTimeMillis,则认为需要关闭当前连接。当
            然,为了保证绝对的可用性,你也可以使用testOnBorrow为true(即在每次获取Connection对象时都检测其可用性),不过这样会影响性能。
        -->
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒(3600000:为1小时) -->
        <property name="timeBetweenEvictionRunsMillis" value="3600000"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒(300000:为5分钟) -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <!-- 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 -->
        <property name="validationQuery" value="${jdbc.validationQuery}"/>
        <!-- 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。建议配置为true,不影响性能,并且保证安全性。-->
        <property name="testWhileIdle" value="true"/>
        <!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:true -->
        <property name="testOnBorrow" value="false"/>
        <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:false -->
        <property name="testOnReturn" value="false"/>
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat"/>
    </bean>

    <bean id="dataSourceSwitcher" class="com.demo.DataSourceSwitcher">
        <property name="targetDataSources">
            <map>
                <entry key="ds1" value-ref="childDataSourceOne"/>
                <entry key="ds2" value-ref="childDataSourceTwo"/>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="childDataSourceOne"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis/mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/demo/**/*.xml"/>
        <property name="dataSource" ref="dataSourceSwitcher"/>
    </bean>

    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.demo.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

    <!--======= 事务配置 Begin ================= -->
    <!-- 事务管理器(由Spring管理MyBatis的事务) -->
    <bean id="transactionManager"
          class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSourceSwitcher"/>
    </bean>
    <!-- 注解事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!--======= 事务配置 End =================== -->

    <!-- 配置druid监控spring jdbc -->
    <bean id="druid-stat-interceptor" class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor"/>
    <bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
        <property name="patterns">
            <list>
                <value>com.demo.service.impl.*</value>
            </list>
        </property>
    </bean>
    <aop:config>
        <aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut"/>
    </aop:config>
</beans>

 2、UseDataSource.java

package com.demo.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * <p>
 * <code>UseDataSource</code>
 * </p>
 * Description:
 *
 * @author 
 * @date 2020-07-27 下午 05:12
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseDataSource {

    String source() default "";
}

3、DataSourceType.java

package com.demo.enums;

import lombok.Getter;

/**
 * <p>
 * <code>DataSourceType</code>
 * </p>
 * Description:
 *
 * @author 
 * @date 2020-07-27 下午 05:12
 */
@Getter
public enum DataSourceType {
    SOURCE_1("ds1", "数据源1-默认数据源"),
    SOURCE_2("ds2", "数据源2");

    DataSourceType(String source, String desc) {
        this.source = source;
        this.desc = desc;
    }

    /**
     * 数据源标识
     */
    String source;
    /**
     * 数据源描述
     */
    String desc;
}

4、DataSourceSwitcher.java

package com.demo.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

/**
 * <p>
 * <code>DataSourceSwitcher</code>
 * </p>
 * Description: 数据源切换
 *
 * @author 
 * @date 2020-07-27 下午 05:12
 */
@Slf4j
public class DataSourceSwitcher extends AbstractRoutingDataSource {

    /**
     * ThreadLocal是线程安全的,并且不能在多线程之间共享
     */
    private static final ThreadLocal<String> DATA_SOURCE_KEY = new ThreadLocal<>();

    public static void clearDataSourceType() {
        String s = DATA_SOURCE_KEY.get();
        log.info("thread:{},remove,dataSource:{}", Thread.currentThread().getName(), s);
        DATA_SOURCE_KEY.remove();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        String s = DATA_SOURCE_KEY.get();
        log.info("thread:{},determine,dataSource:{}", Thread.currentThread().getName(), s);
        return s;
    }

    public static void setDataSourceKey(String dataSource) {
        log.info("thread:{},set,dataSource:{}", Thread.currentThread().getName(), dataSource);
        DATA_SOURCE_KEY.set(dataSource);
    }

    public static String getDataSourceKey() {
        String s = DATA_SOURCE_KEY.get();
        log.info("thread:{},get,dataSource:{}", Thread.currentThread().getName(), s);
        return s;
    }
}

5、DataSourceAsp.java

package com.demo.aop;

import com.demo.annotation.UseDataSource;
import com.demo.enums.DataSourceType;
import com.demo.util.DataSourceSwitcher;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
 * <p>
 * <code>DataSourceAsp</code>
 * </p>
 * Description: 数据源切换
 *
 * @author 
 * @date 2020-07-27 下午 05:12
 */
@Component
@Aspect
@Slf4j
@Order(Ordered.LOWEST_PRECEDENCE - 1)
public class DataSourceAsp {

    @Pointcut("@annotation(com.demo.annotation.UseDataSource)")
    public void useDataSource() {
    }

    @Around(value = "useDataSource() && @annotation(dataSource)")
    public Object dataSourceSwitcher(ProceedingJoinPoint joinPoint, UseDataSource dataSource) throws Throwable {
        try {
            String ds = dataSource.source();
            ds = StringUtils.isBlank(ds) ? DataSourceType.SOURCE_1.getSource() : ds;
            DataSourceSwitcher.setDataSourceKey(ds);
            return joinPoint.proceed();
        } catch (Exception e) {
            log.error("==>thread:{}, execute error", Thread.currentThread().getName(), e);
            throw e;
        } finally {
            DataSourceSwitcher.setDataSourceKey(DataSourceType.SOURCE_1.getSource());
        }
    }
}

6、使用方法

注解加到serviceImpl上,例如:

    @Override
    @UseDataSource(source = "ds2")
    @Transactional(rollbackFor = Exception.class)
    public void save() {}

二、多类型数据源(例如:一个mysql,一个oracle)

1、spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:tx="http://www.springframework.org/schema/tx" xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx
		http://www.springframework.org/schema/tx/spring-tx.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 数据源1 -->
    <bean id="childDataSourceOne" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="clone">
        <!-- 基本属性driverClassName、 url、user、password -->
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <!-- 通常来说,只需要修改initialSize、minIdle、maxActive -->
        <!-- 初始化时建立物理连接的个数,缺省值为0 -->
        <property name="initialSize" value="10"/>
        <!-- 最小连接池数量 -->
        <property name="minIdle" value="10"/>
        <!-- 最大连接池数量,缺省值为8 -->
        <property name="maxActive" value="500"/>

        <!-- 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 -->
        <property name="maxWait" value="60000"/>
        <!--
            有些数据库连接的时候有超时限制(MySQL连接在8小时后断开),或者由于网络中断等原因,连接池的连接会出现失效的情况,这时候可以设置一个testWhileIdle参数为true,
            如果检测到当前连接不活跃的时间超过了timeBetweenEvictionRunsMillis,则去手动检测一下当前连接的有效性,在保证确实有效后才加以使用。
            在检测活跃性时,如果当前的活跃时间大于minEvictableIdleTimeMillis,则认为需要关闭当前连接。当
            然,为了保证绝对的可用性,你也可以使用testOnBorrow为true(即在每次获取Connection对象时都检测其可用性),不过这样会影响性能。
        -->
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒(3600000:为1小时) -->
        <property name="timeBetweenEvictionRunsMillis" value="3600000"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒(300000:为5分钟) -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <!-- 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 -->
        <property name="validationQuery" value="${jdbc.validationQuery}"/>
        <!-- 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。建议配置为true,不影响性能,并且保证安全性。-->
        <property name="testWhileIdle" value="true"/>
        <!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:true -->
        <property name="testOnBorrow" value="false"/>
        <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:false -->
        <property name="testOnReturn" value="false"/>
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat"/>
    </bean>
    <!-- 数据源2 -->
    <bean id="childDataSourceTwo" class="com.alibaba.druid.pool.DruidDataSource" init-method="init"
          destroy-method="clone">
        <!-- 基本属性driverClassName、 url、user、password -->
        <property name="driverClassName" value="${jdbc2.driver}"/>
        <property name="url" value="${jdbc2.url}"/>
        <property name="username" value="${jdbc2.username}"/>
        <property name="password" value="${jdbc2.password}"/>

        <!-- 配置初始化大小、最小、最大 -->
        <!-- 通常来说,只需要修改initialSize、minIdle、maxActive -->
        <!-- 初始化时建立物理连接的个数,缺省值为0 -->
        <property name="initialSize" value="10"/>
        <!-- 最小连接池数量 -->
        <property name="minIdle" value="10"/>
        <!-- 最大连接池数量,缺省值为8 -->
        <property name="maxActive" value="500"/>

        <!-- 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 -->
        <property name="maxWait" value="60000"/>
        <!--
            有些数据库连接的时候有超时限制(MySQL连接在8小时后断开),或者由于网络中断等原因,连接池的连接会出现失效的情况,这时候可以设置一个testWhileIdle参数为true,
            如果检测到当前连接不活跃的时间超过了timeBetweenEvictionRunsMillis,则去手动检测一下当前连接的有效性,在保证确实有效后才加以使用。
            在检测活跃性时,如果当前的活跃时间大于minEvictableIdleTimeMillis,则认为需要关闭当前连接。当
            然,为了保证绝对的可用性,你也可以使用testOnBorrow为true(即在每次获取Connection对象时都检测其可用性),不过这样会影响性能。
        -->
        <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒(3600000:为1小时) -->
        <property name="timeBetweenEvictionRunsMillis" value="3600000"/>
        <!-- 配置一个连接在池中最小生存的时间,单位是毫秒(300000:为5分钟) -->
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <!-- 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。 -->
        <property name="validationQuery" value="${jdbc.validationQuery}"/>
        <!-- 申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。建议配置为true,不影响性能,并且保证安全性。-->
        <property name="testWhileIdle" value="true"/>
        <!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:true -->
        <property name="testOnBorrow" value="false"/>
        <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。缺省值:false -->
        <property name="testOnReturn" value="false"/>
        <!-- 监控数据库 -->
        <property name="filters" value="mergeStat"/>
    </bean>

    <bean id="dataSourceSwitcher" class="com.demo.util.DataSourceSwitcher">
        <property name="targetDataSources">
            <map>
                <entry key="ds1" value-ref="childDataSourceOne"/>
                <entry key="ds2" value-ref="childDataSourceTwo"/>
            </map>
        </property>
        <property name="defaultTargetDataSource" ref="childDataSourceOne"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis/mybatis-config-oracle.xml"/>
        <property name="mapperLocations" value="classpath:com/demo/dao/oracle/*.xml"/>
        <property name="dataSource" ref="dataSourceSwitcher"/>
    </bean>

    <bean id="sqlSessionFactoryTwo" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="configLocation" value="classpath:mybatis/mybatis-config-mysql.xml"/>
        <property name="mapperLocations" value="classpath:com/demo/dao/mysql/*.xml"/>
        <property name="dataSource" ref="dataSourceSwitcher"/>
    </bean>

    <bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.demo.dao.oracle"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>
    <bean id="mapperScannerConfigurerTwo" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.demo.dao.mysql"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryTwo"/>
    </bean>

    <!--======= 事务配置 Begin ================= -->
    <!-- 事务管理器(由Spring管理MyBatis的事务) -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSourceSwitcher"/>
    </bean>
    <!-- 注解事务 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!--======= 事务配置 End =================== -->

    <!-- 配置druid监控spring jdbc -->
    <bean id="druid-stat-interceptor" class="com.alibaba.druid.support.spring.stat.DruidStatInterceptor"/>
    <bean id="druid-stat-pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
        <property name="patterns">
            <list>
                <value>com.demo.service.impl.*</value>
            </list>
        </property>
    </bean>
    <aop:config>
        <aop:advisor advice-ref="druid-stat-interceptor" pointcut-ref="druid-stat-pointcut"/>
    </aop:config>
</beans>

2、UseDataSource.java:与单类型数据源相同

3、DataSourceType.java:与单类型数据源相同

4、DataSourceSwitcher.java:与单类型数据源相同

5、DataSourceAsp.java:与单类型数据源相同

6、使用方法:与单类型数据源相同

7、注意

7.1、两个数据源的mapperLocations扫描的xml路径和basePackage扫描的mapper路径不要有交集,否则会造成失败

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值