场景:

springboot项目,默认使用HikariCP连接池 + MybatisPlus持久层框架 + mysql数据库等一套流程,现需求需去第三方sqlserver数据库拉取数据,直连数据库,不走接口,因此,需把项目改造成 多数据源结构,以实现动态切换数据源。

使用docker 安装mysql + sqlserver 数据库 进行测试

实现示例:

0.pom.xml

 <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.mumu</groupId>
<artifactId>springboot-mumu</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>mumu-web</artifactId> <dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<!-- mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- sqlserver -->
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>runtime</scope>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

1.配置文件

server:
port: 9587
spring:
datasource:
hikari:
master:
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://127.0.0.1:3306/d_credit?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8
username: root
password: 1234567890
slave:
driverClassName: com.microsoft.sqlserver.jdbc.SQLServerDriver
jdbcUrl: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=TestDB
username: SA
password: <1234567890@Passw0rd>
#mybatis-plus
mybatis-plus:
type-aliases-package: com.mumu.model
mapper-locations: classpath:/mapper/**/*.xml
configuration:
jdbc-type-for-null: null
map-underscore-to-camel-case: true
cache-enabled: false
global-config:
db-config:
id-type: auto
field-strategy: not_empty

2.数据源配置类

注意:!!!!!

1.使用mybatis的全局配置文件  这里工厂bean使用SqlSessionFactoryBean

使用mybatis-plus的全局配置 这里工厂bean使用MybatisSqlSessionFactoryBean

否则 mybatis对应的全局配置不会生效

2.@Primary用在DataSourceBuilder.create().build()构建的DataSource方法上,不能放到构建动态数据源方法上,否则会有循环依赖的问题


package com.mumu.common.config;

import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.mumu.common.datasources.DataSourceNames;
import com.mumu.common.datasources.DynamicDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.transaction.annotation.EnableTransactionManagement; import javax.sql.DataSource;
import javax.xml.crypto.Data;
import java.util.HashMap;
import java.util.Map; /**
* @Description
* @Author Created by Mumu
* @Date on 2019/11/25
*/
@Configuration
@EnableTransactionManagement
@MapperScan("com.mumu.*.mapper")
public class DataSourceConfig { @Primary
@Bean(name = "master")
@ConfigurationProperties(prefix = "spring.datasource.hikari.master")
public DataSource master() {
return DataSourceBuilder.create().build();
} @Bean(name = "slave")
@ConfigurationProperties(prefix = "spring.datasource.hikari.slave")
public DataSource slave() {
return DataSourceBuilder.create().build();
} @Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
DynamicDataSource dynamicDataSource = new DynamicDataSource();
Map<Object, Object> dataSourceMap = new HashMap<>(2);
dataSourceMap.put(DataSourceNames.FIRST, master());
dataSourceMap.put(DataSourceNames.SECOND, slave());
/// 将 master 数据源作为默认指定的数据源
dynamicDataSource.setDefaultDataSource(master());
// 将 master 和 slave 数据源作为指定的数据源
dynamicDataSource.setDataSources(dataSourceMap);
return dynamicDataSource;
} @Bean("sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory() throws Exception {
// SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); // 使用mybatis的全局配置文件
//使用mybatisplus的工程bean,mybatis-plus的全局配置文件才会生效
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
// 配置数据源,此处配置为关键配置,如果没有将 dynamicDataSource作为数据源则不能实现切换
sqlSessionFactoryBean.setDataSource(dynamicDataSource());
// 扫描model
sqlSessionFactoryBean.setTypeAliasesPackage("com.mumu.model");
// 扫描映射文件
sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:/mapper/**/*Mapper.xml"));
return sqlSessionFactoryBean.getObject();
}
}
 

 3.动态数据源类

我们上一步把这个动态数据源设置到了SQL会话工厂和事务管理器,这样在操作数据库时就会通过动态数据源类来获取要操作的数据源了。

动态数据源类集成了Spring提供的AbstractRoutingDataSource类,AbstractRoutingDataSource 中获取数据源的方法就是 determineTargetDataSource,而此方法又通过 determineCurrentLookupKey 方法获取查询数据源的key。

所以如果我们需要动态切换数据源,就可以通过以下两种方式定制:

1. 覆写 determineCurrentLookupKey 方法

通过覆写 determineCurrentLookupKey 方法,从一个自定义的 DynamicDataSourceContextHolder.getDataSourceKey() 获取数据源key值,这样在我们想动态切换数据源的时候,只要通过  DynamicDataSourceContextHolder.setDataSourceKey(key)  的方式就可以动态改变数据源了。这种方式要求在获取数据源之前,要先初始化各个数据源到 DynamicDataSource 中,我们案例就是采用这种方式实现的,所以在 MybatisConfig 中把master和slave数据源都事先初始化到DynamicDataSource 中。

