需求:项目使用了读写分离,或者数据进行了分库处理,我们希望在操作不同的数据库的时候,我们的程序能够动态的切换到相应的数据库,执行相关的操作。
首先,你需要一个能够正常运行的springboot项目,配置mybatis并且能够正常的操作数据库(增删查改)
现在开始实现:
思路:现在项目的结构设计基本上是基于MVC的,那么数据库的操作集中在dao层完成,主要业务逻辑在service层处理,controller层处理请求。假设在执行dao层代码之前能够将数据源(DataSource)换成我们想要执行操作的数据源,那么这个问题就解决了。
虽然思路是有了,但是怎么换呢?
爬了很多的博客查看了下官方的文档,我找到了这样一个类:AbstractRoutingDataSource,它继承于AbstractDataSource而AbstractDataSource又实现了DataSource接口,是一个标准的数据源。
查看AbstractRoutingDataSource类:
/**
* Abstract {@link javax.sql.DataSource} implementation that routes {@link #getConnection()}
* calls to one of various target DataSources based on a lookup key. The latter is usually
* (but not necessarily) determined through some thread-bound transaction context.
*
* @author Juergen Hoeller
* @since 2.0.1
* @see #setTargetDataSources
* @see #setDefaultTargetDataSource
* @see #determineCurrentLookupKey()
*/
//翻译结果如下
/**
* 抽象 {@link javax.sql.DataSource} 路由 {@link #getConnection ()} 的实现
* 根据查找键调用不同的目标数据之一。后者通常是
* (但不一定) 通过某些线程绑定事务上下文来确定。
*
* @author 史塔克 Hoeller
* @since 2.0。1
* @see #setTargetDataSources
* @see #setDefaultTargetDataSource
* @see #determineCurrentLookupKey ()
*/
public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean {
.......
/**
* Specify the map of target DataSources, with the lookup key as key.
* The mapped value can either be a corresponding {@link javax.sql.DataSource}
* instance or a data source name String (to be resolved via a
* {@link #setDataSourceLookup DataSourceLookup}).
* <p>The key can be of arbitrary type; this class implements the
* generic lookup process only. The concrete key representation will
* be handled by {@link #resolveSpecifiedLookupKey(Object)} and
* {@link #determineCurrentLookupKey()}.
*/
//翻译如下
/**
*指定目标数据源的映射,查找键为键。
*映射的值可以是相应的{@link javax.sql.DataSource}
*实例或数据源名称字符串(要通过
* {@link #setDataSourceLookup DataSourceLookup})。
*键可以是任意类型的; 这个类实现了
*通用查找过程只。 具体的关键表示将
*由{@link #resolveSpecifiedLookupKey(Object)}和
* {@link #determineCurrentLookupKey()}。
*/
public void setTargetDataSources(Map<Object, Object> targetDataSources) {
this.targetDataSources = targetDataSources;
}
......
/**
* Determine the current lookup key. This will typically be
* implemented to check a thread-bound transaction context.
* <p>Allows for arbitrary keys. The returned key needs
* to match the stored lookup key type, as resolved by the
* {@link #resolveSpecifiedLookupKey} method.
*/
//翻译如下
/**
* 确定当前的查找键。这通常会
* 实现以检查线程绑定的事务上下文。
* <p> 允许任意键。返回的密钥需要
* 与存储的查找密钥类型匹配, 如
* {@link #resolveSpecifiedLookupKey} 方法。
*/
protected abstract Object determineCurrentLookupKey();
}
我这里只是选择性的贴出了部分源码,详细信息大家可以在IDE里直接点进去查看。翻译的很鸡肋,但是可以明白,这个类的基本运作方式,它是一个abstract类,所以我们使用的话,推荐的方式是创建一个类来继承它并且实现它的determineCurrentLookupKey()
方法,这个方法介绍上面也进行了说明,就是通过这个方法进行数据源的切换,这个时候你会又疑问,我没设置数据源,它是怎么切换的?上面我贴出了另外一个核心的方法setTargetDataSources(Map<Object, Object> targetDataSources)
,它需要一个Map,在方法注释中我们可以得知,这个Map存储的就是我们配置的多个数据源的键值对。我们整理一下这个类切换数据源的运作方式,这个类在连接数据库之前会执行determineCurrentLookupKey()方法,这个方法返回的数据将作为key去targetDataSources中查找相应的值,如果查找到相对应的DataSource,那么就使用此DataSource获取数据库连接。
基本的原理已经说完了,接下来去项目里面配置就OK。
1、创建枚举类DataSourceKey列出你所有的数据源名称,当然了,类名你