mybatis多数据源动态切换的完整步骤

笔者主要从事c#开发,近期因为项目需要,搭建了一套spring-cloud微服务框架,集成了eureka服务注册中心、
gateway网关过滤、admin服务监控、auth授权体系验证,集成了redis、swagger、jwt、mybatis多数据源等各项功能。
具体搭建过程后续另写播客介绍。具体结构如下:

在搭建过程集成mybatis的时候,考虑到单一数据源无法满足实际业务需要,故结合c#的开发经验,进行多数据源动态集成。

mybatis的多数据源可以采用两种方式进行,第一种是分包方式实现,这种方式灵活性不高,而且较为繁琐,故不做过多介绍。

另一种方式是采用AOP的思想,进行注解动态切换,参考网上教程,核心思想是依靠 继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法,在该方法中使用DatabaseContextHolder获取当前线程的dataSource。

但是网上方法大都是首先定义好各个datasource,比如有三个数据源,就需要实现定义好三个datasource,笔者感觉这种方法,在我目前这套框架中不够灵活,因为笔者采用的是微服务框架,考虑到各个服务都有可能使用不同的数据源,而多数据源动态切换是放在公共方法中实现的,如果每有新的数据源就要定义一个,对代码的侵入性太高,在c#中,选择数据源很容易,根据连接名称就可以切换过去,如下所示:

<connectionStrings>
<add name="test1" connectionString="server=127.0.0.1;user id=root;password=123456;database=db1;charset=utf8" providerName="MySql.Data.MySqlClient" />
<add name="test2" connectionString="server=127.0.0.1;user id=root;password=123456;database=db2;charset=utf8" providerName="MySql.Data.MySqlClient" />
<add name="test3" connectionString="server=127.0.0.1;user id=root;password=123456;database=db3;charset=utf8" providerName="MySql.Data.MySqlClient" />
<connectionStrings>

能不能像c#这样根据连接名称就自动选择呢,笔者的连接配置如下所示:

spring:
 application:
 name: csg-auth
 datasource:
 kbase:
  - driverClassName: com.kbase.jdbc.Driver
  jdbcUrl: jdbc:kbase://127.0.0.1
  username: DBOWN
  password:
 jdbc:
  - driverClassName: com.mysql.cj.jdbc.Driver
  jdbcUrl: jdbc:mysql://localhost:3306/nacos?serverTimezone=GMT%2B8&useUnicode=false&characterEncoding=utf8&useSSL=false
  username: root
  password: 123456
  connName: nacos
  - driverClassName: com.mysql.cj.jdbc.Driver
  jdbcUrl: jdbc:mysql://localhost:3306/tpi?serverTimezone=GMT%2B8&useUnicode=false&characterEncoding=utf8&useSSL=false
  username: root
  password: 123456
  connName: tpi

其中kbase不用理会,是我们公司自己的数据库,jdbc是维护的连接集合,其中connName就是我们自定义的连接名称,
根据connName就可以自动切换到对应数据源。

笔者实现代码如下:

第一步

首先,编写DynamicDataSource类集成AbstractRoutingDataSource,重写determineCurrentLookupKey方法,该方法主要作用是选择数据源的key
代码如下:

/**
 * 动态数据源
 * */
public class DynamicDataSource extends AbstractRoutingDataSource {
 @Override
 protected Object determineCurrentLookupKey() {
  return DataSourceHolder.getDataSource();
 }
}

第二步

第二部编写DataSourceHolder类,提供设置、获取、情况数据源的方法,如下所示:

public class DataSourceHolder {
 /**
  * 线程本地环境
  */
 private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();

 /**
  * 设置数据源
  */
 public static void setDataSources(String connName) {
  dataSources.set(connName);
 }

 /**
  * 获取数据源
  */
 public static String getDataSource() {
  return dataSources.get();
 }

 /**
  * 清楚数据源
  */
 public static void clearDataSource() {
  dataSources.remove();
 }
}

第三步

第三步,编写DataSourceConfig类,该类主要作用是读取配置文件中的数据源连接集合,以及维护项目数据源的Bean对象,
代码如下:

@Component
@ConfigurationProperties("spring.datasource")
public class DataSourceConfig {
 private List<DataSourceModel> jdbc;

