笔者主要从事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 多数据源动态切换的更多相关文章

  1. spring+mybatis多数据源动态切换

    spring mvc+mybatis+多数据源切换 选取oracle,mysql作为例子切换数据源.oracle为默认数据源,在测试的action中,进行mysql和oracle的动态切换. web. ...

  2. springboot多数据源动态切换和自定义mybatis分页插件

    1.配置多数据源 增加druid依赖 完整pom文件 数据源配置文件 route.datasource.driver-class-name= com.mysql.jdbc.Driver route.d ...

  3. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源 方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  4. Spring3.3 整合 Hibernate3、MyBatis3.2 配置多数据源/动态切换数据源方法

    一.开篇 这里整合分别采用了Hibernate和MyBatis两大持久层框架,Hibernate主要完成增删改功能和一些单一的对象查询功能,MyBatis主要负责查询功能.所以在出来数据库方言的时候基 ...

  5. Spring多数据源动态切换

    title: Spring多数据源动态切换 date: 2019-11-27 categories: Java Spring tags: 数据源 typora-root-url: ...... --- ...

  6. Springboot多数据源配置--数据源动态切换

    在上一篇我们介绍了多数据源,但是我们会发现在实际中我们很少直接获取数据源对象进行操作,我们常用的是jdbcTemplate或者是jpa进行操作数据库.那么这一节我们将要介绍怎么进行多数据源动态切换.添 ...

  7. 实战:Spring AOP实现多数据源动态切换

    需求背景 去年底,公司项目有一个需求中有个接口需要用到平台.算法.大数据等三个不同数据库的数据进行计算.组装以及最后的展示,当时这个需求是另一个老同事在做,我只是负责自己的部分. 直到今年回来了,这个 ...

  8. Spring + Mybatis 项目实现动态切换数据源

    项目背景:项目开发中数据库使用了读写分离,所有查询语句走从库,除此之外走主库. 最简单的办法其实就是建两个包,把之前数据源那一套配置copy一份,指向另外的包,但是这样扩展很有限,所有采用下面的办法. ...

  9. 170615、spring不同数据库数据源动态切换

    spring mvc+mybatis+多数据源切换 选取Oracle,MySQL作为例子切换数据源.mysql为默认数据源,在测试的action中,进行mysql和oracle的动态切换. 1.web ...

随机推荐

  1. gin框架使用orm操作数据库(转)

      简介:orm俗称关系对象模型,用来映射数据库SQL和对象的工具 ,相当于mongodb里面的mongoose库,Java里面的mybatis ibatis Golang GORM使用 https: ...

  2. fiddler 实用小技巧

    1.添加查看响应时间

  3. 【转】time 模块详解(时间获取和转换)

    转自鱼C论坛--https://fishc.com.cn/forum.php?mod=viewthread&tid=51326&highlight=time time 模块 -- 时间 ...

  4. ZooKeeper的数据模型

    ZooKeeper的数据模型 ZooKeeper提供的命名空间与标准的文件系统的命名空间非常类似:名称是由斜杠(/)分隔的一系列路径元素:ZooKeeper命名空间中的每个节点都由路径标识,如下图: ...

  5. ServletResponse使用介绍

    ServletResponse为将响应发送到客户端的对象:Servlet 容器创建 ServletResponse 对象,并将它作为参数传递给Servlet的service 方法,如下图 : Http ...

  6. 基于.Net Core开发的物联网平台 IoTSharp V1.5 发布

    很高兴的宣布新版本的发布, 这次更新我们带来了大量新特性, 最值得关注的是, 我们逐步开始支持分布式, 这意味着你可以通过多台服务器共同处理数据, 而不是原来的单机处理, 我们也将遥测数据进行分开存储 ...

  7. RocketMQ4.7.1双主双从集群搭建

    导读 上一集我们已经学会了SpringBoot整合RocketMQ点我直达,今天我们来搭建双主双从高性能MQ服务集群. 简介 主从架构 Broker角色,Master提供读写,Slave只支持读,Co ...

  8. 从原生web组件到框架组件源码(三)

    快乐的时光都是这么短暂,转眼间,web原生组件的知识点已经学完了,这个虽然暂时不一定有用,但是随着时间的积累,一步一个脚印的积累,你会有相应的收获,希望能变得更强,比如两年前我也会想有现成的东西不用, ...

  9. python实现非常有趣的数学问题

    1.无重复数字的三位数 题目:有1.2.3.4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少? import itertools ret = [] for i in range(1, 5) ...

  10. [Luogu P1119] 灾后重建 (floyd)

    题面 传送门:https://www.luogu.org/problemnew/show/P1119 Solution 这题的思想很巧妙. 首先,我们可以考虑一下最暴力的做法,对每个时刻的所有点都求一 ...