2. 可以通过覆写 determineTargetDataSource,因为数据源就是在这个方法创建并返回的,所以这种方式就比较自由了,支持到任何你希望的地方读取数据源信息,只要最终返回一个 DataSource 的实现类即可。比如你可以到数据库、本地文件、网络接口等方式读取到数据源信息然后返回相应的数据源对象就可以了。

 package com.mumu.common.datasources;

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

 import java.util.Map;

 /**
* @Description 动态数据源实现类
* @Author Created by Mumu
* @Date on 2019/11/25
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 如果希望所有数据源在启动配置时就加载好,这里通过设置数据源Key值来切换数据,定制这个方法
*/
@Override
protected Object determineCurrentLookupKey() {
return DynamicDataSourceContextHolder.getDataSourceKey();
} /**
* 设置默认数据源
* @param defaultDataSource
*/
public void setDefaultDataSource(Object defaultDataSource) {
super.setDefaultTargetDataSource(defaultDataSource);
} /**
* 设置数据源
* @param dataSources
*/
public void setDataSources(Map<Object, Object> dataSources) {
super.setTargetDataSources(dataSources);
// 将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
// DynamicDataSourceContextHolder.addDataSourceKeys(dataSources.keySet());
}
}

4.动态数据源上下文

动态数据源的切换主要是通过调用这个类的方法来完成的

 package com.mumu.common.datasources;

 /**
* @Description 动态数据源上下文
* @Author Created by Mumu
* @Date on 2019/11/25
*/
public class DynamicDataSourceContextHolder {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>() {
/**
* 将 master 数据源的 key作为默认数据源的 key
*/
@Override
protected String initialValue() {
return DataSourceNames.FIRST;
}
}; /**
* 获取数据源
* @return
*/
public static String getDataSourceKey() {
return contextHolder.get();
} /**
* 切换数据源
* @param key
*/
public static void setDataSourceKey(String key) {
contextHolder.set(key);
} /**
* 重置数据源
*/
public static void clearDataSourceKey() {
contextHolder.remove();
} }

5.动态数据源注解

 package com.mumu.common.datasources.annotation;

 import java.lang.annotation.*;

 /**
* @Description 动态数据源注解
* @Author Created by Mumu
* @Date on 2019/11/25
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 数据源key值
*
* @return
*/
String name() default "";
}

6.动态数据源切换处理器

创建一个AOP切面,拦截带 @DataSource 注解的方法,在方法执行前切换至目标数据源,执行完成后恢复到默认数据源。

 package com.mumu.common.datasources.aspect;

 import com.mumu.common.datasources.DataSourceNames;
import com.mumu.common.datasources.DynamicDataSourceContextHolder;
import com.mumu.common.datasources.annotation.DataSource;
import lombok.extern.slf4j.Slf4j;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component; import java.lang.reflect.Method; /**
* @Description 动态数据源切换处理器
* @Author Created by Mumu
* @Date on 2019/11/25
*/
@Aspect
@Component
@Slf4j
@Order(-1)// 该切面应当先于 @Transactional 执行
public class DynamicDataSourceAspect {
@Pointcut("@annotation(com.mumu.common.datasources.annotation.DataSource)")
public void dataSourcePointCut() { } @Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod(); DataSource ds = method.getAnnotation(DataSource.class);
if (ds == null) {
DynamicDataSourceContextHolder.setDataSourceKey(DataSourceNames.FIRST);
log.info("set datasource is " + DataSourceNames.FIRST);
} else {
// 切换数据源
DynamicDataSourceContextHolder.setDataSourceKey(ds.name());
log.info("Switch DataSource to【{}】in Method【{}】", DynamicDataSourceContextHolder.getDataSourceKey(), signature);
} try {
return point.proceed();
} finally {
// 将数据源置为默认数据源
DynamicDataSourceContextHolder.clearDataSourceKey();
log.info("Restore DataSource to【{}】in Method【{}】", DynamicDataSourceContextHolder.getDataSourceKey(), signature);
}
}
}

7.测试

默认操作mysql数据源(master),不做特殊处理

 package com.mumu.service.impl;

 import com.mumu.common.datasources.DataSourceNames;
import com.mumu.common.datasources.annotation.DataSource;
import com.mumu.model.AddressBook;
import com.mumu.persistence.mapper.AddressBookMapper;
import com.mumu.service.AddressBookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @Description
* @Author Created by Mumu
* @Date on 2019/11/25
*/
@Service
public class AddressBookServiceImpl implements AddressBookService {
@Autowired
private AddressBookMapper addressBookMapper; @Override
public AddressBook queryById(Integer bookId){
AddressBook addressBook = addressBookMapper.queryById(bookId);
return addressBook;
}
}

动态切换sqlserver数据源(slave) 通过@DataSource(name = DataSourceNames.SECOND)完成

 package com.mumu.service.impl;

 import com.mumu.common.datasources.DataSourceNames;
import com.mumu.common.datasources.annotation.DataSource;
import com.mumu.model.Inventory;
import com.mumu.persistence.mapper.InventoryMapper;
import com.mumu.service.InventoryService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; /**
* @Description
* @Author Created by Mumu
* @Date on 2019/11/25
*/
@Service
public class InventoryServiceImpl implements InventoryService {
@Autowired
private InventoryMapper inventoryMapper;
@Override
@DataSource(name = DataSourceNames.SECOND)
public Inventory queryById(Integer id){
return inventoryMapper.queryById(id);
};

