zoukankan      html  css  js  c++  java
  • 【Mybatis】MyBatis之配置自定义数据源(十一)

      本例是在【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.sql.SQLException;
      5 import java.util.Map;
      6 import java.util.concurrent.ConcurrentHashMap;
      7 
      8 import javax.sql.DataSource;
      9 
     10 import org.springframework.jdbc.datasource.lookup.DataSourceLookup;
     11 import org.springframework.jdbc.datasource.lookup.DataSourceLookupFailureException;
     12 import org.springframework.util.Assert;
     13 
     14 import com.mchange.v2.c3p0.ComboPooledDataSource;
     15 
     16 public class DynamicDataSourceLookup implements DataSourceLookup {
     17 
     18     /**
     19      * 数据源集合
     20      */
     21     private final Map<String, DataSource> dataSources = new ConcurrentHashMap<>();
     22 
     23     /**
     24      * 根据数据源名称,获取数据源
     25      */
     26     @Override
     27     public DataSource getDataSource(String dataSourceName) throws DataSourceLookupFailureException {
     28 
     29         Assert.notNull(dataSourceName, "DataSourceName must not be null");
     30         DataSource dataSource = this.dataSources.get(dataSourceName);
     31         if (dataSource == null) {
     32             // datasource not in map.. create one and add to map
     33             dataSource = createDataSource(dataSourceName);
     34             if(dataSource != null) {
     35                 addDataSource(dataSourceName, dataSource);
     36             }
     37         }
     38         // 记录数据源状态
     39         if(dataSource instanceof ComboPooledDataSource) {
     40             ComboPooledDataSource ds = (ComboPooledDataSource) dataSource;
     41             try {
     42                 System.out.println("ds.getMaxPoolSize():" + ds.getMaxPoolSize());// 最大连接数
     43                 System.out.println("ds.getMaxPoolSize():" + ds.getMaxPoolSize());// 最大连接数
     44                 System.out.println("ds.getMinPoolSize():" + ds.getMinPoolSize());// 最小连接数
     45                 System.out.println("ds.getNumBusyConnections():" + ds.getNumBusyConnections());// 正在使用连接数
     46                 System.out.println("ds.getNumIdleConnections():" + ds.getNumIdleConnections());// 空闲连接数
     47                 System.out.println("ds.getNumConnections():" + ds.getNumConnections());
     48             } catch (SQLException e) {
     49                 // TODO Auto-generated catch block
     50                 e.printStackTrace();
     51             }// 总连接数
     52         }
     53         return dataSource;
     54     }
     55 
     56     /**
     57      * 创建数据源
     58      * @param dataSourceName
     59      * @return
     60      */
     61     private DataSource createDataSource(String dataSourceName) {
     62         
     63         ComboPooledDataSource dataSource = null;
     64         
     65         // 可根据其他业务数据,创建数据源
     66         try {
     67             dataSource = new ComboPooledDataSource();
     68             dataSource.setUser("admin");
     69             dataSource.setPassword("123456");
     70             dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/test_mybatis?allowPublicKeyRetrieval=true");
     71             dataSource.setDriverClass("com.mysql.jdbc.Driver");
     72             dataSource.setDataSourceName(dataSourceName);
     73             
     74             
     75         } catch (PropertyVetoException e) {
     76             throw new RuntimeException("Error creating data source");
     77         }
     78         return dataSource;
     79     }
     80 
     81     /**
     82      * 添加数据源到 数据源集合中
     83      * @param dataSourceName
     84      * @param dataSource
     85      */
     86     public void addDataSource(String dataSourceName, DataSource dataSource) {
     87         Assert.notNull(dataSourceName, "DataSourceName must not be null");
     88         Assert.notNull(dataSource, "DataSource must not be null");
     89         this.dataSources.put(dataSourceName, dataSource);
     90     }
     91 
     92     /**
     93      * 从数据源集合移除数据源
     94      * @param dataSourceName
     95      */
     96     public void removeDataSource(String dataSourceName) {
     97         Assert.notNull(dataSourceName, "DataSourceName must not be null");
     98         this.dataSources.remove(dataSourceName);
     99     }
    100 
    101 }

      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

  • 相关阅读:
    springJDBC01 利用springJDBC操作数据库
    Struts2框架05 result标签的类型、拦截器
    Struts2框架04 struts和spring整合
    JDBC03 利用JDBC实现事务提交与回滚【调用Connection中的方法实现事务管理】
    JDBC02 利用JDBC连接数据库【使用数据库连接池】
    JDBC01 利用JDBC连接数据库【不使用数据库连接池】
    ???Struts2框架03 session的使用、登录逻辑【session工作原理】
    Struts2框架02 消息传递
    Struts2框架01【如果使用struts框架】【利用struts框架写一个 hello world】
    格式化输出
  • 原文地址:https://www.cnblogs.com/h--d/p/11388044.html
Copyright © 2011-2022 走看看