当前位置: 首页 > news >正文

MyBatis配置多数据源

  • 分类
    • 准备工作
    • 多数据源配置
    • 动态数据源配置
  • 参考链接

分类

MyBatis的多数据源配置分2种,

  1. 多数据源配置:两个库业务互不相干,a方法使用a库的数据,b方法使用b库的数据;
  2. 动态数据源配置:两个库业务有关联,如读写分离库。

第一种直接配置2个单独的数据源,不同模块引入不同的sqlSessionFactory即可;第二种需要配置可动态切换的数据源。

准备工作

两种方式都需要在pom.xml文件引入不同数据库需要的jar包,此处以MySQL和Oracle为例:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.40</version>
</dependency>
<dependency>
  <groupId>com.oracle</groupId>
  <artifactId>ojdbc</artifactId>
  <version>7</version>
</dependency>
复制代码

此处Oracle的jar包因为版权问题,在maven中央仓库中没有,需要手动安装。 安装教程

多数据源配置

在applicationContext.xml中配置两套数据源即可

<!-- ===============第一个数据源的配置开始=============== -->
<!--MySQL的数据库配置-->
<bean id="mysqlDtaSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="driverClassName" value="${mysql.driverClassName}"/>
    <property name="url" value="${mysql.url}"/>
    <property name="username" value="${mysql.username}"/>
    <property name="password" value="${mysql.password}"/>
    <!-- 初始化连接大小 -->
    <property name="initialSize" value="${mysql.druid.initialSize}"></property>
    <!-- 连接池最大数量 -->
    <property name="maxActive" value="${mysql.druid.maxActive}"></property>
    <!-- 连接池最大空闲 -->
    <property name="maxIdle" value="${mysql.druid.maxIdle}"></property>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="${mysql.druid.minIdle}"></property>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="${mysql.druid.maxWait}"></property>
</bean>
<!--配置MySQL的SqlSessionFactory-->
<bean id="sqlSessionFactoryMySQL" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="mysqlDtaSource"/>
    <property name="configLocation" value="classpath*:mybatis-config.xml" />
    <!-- 自动扫描mapping.xml文件 -->
    <property name="mapperLocations" value="classpath*:com/rebecca/mybatismutildb/mysql/**/dao/mapper/*Mapper.xml"/>
    <property name="typeAliasesPackage" value="com.rebecca.mybatismutildb.mysql.**"/>
</bean>
<!--告诉框架扫描指定包中的mapper接口,然后为其自动的生成代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.rebecca.mybatismutildb.mysql.**.dao"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryMySQL"/>
</bean>
<!--配置事务管理器-->
<bean id="mysqlTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="mysqlDtaSource"/>
</bean>
<!--配置事务的传播特性-->
<tx:advice id="txAdvice" transaction-manager="mysqlTxManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="list*" read-only="true"/>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!-- ===============第一个数据源的配置结束=============== -->

<!-- ===============第二个数据源的配置开始=============== -->
<!--Oracle的数据库配置-->
<bean id="oracleDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${oracle.driverClassName}" />
    <property name="url" value="${oracle.url}" />
    <property name="username" value="${oracle.username}" />
    <property name="password" value="${oracle.password}" />
    <!-- 初始化连接大小 -->
    <property name="initialSize" value="${oracle.druid.initialSize}"></property>
    <!-- 连接池最大数量 -->
    <property name="maxActive" value="${oracle.druid.maxActive}"></property>
    <!-- 连接池最大空闲 -->
    <property name="maxIdle" value="${oracle.druid.maxIdle}"></property>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="${oracle.druid.minIdle}"></property>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="${oracle.druid.maxWait}"></property>
</bean>
<!--配置SqlSessionFactory-->
<bean id="sqlSessionFactoryOracle" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="oracleDataSource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <!-- 自动扫描mapping.xml文件 -->
    <property name="mapperLocations" value="classpath*:com/rebecca/mybatismutildb/oracle/**/dao/mapper/*Mapper.xml">
    </property>
    <property name="typeAliasesPackage" value="com.rebecca.mybatismutildb.oracle.**"/>
</bean>
<!--告诉框架扫描指定包中的mapper接口,然后为其自动的生成代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.rebecca.mybatismutildb.oracle.**.dao"/>
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryOracle"/>
</bean>

<!--配置事务管理器-->
<bean id="txManagerOracle" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="oracleDataSource"/>
</bean>