 8.遇到的问题

多数据源注入、循环依赖问题

自定义sqlSessionFactory时,用错mybatis/mybatis-plus的类,导致对应的mybatis全局配置文件未生效

敬请期待...

springboot-配置多数据源(AOP实现)(HikariCP + MybatisPlus + mysql + SqlServer)的更多相关文章

  1. springboot配置Druid数据源

    springboot配置druid数据源 Author:SimpleWu springboot整合篇 前言 对于数据访问层,无论是Sql还是NoSql,SpringBoot默认采用整合SpringDa ...

  2. SpringBoot配置多数据源时遇到的问题

    SpringBoot配置多数据源 参考代码:Spring Boot 1.5.8.RELEASE同时配置Oracle和MySQL 原作者用的是1.5.8版本的SpringBoot,在升级到2.0.*之后 ...

  3. springboot 配置多数据源

    1.首先在创建应用对象时引入autoConfig package com; import org.springframework.boot.SpringApplication; import org. ...

  4. springboot 配置多数据源 good

    1.首先在创建应用对象时引入autoConfig package com; import org.springframework.boot.SpringApplication; import org. ...

  5. SpringBoot配置多数据源Mysql+Sqlite

    ​ 配置了一下druid的多数据源配置,尝试了很多方法,Spring boot关于对Mysql和Sqlite多数据源的配置,记录下来: 涉及技术点: Springboot + Druid + Mysq ...

  6. springboot配置多数据源mybatis配置失效问题

    mybatis配置 #开启驼峰映射 mybatis.configuration.map-underscore-to-camel-case=true #开启打印sql mybatis.configura ...

  7. SpringBoot配置多数据源

    原文:https://www.jianshu.com/p/033e0ebeb617 项目中用到了两个数据库,分别是Oracle和Mysql,涉及到了多数据源问题,这里做下记录 官方讲解:https:/ ...

  8. springboot配置多数据源(JdbcTemplate方式)

    在实际开发中可能会遇到需要配置多个数据源的情况,比如:需要使用多个host.需要使用多种数据库(MySql.Oracle.SqlServer…) 如果使用springboot开发,可做如下配置: Co ...

  9. Springboot配置多数据源(Mysql和Orcale)--(Idea Maven JDBCTemplate支持下的)

    1.配置 orcale jdbc 对于一个Maven项目,使用Mysql时,可直接添加如下依赖: <dependency> <groupId>mysql</groupId ...

随机推荐

  1. focus /focusin /focusout /blur 事件

    事件触发时间 focus:当focusable元素获得焦点时,不支持冒泡:focusin:和focus一样,只是此事件支持冒泡:blur:当focusable元素失去焦点时,不支持冒泡:focusou ...

  2. ES,kibana通过nginx添加访问权限

    一.安装nginx yum install epel-release -y yum install -y nginx 二.安装Apache Httpd 密码生成工具 # 生成密码 yum instal ...

  3. NX二次开发-UFUN创建倒角UF_MODL_create_chamfer

    NX9+VS2012 #include <uf.h> #include <uf_modl.h> UF_initialize(); //创建块 UF_FEATURE_SIGN S ...

  4. JVM内核-原理、诊断与优化学习笔记(一):初识JVM

    文章目录 JVM的概念 JVM是Java Virtual Machine的简称.意为Java虚拟机 虚拟机 有哪些虚拟机 VMWare或者Visual Box都是使用软件模拟物理CPU的指令集 JVM ...

  5. jquery控件的学习

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. NFS服务器简易安装

    1.服务端 创建挂载目录 # mkdir /data/nfs 安装NFS软件 # yum install nfs-utils -y 添加配置信息 # vim /etc/exports /data/nf ...

  7. 使用CSS3开启GPU硬件加速提升网站动画渲染性能

    遇到的问题: 网站本身设计初衷就没有打算支持IE8及以下版本浏览器,并不是因为代码兼容性问题,而是真的不想迁就那些懒得更新自己操作系统和浏览器的用户,毕竟是我自己的网站,所以我说了算!哈哈~ 没有了低 ...

  8. IDEA与Tomcat的相关配置说明

    1.IDEA会为每个Tomcat部署的项目单独建立一份配置文件 查看控制台的log输出:Using CATAINA_BASE 2.工作空间项目和Tomcat部署的web项目 WEB-INF:内的资源不 ...

  9. Mysql优化-概述

    摘抄并用于自查笔记 为什么要优化 一个应用吞吐量瓶颈往往出现在数据库的处理速度上. 随着应用程序的使用,数据库数据逐渐增多,数据库处理压力逐渐增大. 关系型数据库的数据是存放在磁盘上,读写速度慢(与内 ...

  10. 【bug】使用element-ui遇到在IE浏览器中点击enter会回到登录页

    1.点击el-input框,会回到登录页(IE浏览器) 外层是el-table/el-form/el-input 添加可以解决 <el-form onSubmit="return fa ...