 public Map<Object, Object> getDataSourceMap(){
  Map<Object, Object>map=new HashMap<>();
  if (jdbc!=null&&jdbc.size()>0){
   for (int i = 0; i < jdbc.size() ; i++) {
    DataSourceBuilder dataSourceBuilder=DataSourceBuilder.create();
    dataSourceBuilder.driverClassName(jdbc.get(i).getDriverClassName());
    dataSourceBuilder.password(jdbc.get(i).getPassword());
    dataSourceBuilder.username(jdbc.get(i).getUsername());
    dataSourceBuilder.url(jdbc.get(i).getJdbcUrl());
    map.put(jdbc.get(i).getConnName(),dataSourceBuilder.build());
   }
  }
  return map;
 }

 @Bean
 public DataSource csgDataSource(){
  DynamicDataSource dynamicDataSource=new DynamicDataSource();
  Map<Object,Object>dataSourceMap=getDataSourceMap();
  dynamicDataSource.setTargetDataSources(dataSourceMap);
   Object object= dataSourceMap.values().toArray()[0];
  dynamicDataSource.setDefaultTargetDataSource(object);
  return dynamicDataSource;
 }

 public void setJdbc(List<DataSourceModel> jdbc) {
  this.jdbc = jdbc;
 }

 public List<DataSourceModel> getJdbc(){
  return this.jdbc;
 }
}

其中,getDataSourceMap()方法,作用是根据配置的连接集合,生成AbstractRoutingDataSource所需要的resolvedDataSources。

而csgDataSource()方法,添加了@Bean注解,作用是让mybatis的SqlSessionFactory,能够使用咱们维护的数据源。

第四部

编写MyBatisConfig类,该类主要作用是 配置好mybatis的数据源。

@Configuration
public class MyBatisConfig {

 @Autowired
 private DataSource csgDataSource;

 @Bean
 public SqlSessionFactory sqlSessionFactory() throws Exception {
  SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
  sqlSessionFactoryBean.setDataSource(csgDataSource);
  sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
    .getResources("classpath:mapper/**/*.xml"));
  return sqlSessionFactoryBean.getObject();
 }

 @Bean
 public PlatformTransactionManager platformTransactionManager(){
  return new DataSourceTransactionManager(csgDataSource);
 }
}

可以看到,这里选择的是我们定义好的csgDataSource,其作用也是如此。

第五步

编写TargetDataSource注解

/**
 * 注解标签
 * 作用于 方法、接口、类、枚举、注解
 * */

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.TYPE})
public @interface TargetDataSource {
 String connName();
}

其中connName,就是我们需要使用的数据源

第六步

编写DataSourceExchange,改类为切面,作用于TargetDataSource注解,故使用TargetDataSource注解的时候,
会根据connName自动选择数据源。

@Aspect
@Component
public class DataSourceExchange {

 @Before("@annotation(TargetDataSource)")
 public void before(JoinPoint joinPoint){
  MethodSignature sign = (MethodSignature) joinPoint.getSignature();
  Method method = sign.getMethod();
  boolean isMethodAop= method.isAnnotationPresent(TargetDataSource.class);
  if (isMethodAop){
   TargetDataSource datasource = method.getAnnotation(TargetDataSource.class);
   DataSourceHolder.setDataSources(datasource.connName());
  }else {
   if (joinPoint.getTarget().getClass().isAnnotationPresent(TargetDataSource.class)){
    TargetDataSource datasource = joinPoint.getTarget().getClass().getAnnotation(TargetDataSource.class);
    DataSourceHolder.setDataSources(datasource.connName());
   }
  }
 }

 @After("@annotation(TargetDataSource)")
 public void after(){
  DataSourceHolder.clearDataSource();
 }
}

改切面作用于方法运行前后,负责选择、取消数据源。

第七部

开始验证,使用方法如下:

@Service
public class UserServiceImpl implements UserService {
 @Autowired
 private UserMapper userMapper;

 @Override
 @TargetDataSource(connName = "nacos")
 public List<UserEntity> getList() {
  List<UserEntity> list= userMapper.selectUserList();
  return list;
 }
}

在service中,在需要进行数据库操作的方法上,添加TargetDataSource注解,即可自动切换到所需要的数据源。
至此,mybatis就可以动态切换数据源了。

笔者从事java开发工作不多,改方法可能不是太好,也请各位看官勿喷~

总结