<!--配置事务的传播特性-->
<tx:advice id="txAdviceOracle" transaction-manager="txManagerOracle">
    <tx:attributes>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="list*" read-only="true"/>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!-- ===============第二个数据源的配置结束=============== -->
复制代码

此处第一个数据源指定去解析com/rebecca/mybatismutildb/mysql/**/dao/mapper包下的*Mapper.xml文件,即指定这些dao的方法去连MySQL的数据源;第二个数据源指定去解析com/rebecca/mybatismutildb/oracle/**/dao/mapper包下的*Mapper.xml文件,即指定这些dao的方法去连Oracle的数据源。

applicationContext.xml配置好后,直接在指定的包内编码即可。

动态数据源配置

  1. 在applicationContext.xml中配置动态切换数据源

<!--MySQL的数据库配置-->
<bean id="mysqlDtaSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    <property name="driverClassName" value="${mysql.driverClassName}"/>
    <property name="url" value="${mysql.url}"/>
    <property name="username" value="${mysql.username}"/>
    <property name="password" value="${mysql.password}"/>
    <!-- 初始化连接大小 -->
    <property name="initialSize" value="${mysql.druid.initialSize}"></property>
    <!-- 连接池最大数量 -->
    <property name="maxActive" value="${mysql.druid.maxActive}"></property>
    <!-- 连接池最大空闲 -->
    <property name="maxIdle" value="${mysql.druid.maxIdle}"></property>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="${mysql.druid.minIdle}"></property>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="${mysql.druid.maxWait}"></property>
</bean>
<!--Oracle的数据库配置-->
<bean id="oracleDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${oracle.driverClassName}" />
    <property name="url" value="${oracle.url}" />
    <property name="username" value="${oracle.username}" />
    <property name="password" value="${oracle.password}" />
    <!-- 初始化连接大小 -->
    <property name="initialSize" value="${oracle.druid.initialSize}"></property>
    <!-- 连接池最大数量 -->
    <property name="maxActive" value="${oracle.druid.maxActive}"></property>
    <!-- 连接池最大空闲 -->
    <property name="maxIdle" value="${oracle.druid.maxIdle}"></property>
    <!-- 连接池最小空闲 -->
    <property name="minIdle" value="${oracle.druid.minIdle}"></property>
    <!-- 获取连接最大等待时间 -->
    <property name="maxWait" value="${oracle.druid.maxWait}"></property>
</bean>
<!--告诉框架扫描指定包中的mapper接口,然后为其自动的生成代理对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.rebecca.mybatismutildb.mysql.**.dao,com.rebecca.mybatismutildb.oracle.**.dao"/>
</bean>
<!--配置事务管理器-->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>
<!--配置事务的传播特性-->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true"/>
        <tx:method name="list*" read-only="true"/>
        <tx:method name="query*" read-only="true"/>
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>

<!--配置SqlSessionFactory(此处自定义实现SqlSessionFactoryBean,以支持mapperLocations的通配符配置)-->
<bean id="sqlSessionFactory" class="com.rebecca.mybatismutildb.db.PackagesSqlSessionFactoryBean" scope="prototype">
    <property name="dataSource" ref="dataSource"/>
    <property name="configLocation" value="classpath:mybatis-config.xml" />
    <!-- 自动扫描mapping.xml文件 -->
    <property name="mapperLocations">
        <list>
            <value>classpath*:com/rebecca/mybatismutildb/mysql/**/dao/mapper/*Mapper.xml</value>
            <value>classpath*:com/rebecca/mybatismutildb/oracle/**/dao/mapper/*Mapper.xml</value>
        </list>
    </property>
    <property name="typeAliasesPackage" value="com.rebecca.mybatismutildb.**"/>
</bean>
<!--动态配置数据源-->
<bean id="dataSource" class="com.rebecca.mybatismutildb.db.DynamicDataSource" >
    <property name="targetDataSources">
        <map key-type="java.lang.String">
            <!--通过不同的key决定用哪个dataSource-->
            <entry value-ref="mysqlDtaSource" key="mysqlDtaSource"></entry>
            <entry value-ref="oracleDataSource" key="oracleDataSource"></entry>
        </map>
    </property>
    <!--设置默认的dataSource-->
    <property name="defaultTargetDataSource" ref="mysqlDtaSource">
    </property>
</bean>

<!--动态数据源切换 -->
<bean id="dataSourceAspect" class="com.rebecca.mybatismutildb.db.DataSourceAspect" />
<!--动态数据源切换 spring的aop切面-->
<aop:config>
    <!--请设置 @Order(0)。否则可能出现 数据源切换失败问题! 因为要在事务开启之前就进行判断,并进行切换数据源!-->
    <aop:aspect ref="dataSourceAspect" order="0">
        <!--expression中要配置多个包时,注意用or连接,如:execution(* com.rebecca.mybatismutildb.mysql..dao..*.*(..)) or execution(* com.rebecca.mybatismutildb.oracle..dao..*.*(..)) ; 即拦截com.rebecca.mybatismutildb.mysql和com.rebecca.mybatismutildb.oracle下所有的dao包 -->
        <aop:pointcut id="dataSourcePointcut" expression="execution(* com.rebecca.mybatismutildb..dao..*.*(..)) "/>
        <aop:before pointcut-ref="dataSourcePointcut" method="changeDataSource" />
        <aop:after pointcut-ref="dataSourcePointcut" method="removeDataSource" />
    </aop:aspect>
</aop:config>

复制代码
  1. applicationContext.xml中用到的相关类
  • DataSource 注解
package com.rebecca.mybatismutildb.db;

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

/**
 * DataSource的注解
 * @Author: rebecca
 * @Date: Created in 2019/4/25 17:27
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface DataSource {
    String value();
}
复制代码
  • DataSourceAspect 切面
package com.rebecca.mybatismutildb.db;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

/**
 * 拦截所有带有注解@DataSource的类和方法
 * @Author: rebecca
 * @Date: Created in 2019/4/25 17:28
 */
