本例是在【Mybatis】MyBatis之配置多数据源(十)的基础上进行拓展,查看本例请先学习第十章

实现原理

  1、扩展Spring的AbstractRoutingDataSource抽象类(该类充当了DataSource的路由中介, 能有在运行时, 根据某种key值来动态切换到真正的DataSource上。)

     从AbstractRoutingDataSource的源码中,有一个数据源查找属性(dataSourceLookup),和一个 查询数据源方法 (resolveSpecifiedDataSource):

 private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();

 protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException {
if (dataSource instanceof DataSource) {
return (DataSource) dataSource;
}
else if (dataSource instanceof String) {
return this.dataSourceLookup.getDataSource((String) dataSource);
}
else {
throw new IllegalArgumentException(
"Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
}
}

  2、类中数据源查找类使用的是JndiDataSourceLookup,此类查找的是JNDI数据源,参考:【Tomcat】Tomcat 配置JNDI数据源(三)

  3、我们可以自定义动态数据源查找类来,来控制自定义数据源

实现案例

  1、搭建项目,参考:【Mybatis】MyBatis之配置多数据源(十)

  2、自定义数据源查找类

 package com.test.datasource;

 import java.beans.PropertyVetoException;
import java.sql.SQLException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import javax.sql.DataSource; import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
import org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException;
import org.springframework.util.Assert; import com.mchange.v2.c3p0.ComboPooledDataSource; public class DynamicDataSourceLookup implements DataSourceLookup { /**
* 数据源集合
*/
private final Map<String, DataSource> dataSources = new ConcurrentHashMap<>(); /**
* 根据数据源名称,获取数据源
*/
@Override
public DataSource getDataSource(String dataSourceName) throws DataSourceLookupFailureException { Assert.notNull(dataSourceName, "DataSourceName must not be null");
DataSource dataSource = this.dataSources.get(dataSourceName);
if (dataSource == null) {
// datasource not in map.. create one and add to map
dataSource = createDataSource(dataSourceName);
if(dataSource != null) {
addDataSource(dataSourceName, dataSource);
}
}
// 记录数据源状态
if(dataSource instanceof ComboPooledDataSource) {
ComboPooledDataSource ds = (ComboPooledDataSource) dataSource;
try {
System.out.println("ds.getMaxPoolSize():" + ds.getMaxPoolSize());// 最大连接数
System.out.println("ds.getMaxPoolSize():" + ds.getMaxPoolSize());// 最大连接数
System.out.println("ds.getMinPoolSize():" + ds.getMinPoolSize());// 最小连接数
System.out.println("ds.getNumBusyConnections():" + ds.getNumBusyConnections());// 正在使用连接数
System.out.println("ds.getNumIdleConnections():" + ds.getNumIdleConnections());// 空闲连接数
System.out.println("ds.getNumConnections():" + ds.getNumConnections());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}// 总连接数
}
return dataSource;
} /**
* 创建数据源
* @param dataSourceName
* @return
*/
private DataSource createDataSource(String dataSourceName) { ComboPooledDataSource dataSource = null; // 可根据其他业务数据,创建数据源
try {
dataSource = new ComboPooledDataSource();
dataSource.setUser("hd");
dataSource.setPassword("hd123456");
dataSource.setJdbcUrl("jdbc:mysql://mysql.naughty7878.top:3306/test_mybatis?allowPublicKeyRetrieval=true");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setDataSourceName(dataSourceName); } catch (PropertyVetoException e) {
throw new RuntimeException("Error creating data source");
}
return dataSource;
} /**
* 添加数据源到 数据源集合中
* @param dataSourceName
* @param dataSource
*/
public void addDataSource(String dataSourceName, DataSource dataSource) {
Assert.notNull(dataSourceName, "DataSourceName must not be null");
Assert.notNull(dataSource, "DataSource must not be null");
this.dataSources.put(dataSourceName, dataSource);
} /**
* 从数据源集合移除数据源
* @param dataSourceName
*/
public void removeDataSource(String dataSourceName) {
Assert.notNull(dataSourceName, "DataSourceName must not be null");
this.dataSources.remove(dataSourceName);
} }

  3、编辑动态数据源类,从写determineTargetDataSource方法,查询数据源时,先从自定义数据源中查找,然后从内部数据源中查找

 package com.test.datasource;

 import javax.sql.DataSource;

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

 /**
* 动态数据源(依赖于spring)
* @author chenheng
* @date 2019-08-03 17:27:35
*
*/
public class DynamicDataSource extends AbstractRoutingDataSource { @Override
protected Object determineCurrentLookupKey() {
return DataSourceHolder.getDataSourceKey();
} @Override
protected DataSource determineTargetDataSource() { Object lookupKey = determineCurrentLookupKey();
DataSource dataSource = null;
// 自定义外部数据源类中,查询数据源
if(lookupKey != null) {
dataSource = resolveSpecifiedDataSource(key);
}
// 在内部数据源中,查询数据源
if(dataSource == null){
dataSource= super.determineTargetDataSource();
}
return dataSource;
}
}

  4、注入dynamicDataSourceLookup,并且在dataSource注入属性dataSourceLookup

 <bean id="dynamicDataSourceLookup" class="com.test.datasource.DynamicDataSourceLookup">
</bean> <!-- 数据源:Spring用来控制业务逻辑。数据源、事务控制、aop -->
<bean id="dataSource" class="com.test.datasource.DynamicDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="dataSource1" value-ref="dataSource1"></entry>
<entry key="dataSource2" value-ref="dataSource2"></entry>
</map>
</property>
<!-- 默认目标数据源为你主库数据源 -->
<property name="defaultTargetDataSource" ref="dataSource1"/>
<property name="dataSourceLookup" ref="dynamicDataSourceLookup"/>
</bean>

  5、编辑是一个动态数据源DAO,来使用自定动态数据源

 package com.test.dao;

 import javax.sql.DataSource;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository; @Repository
public class DynamicDao extends JdbcDaoSupport { @Autowired
public DynamicDao(DataSource dataSource) {
this.setDataSource(dataSource);
} }

  6、在Service中使用

 package com.test.service;

 import java.util.List;

 import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.stereotype.Service; import com.test.dao.DynamicDao;
import com.test.datasource.DataSourceHolder;
import com.test.pojo.Employee; @Service
public class DynamicService { @Autowired
DynamicDao dynamicDao; public List<Employee> getEmployee() {
DataSourceHolder.setDataSourceKey("自定义");
return dynamicDao.getJdbcTemplate().query("select id, last_name lastName, gender, email from employee",
new BeanPropertyRowMapper<>(Employee.class));
} }

数据流转顺序:

  1.在 DataSourceHolder中,设置数据源名称

  2.Spring 调用determineCurrentLookupKey()方法<DynamicDataSource中重写AbstractRoutingDataSource类中的方法> ,从DataSourceHolder取出当前的数据库名称,并返回

  3.AbstractRoutingDataSource类中determineTargetDataSource()方法调用determineCurrentLookupKey(),先匹配外部数据库,然后匹配内部数据库;

  4.匹配到指定的数据库,并建立链接,即为切换到相应的数据库;

  5.在指定的数据库中执行相应的sql

【Mybatis】MyBatis之配置自定义数据源(十一)的更多相关文章

  1. springboot+mybatis +yml文件配置多数据源

    记录一下java开发中多数据源的配置过程, 参考博客:https://blog.csdn.net/weinichendian/article/details/72903757,我在这里进行了整理,使用 ...

  2. mybatis数据库基本配置包括数据源事物类型等

    <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC ...

  3. [教程] Spring+Mybatis环境配置多数据源

    一.简要概述 在做项目的时候遇到需要从两个数据源获取数据,项目使用的Spring + Mybatis环境,看到网上有一些关于多数据源的配置,自己也整理学习一下,然后自动切换实现从不同的数据源获取数据功 ...

  4. Spring Boot + MyBatis + Pagehelper 配置多数据源

    前言: 本文为springboot结合mybatis配置多数据源,在项目当中很多情况是使用主从数据源来读写分离,还有就是操作多库,本文介绍如何一个项目同时使用2个数据源. 也希望大家带着思考去学习!博 ...

  5. 【Mybatis】MyBatis之配置多数据源(十)

    在做项目的过程中,有时候一个数据源是不够,那么就需要配置多个数据源.本例介绍mybatis多数据源配置 前言 一般项目单数据源,使用流程如下: 单个数据源绑定给sessionFactory,再在Dao ...

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

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

  7. mybatis源码解读(三)——数据源的配置

    在mybatis-configuration.xml 文件中,我们进行了如下的配置: <!-- 可以配置多个运行环境,但是每个 SqlSessionFactory 实例只能选择一个运行环境常用: ...

  8. spring+myBatis 配置多数据源,切换数据源

    注:本文来源于  tianzhiwuqis <spring+myBatis 配置多数据源,切换数据源> 一个项目里一般情况下只会使用到一个数据库,但有的需求是要显示其他数据库的内容,像这样 ...

  9. SpringBoot系列七:SpringBoot 整合 MyBatis(配置 druid 数据源、配置 MyBatis、事务控制、druid 监控)

    1.概念:SpringBoot 整合 MyBatis 2.背景 SpringBoot 得到最终效果是一个简化到极致的 WEB 开发,但是只要牵扯到 WEB 开发,就绝对不可能缺少数据层操作,所有的开发 ...

随机推荐

  1. Bias vs. Variance(3)---用learning curves来判断bias/variance problem

    画learning curves可以用来检查我们的学习算法运行是否正常或者用来改进我们的算法,我们经常使用learning cruves来判断我们的算法是否存在bias problem/varianc ...

  2. 结构型模式(二) 桥接模式(Bridge)

    一.动机(Motivation) 在很多游戏场景中,会有这样的情况:[装备]本身会有的自己固有的逻辑,比如枪支,会有型号的问题,同时现在很多的游戏又在不同的介质平台上运行和使用,这样就使得游戏的[装备 ...

  3. Django REST framework认证权限和限制和频率

    认证.权限和限制 身份验证是将传入请求与一组标识凭据(例如请求来自的用户或其签名的令牌)相关联的机制.然后 权限 和 限制 组件决定是否拒绝这个请求. 简单来说就是: 认证确定了你是谁 权限确定你能不 ...

  4. 安装 selenium 对于python而言属于一个第三方的模块

    针对第三方的模块,如何安装 在dos界面输入python -m pip install 模块名称 安装相关的浏览器以及浏览器的驱动 下载谷歌浏览器的驱动,淘宝镜像 下载后,解压,然后将得到的exe文件 ...

  5. 014——C#新建文件夹

    (一)如果不存在路径就新建文件夹 string directory = @"C:\Users\Administrator\Desktop\温控数据\"; if (!Director ...

  6. WebAPI学习

    WebAPI概述 今天的web计算平台包含了广泛的功能,其中的大部分均可以通过API(应用程序编程接口)访问. web平台归为6个基本设施,都会用到webapi,包括存储服务.消息服务.计算服务.信息 ...

  7. 2019.11.11 模拟赛 T2 乘积求和

    昨天 ych 的膜你赛,这道题我 O ( n4 ) 暴力拿了 60 pts. 这道题的做法还挺妙的,我搞了将近一天呢qwq 题解 60 pts 根据题目给出的式子,四层 for 循环暴力枚举统计答案即 ...

  8. Codeforces 1239E. Turtle 折半

    原文链接www.cnblogs.com/zhouzhendong/p/CF1239E.html 前言 咕了这么久之后,我的博客复活了! 题解 结论1 存在一个最优解\(A\)数组,满足\(\foral ...

  9. SQLServer 使用自定义端口连接的方法(转载)

    使用过SQL Server的人大多都知道,SQL Server服务器默认监听的端口号是1433,但是我今天遇到的问题是我的机器上有三个数据库实例,这样使用TCP/IP远程连接时就产生了问题.如何在Mi ...

  10. SignalR简单实用_转自:https://www.cnblogs.com/humble/p/3851205.html

    一.指定通信方式 建立一个通讯方式需要一定的时间和客户机/服务器资源.如果客户机的功能是已知的,那么通信方式在客户端连接开始的时候就可以指定.下面的代码片段演示了使用AJAX长轮询方式来启动一个连接, ...