到此这篇关于mybatis多数据源动态切换的文章就介绍到这了,更多相关mybatis多数据源动态切换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring + Mybatis 项目实现动态切换数据源实例详解

    项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库. 最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法. 参考了两篇文章如下: http://www.jb51.net/article/111840.htm http://www.jb51.net/article/111842.htm 这两篇文章都对原理进行了分析,下面只写自己的实现过程其他不再叙述. 实现思路是: 第一步,实现动态切换数据源:配置两个D

  • SpringBoot Mybatis动态数据源切换方案实现过程

    背景 最近让我做一个大数据的系统,分析了一下,麻烦的地方就是多数据源切换抽取数据.考虑到可以跨服务器跨数据库抽数,再整理数据,就配置了这个动态数据源的解决方案.在此分享给大家. 实现方案 数据库配置文件 我们项目使用的是yml形式的配置文件,采用的是hikari的数据库连接池.第一步我们自然是配置多个数据库源头. 我们找到spring的datasource,在下方配置三个数据源. spring: application: name: dynamicDatasource datasource: t

  • 关于Spring3 + Mybatis3整合时多数据源动态切换的问题

    以前的项目经历中,基本上都是spring + hibernate + Spring JDBC这种组合用的多.至于MyBatis,也就这个项目才开始试用,闲话不多说,进入正题. 以前的这种框架组合中,动态数据源切换可谓已经非常成熟了,网上也有非常多的博客介绍,都是继承AbstractRoutingDataSource,重写determineCurrentLookupKey()方法.具体做法就不在此废话了. 所以当项目中碰到这个问题,我几乎想都没有想,就采用了这种做法,但是一测试,一点反应都没有.当

  • spring boot + mybatis实现动态切换数据源实例代码

    前言 前几天有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源.我上网收集了一些资料整合了一下,虽然最后这个需求不了了之了,但是多数据源动态切换还是蛮好用的,所以记录一下,或许以后有用呢?或者自己感兴趣又想玩呢! 下面话不多说了,随着小编来一起看看详细的介绍吧 方法如下: 1.加个依赖 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybat

  • Mybatis多数据源切换实现代码

    这次要完成的是从一个数据库中读取数据,然后再把数据插入到另一个数据库中.在同一套项目代码中要完成这个操作,就不可避免的涉及到了多数据源.本文即介绍在mybatis中完成多数据源的切换相关内容 指定数据源一 @Configuration // 扫描 Mapper 接口并容器管理 @MapperScan(basePackages = MasterDataSourceConfig.PACKAGE, sqlSessionFactoryRef = "masterSqlSessionFactory"

  • spring boot+mybatis 多数据源切换(实例讲解)

    由于公司业务划分了多个数据库,开发一个项目会同事调用多个库,经过学习我们采用了注解+aop的方式实现的 1.首先定义一个注解类 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface TargetDataSource { String value();//此处接收的是数据源的名称 } 2.然后建一个配置类,这个在项目启动时会加载数据源,一开始采用了HikariCP,查资料说是最快性能最好的

  • Spring与Mybatis相结合实现多数据源切换功能

    废话不多说,关键代码如下所示: 1. 代码: DbContextHolder public class DbContextHolder { //线程安全的ThreadLocal private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static void setDbType(String dbType) { contextHolder.set(dbType

  • Spring+Mybatis动态切换数据源的方法

    功能需求是公司要做一个大的运营平台: 1.运营平台有自身的数据库,维护用户.角色.菜单.部分以及权限等基本功能. 2.运营平台还需要提供其他不同服务(服务A,服务B)的后台运营,服务A.服务B的数据库是独立的. 所以,运营平台至少要连三个库:运营库,A库,B库,并且希望达到针对每个功能请求能够自动切换到对应的数据源(我最终实现是针对Service的方法级别进行切换的,也可以实现针对每个DAO层的方法进行切换.我们系统的功能是相互之间比较独立的). 第一步:配置多数据源 1.定义数据源: 我采用的

  • Spring AOP如何实现注解式的Mybatis多数据源切换详解

    一.为什么要使用多数据源切换? 多数据源切换是为了满足什么业务场景?正常情况下,一个微服务或者说一个WEB项目,在使用Mybatis作为数据库链接和操作框架的情况下通常只需要构建一个系统库,在该系统库创建业务表来满足需求,当然也有分为测试库和正式库dev/prod,不过这俩库的切换是使用配置文件进行切分的,在项目启动时或者打成maven JAR包指定environment-dev.properties或者environment-prod.properties. 那么当程序运行过程中,比如一个co

  • SpringMVC Mybatis配置多个数据源并切换代码详解

    这篇文章主要介绍了SpringMVC Mybatis配置多个数据源并切换代码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 最近公司一个项目需要连接两个数据库(A和B)操作,有的模块查询A库,有的模块查询B库,因此需要改造下,项目后台用的是SpringMVC+Mybatis+MySQL架构,折腾了两天后终于搞定了,在这里记录过改造过程. 使用场景 多数据源的使用的场景一般有: 主从数据库切换 读写分离 兼容旧库 实现原理 Spring2.x

随机推荐