在以往的学习中,经常会听到数据库的分库分表等操作,我就会想,平常的应用都是连接一个数据库,但是分库时候,要连接的数据库就不止一个,这个时候后台是怎么实现的?答案是肯定的!切换数据源。我们只要配置需要用到的多个数据源,然后在需要的地方进行数据源的切换,后端应用就可以像指针一样指向具体的某一个数据库,获取到到数据库的连接,接下来的操作就是针对该数据源所指向的数据库的。
那么,要如何实现数据库的动态切换呢?经过我阅读过几篇博文并加上自己的思考后,得出了一下解决思路,在这里以文档的形式做下记录。
首先,需要弄明白“数据源”是一个什么概念。在传统的java连接数据库的操作中,是用到DriverManager的getConnection()方法,该方法的弊端很明显,会频繁的创建和销毁到数据库的连接,这是一种十分耗时且极大浪费资源的操作。因此后来进行了优化,普遍使用数据库连接池的改进方案。顾名思义,数据库连接池就是一个装有许多数据库连接的“池子”,或者说是容器。需要连接数据库时只需要向该连接池索要到数据库的连接,即可对指定数据库进行操作;当操作结束后,不急于销毁连接,而是将连接放回池子中,使得连接可以服用。数据源DataSource,顾名思义就是数据的源头,内部含有用来表征到达指定数据库的必要信息,如username,url等。目前,提到数据源DataSource基本可以等于数据库连接池,或是包含连接池,可以通过调用DataSource的getConnection()方法来获取到某个数据库的连接。
下面简述思路中的重要细节。自定义动态数据源,该类继承AbstractRoutingDataSource类,该类是springboot提供的,大名鼎鼎的DruidDataSource、HirakiDataSource都继承该类。因此可以将我们自定义的数据源类想象成与上述两类处在一个层次。进入AbstractRoutingDataSource类中阅读源码,发现重点字段和方法如下:
targetDataSources:目标数据源,是一个map。key是用来索引value的键,value是具体的dataSource对象。
defaultTargetDataSource:默认的数据源。是一个DataSource对象。
resolvedDataSources、resolvedDefaultDataSource:这俩可以类比上面俩,分别是一个map和一个对象。
在外部,我们是将数据源都加入AbstractRoutingDataSource中的targetDataSources中,并且设置一个defaultTargetDataSource默认数据源。在AbstractRoutingDataSource内部,AbstractRoutingDataSource会在执行初始化的时候,将target的map和对象转换到resolved的map和对象中,实际用的是resolved的。
关键方法是determineTargetDataSource()和determineCurrentLookupKey():determineTargetDataSource()方法是用来确定使用哪个数据源的(因为AbstractRoutingDataSource类中的map中有多个配置的数据源,这也从侧面说明了切换数据源的可能性!)。该方法中有一个determineCurrentLookupKey()方法,这个方法是一个抽象的空方法,用来给出具体的key,好来映射出map中的数据源。这个方法就是我们自定义的地方!!用一个ThreadLocal来存储键,并在该方法中取出键。如下如所示:
数据源的配置就入下所示:
既然有多个数据源,那就有多个数据库,在数据源切换了之后,操作的数据库的model、dao、mapper等也要跟上。做法思路很简单,平常单数据源单数据库时是怎么做的,现在就横向拓展即可,使得每个数据库都有自己的model、dao、mapper即可。
库都有自己的model、dao、mapper即可。