MybatisMyBatis之配置自定义数据源

Posted h--d

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MybatisMyBatis之配置自定义数据源相关的知识,希望对你有一定的参考价值。

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

实现原理

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

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

 1 private DataSourceLookup dataSourceLookup = new JndiDataSourceLookup();
 2 
 3 
 4 protected DataSource resolveSpecifiedDataSource(Object dataSource) throws IllegalArgumentException 
 5   if (dataSource instanceof DataSource) 
 6     return (DataSource) dataSource;
 7   
 8   else if (dataSource instanceof String) 
 9     return this.dataSourceLookup.getDataSource((String) dataSource);
10   
11   else 
12     throw new IllegalArgumentException(
13         "Illegal data source value - only [javax.sql.DataSource] and String supported: " + dataSource);
14   
15 

 

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

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

实现案例

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

  2、自定义数据源查找类

 1 package com.test.datasource;
 2 
 3 import java.beans.PropertyVetoException;
 4 import java.util.Map;
 5 import java.util.concurrent.ConcurrentHashMap;
 6 
 7 import javax.sql.DataSource;
 8 
 9 import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
10 import org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException;
11 import org.springframework.util.Assert;
12 
13 import com.mchange.v2.c3p0.ComboPooledDataSource;
14 
15 public class DynamicDataSourceLookup implements DataSourceLookup 
16 
17     /**
18      * 数据源集合
19      */
20     private final Map<String, DataSource> dataSources = new ConcurrentHashMap<>();
21 
22     /**
23      * 根据数据源名称,获取数据源
24      */
25     @Override
26     public DataSource getDataSource(String dataSourceName) throws DataSourceLookupFailureException 
27 
28         Assert.notNull(dataSourceName, "DataSourceName must not be null");
29         DataSource dataSource = this.dataSources.get(dataSourceName);
30         if (dataSource == null) 
31             // datasource not in map.. create one and add to map
32             dataSource = createDataSource(dataSourceName);
33             if(dataSource != null) 
34                 addDataSource(dataSourceName, dataSource);
35             
36         
37         return dataSource;
38     
39 
40     /**
41      * 创建数据源
42      * @param dataSourceName
43      * @return
44      */
45     private DataSource createDataSource(String dataSourceName) 
46         
47         ComboPooledDataSource dataSource = null;
48         
49         // 可根据其他业务数据,创建数据源
50         try 
51             dataSource = new ComboPooledDataSource();
52             dataSource.setUser("admin");
53             dataSource.setPassword("admin");
54             dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test_mybatis?allowPublicKeyRetrieval=true");
55             dataSource.setDriverClass("com.mysql.jdbc.Driver");
56             dataSource.setDataSourceName(dataSourceName);
57             
58          catch (PropertyVetoException e) 
59             throw new RuntimeException("Error creating data source");
60         
61         return dataSource;
62     
63 
64     /**
65      * 添加数据源到 数据源集合中
66      * @param dataSourceName
67      * @param dataSource
68      */
69     public void addDataSource(String dataSourceName, DataSource dataSource) 
70         Assert.notNull(dataSourceName, "DataSourceName must not be null");
71         Assert.notNull(dataSource, "DataSource must not be null");
72         this.dataSources.put(dataSourceName, dataSource);
73     
74 
75     /**
76      * 从数据源集合移除数据源
77      * @param dataSourceName
78      */
79     public void removeDataSource(String dataSourceName) 
80         Assert.notNull(dataSourceName, "DataSourceName must not be null");
81         this.dataSources.remove(dataSourceName);
82     
83 
84 

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

 1 package com.test.datasource;
 2 
 3 import javax.sql.DataSource;
 4 
 5 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
 6 
 7 /**
 8  *     动态数据源(依赖于spring)
 9  * @author chenheng
10  * @date 2019-08-03 17:27:35
11  *
12  */
13 public class DynamicDataSource extends AbstractRoutingDataSource 
14 
15     @Override
16     protected Object determineCurrentLookupKey() 
17          return DataSourceHolder.getDataSourceKey();
18     
19 
20     @Override
21     protected DataSource determineTargetDataSource() 
22         
23         Object lookupKey = determineCurrentLookupKey();24         DataSource dataSource = null;
25         // 自定义外部数据源类中,查询数据源
26         if(lookupKey != null) 
27             dataSource =  resolveSpecifiedDataSource(key);
28         
29         // 在内部数据源中,查询数据源
30         if(dataSource == null)
31             dataSource= super.determineTargetDataSource();
32         
33         return dataSource;
34     
35 

 

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

 1 <bean id="dynamicDataSourceLookup" class="com.test.datasource.DynamicDataSourceLookup">
 2 </bean>
 3 
 4 
 5 <!-- 数据源:Spring用来控制业务逻辑。数据源、事务控制、aop -->
 6 <bean id="dataSource" class="com.test.datasource.DynamicDataSource">
 7     <property name="targetDataSources">
 8         <map key-type="java.lang.String">
 9         <entry key="dataSource1" value-ref="dataSource1"></entry>
10             <entry key="dataSource2" value-ref="dataSource2"></entry>
11         </map>
12     </property>
13     <!-- 默认目标数据源为你主库数据源 -->
14     <property name="defaultTargetDataSource" ref="dataSource1"/>
15     <property name="dataSourceLookup" ref="dynamicDataSourceLookup"/>
16 </bean>

 

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

 1 package com.test.dao;
 2 
 3 
 4 import javax.sql.DataSource;
 5 
 6 import org.springframework.beans.factory.annotation.Autowired;
 7 import org.springframework.jdbc.core.support.JdbcDaoSupport;
 8 import org.springframework.stereotype.Repository;
 9 
10 
11 @Repository
12 public class DynamicDao extends JdbcDaoSupport 
13 
14     @Autowired
15     public DynamicDao(DataSource dataSource) 
16         this.setDataSource(dataSource);
17     
18 
19 

  6、在Service中使用

 1 package com.test.service;
 2 
 3 import java.util.List;
 4 
 5 import org.springframework.beans.factory.annotation.Autowired;
 6 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 7 import org.springframework.stereotype.Service;
 8 
 9 import com.test.dao.DynamicDao;
10 import com.test.datasource.DataSourceHolder;
11 import com.test.pojo.Employee;
12 
13 @Service
14 public class DynamicService 
15 
16     @Autowired
17     DynamicDao dynamicDao;
18     
19     public List<Employee> getEmployee() 
20         DataSourceHolder.setDataSourceKey("自定义");
21         return dynamicDao.getJdbcTemplate().query("select id, last_name lastName, gender, email from employee",
22                 new BeanPropertyRowMapper<>(Employee.class));
23     
24     
25 

 

数据流转顺序:

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

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

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

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

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

 

以上是关于MybatisMyBatis之配置自定义数据源的主要内容,如果未能解决你的问题,请参考以下文章

MyBatisMyBatis 核心配置综述之StatementHandler

MyBatisMyBatis之配置讲解

MyBatisMyBatis 启动流程

MyBatisMyBatis 核心配置综述之 ResultSetHandler

docker网络配置之自定义网桥

MyBatisMyBatis 核心配置综述之 ParameterHandler