public class DataSourceAspect {
    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(DataSourceAspect.class);
    /**
     * @Title: changeDataSource
     * @Description: 拦截目标方法,获取由@DataSource指定的数据源标识,设置到线程存储中以便切换数据源
     * @param joinPoint
     * @return: void
     */
    public void changeDataSource(JoinPoint joinPoint){
        Class<?> target = joinPoint.getTarget().getClass();
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        for(Class<?> clazz : target.getInterfaces()){
            resolveDataSource(clazz,methodSignature.getMethod());
        }
        resolveDataSource(target,methodSignature.getMethod());
        logger.debug("数据源切换至--->", DataSourceContextHolder.getDbType());
    }

    private void resolveDataSource(Class<?> clazz, Method method){
        try {
            if(clazz.isAnnotationPresent(DataSource.class)){
                DataSource source = clazz.getAnnotation(DataSource.class);
                DataSourceContextHolder.setDbType(source.value());
            }

            Class<?>[] types = method.getParameterTypes();
            Method m = clazz.getDeclaredMethod(method.getName(), types);
            if(null != m && m.isAnnotationPresent(DataSource.class)){
                DataSource source = m.getAnnotation(DataSource.class);
                DataSourceContextHolder.setDbType(source.value());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void removeDataSource() {
        try {
            DataSourceContextHolder.clearDbType();
            logger.debug("数据源已移除!");
        } catch (Exception e) {
            e.printStackTrace();
            logger.debug("数据源移除报错!", e);
        }
    }
}

复制代码
  • DataSourceContextHolder 类
package com.rebecca.mybatismutildb.db;

/**
 * @Author: rebecca
 * @Description:
 * @Date: Created in 2019/3/28 11:43
 * @Modified By:
 */
public class DataSourceContextHolder {
    // 注意此处的属性值要与applicationContext.xml中配置的一致
    public static final String DATA_SOURCE_MYSQL = "mysqlDtaSource";
    public static final String DATA_SOURCE_ORACLE = "oracleDataSource";
    //用ThreadLocal来设置当前线程使用哪个dataSource
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

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

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

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

复制代码
  • DynamicDataSource 类
package com.rebecca.mybatismutildb.db;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.logging.Logger;

/**
 * 动态切换数据源
 * @Author: rebecca
 * @Description:
 * @Date: Created in 2019/3/28 11:42
 * @Modified By:
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    @Override
    protected Object determineCurrentLookupKey() {
        return DataSourceContextHolder.getDbType();
    }

    @Override
    public Logger getParentLogger() {
        return null;
    }
}
复制代码
  • PackagesSqlSessionFactoryBean 类(自定义实现SqlSessionFactoryBean,以支持mapperLocations的通配符配置)
package com.rebecca.mybatismutildb.db;

import org.apache.commons.lang.StringUtils;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 支持包名的通配符解析
 * @Author: rebecca
 * @Description:
 * @Date: Created in 2019/3/28 15:30
 * @Modified By:
 */
public class PackagesSqlSessionFactoryBean extends SqlSessionFactoryBean {

    static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";

    private Logger logger = LoggerFactory.getLogger(PackagesSqlSessionFactoryBean.class);

    @Override
    public void setTypeAliasesPackage(String typeAliasesPackage) {
        ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver);
        typeAliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                ClassUtils.convertClassNameToResourcePath(typeAliasesPackage) + "/" + DEFAULT_RESOURCE_PATTERN;

        // 将加载多个绝对匹配的所有Resource
        // 将首先通过ClassLoader.getResource("META-INF")加载非模式路径部分
        // 然后进行遍历模式匹配
        try {
            List<String> result = new ArrayList<String>();
            Resource[] resources =  resolver.getResources(typeAliasesPackage);
            if(resources != null && resources.length > 0){
                MetadataReader metadataReader = null;
                for(Resource resource : resources){
                    if(resource.isReadable()){
                        metadataReader =  metadataReaderFactory.getMetadataReader(resource);
                        try {
                            result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName());
                        } catch (ClassNotFoundException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
            if(result.size() > 0) {
                super.setTypeAliasesPackage(StringUtils.join(result.toArray(), ","));
            }else{
                logger.warn("参数typeAliasesPackage:"+typeAliasesPackage+",未找到任何包");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
复制代码
  1. 在对应的dao的接口上,增加@DataSource(string value)注解,其中value的值是DataSourceContextHolder.DATA_SOURCE_MYSQLDataSourceContextHolder.DATA_SOURCE_ORACLE
    因为在applicationContext.xml中配置的默认数据库连接是MySQL,所以在连MySQL库的接口上可以不写@DataSource(string value)注解。同样,此注解也可以加在接口的方法上。

参考链接

mybatis配置多数据源

mybatis配置动态数据源

Spring aop:pointcut--expression--多个execution连接方法

转载于:https://juejin.im/post/5cc57880f265da03b858597b

相关文章:

  • Asp.net core Identity + identity server + angular 学习笔记 (第三篇)
  • 【题解】四色定理
  • Android 实现动态背景“五彩蛛网”特效,让你大开眼界!
  • python高并发?
  • 雷林鹏分享:二级目录配置CI应用
  • Sym System Recovery 2013 ( 備份 操作 )
  • iOS-在项目中引入RSA算法
  • 简单的数学题
  • 关于JS引擎优化的理解
  • 如何优雅的备份账号相关信息
  • mybatis学习总结
  • 全球首个大规模光电芯片到来
  • 1.4T的mysql表删除
  • MYSQL-SELECT查
  • css 浮动的时候如何,div进行居中
  • Android框架之Volley
  • Angular6错误 Service: No provider for Renderer2
  • Effective Java 笔记(一)
  • ES学习笔记(12)--Symbol
  • Fastjson的基本使用方法大全
  • JavaScript-Array类型
  • java架构面试锦集:开源框架+并发+数据结构+大企必备面试题
  • js正则,这点儿就够用了
  • Python 使用 Tornado 框架实现 WebHook 自动部署 Git 项目
  • 初识 beanstalkd
  • 聚簇索引和非聚簇索引
  • 排序算法之--选择排序
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 前端学习笔记之原型——一张图说明`prototype`和`__proto__`的区别
  • 我与Jetbrains的这些年
  • 一起来学SpringBoot | 第三篇:SpringBoot日志配置
  • 一些css基础学习笔记
  • 移动端解决方案学习记录
  • 云栖大讲堂Java基础入门(三)- 阿里巴巴Java开发手册介绍
  • 好程序员大数据教程Hadoop全分布安装(非HA)
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • #我与Java虚拟机的故事#连载11: JVM学习之路
  • (¥1011)-(一千零一拾一元整)输出
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (层次遍历)104. 二叉树的最大深度
  • (第一天)包装对象、作用域、创建对象
  • (附源码)spring boot车辆管理系统 毕业设计 031034
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (附源码)计算机毕业设计SSM基于健身房管理系统
  • (蓝桥杯每日一题)平方末尾及补充(常用的字符串函数功能)
  • (三)uboot源码分析
  • (一)WLAN定义和基本架构转
  • (一)硬件制作--从零开始自制linux掌上电脑(F1C200S) <嵌入式项目>
  • (转)C#调用WebService 基础
  • (转)shell中括号的特殊用法 linux if多条件判断
  • (转)利用PHP的debug_backtrace函数,实现PHP文件权限管理、动态加载 【反射】...
  • (转)树状数组
  • .equals()到底是什么意思?
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .NET/C# 异常处理:写一个空的 try 块代码,而把重要代码写到 finally 中(Constrained Execution Regions)