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

      在做项目的过程中,有时候一个数据源是不够,那么就需要配置多个数据源。本例介绍mybatis多数据源配置

    前言

      一般项目单数据源,使用流程如下:

        

      单个数据源绑定给sessionFactory,再在Dao层操作,若多个数据源的话,那不是就成了下图

        

      可见,sessionFactory都写死在了Dao层,若我再添加个数据源的话,则又得添加一个sessionFactory。所以比较好的做法应该是下图

        

    实现原理

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

         从AbstractRoutingDataSource的源码中:

    1 public abstract class AbstractRoutingDataSource extends AbstractDataSource implements InitializingBean

      2、我们可以看到,它继承了AbstractDataSource,而AbstractDataSource不就是javax.sql.DataSource的子类,So我们可以分析下它的getConnection方法:

    1 public Connection getConnection() throws SQLException {  
    2     return determineTargetDataSource().getConnection();  
    3 }  
    4   
    5 public Connection getConnection(String username, String password) throws SQLException {  
    6      return determineTargetDataSource().getConnection(username, password);  
    7 }

      3、 获取连接的方法中,重点是determineTargetDataSource()方法,看源码:

     1 /** 
     2      * Retrieve the current target DataSource. Determines the 
     3      * {@link #determineCurrentLookupKey() current lookup key}, performs 
     4      * a lookup in the {@link #setTargetDataSources targetDataSources} map, 
     5      * falls back to the specified 
     6      * {@link #setDefaultTargetDataSource default target DataSource} if necessary. 
     7      * @see #determineCurrentLookupKey() 
     8      */  
     9     protected DataSource determineTargetDataSource() {  
    10         Assert.notNull(this.resolvedDataSources, "DataSource router not initialized");  
    11         Object lookupKey = determineCurrentLookupKey();    
    12         DataSource dataSource = this.resolvedDataSources.get(lookupKey);  
    13         if (dataSource == null && (this.lenientFallback || lookupKey == null)) {  
    14             dataSource = this.resolvedDefaultDataSource;  
    15         }  
    16         if (dataSource == null) {  
    17             throw new IllegalStateException("Cannot determine target DataSource for lookup key [" + lookupKey + "]");  
    18         }  
    19         return dataSource;  
    20     }

      上面这段源码的重点在于determineCurrentLookupKey()方法,这是AbstractRoutingDataSource类中的一个抽象方法,而它的返回值是你所要用的数据源dataSource的key值,有了这个key值,resolvedDataSource(这是个map,由配置文件中设置好后存入的)就从中取出对应的DataSource,如果找不到,就用配置默认的数据源。

      看完源码,应该有点启发了吧,没错!你要扩展AbstractRoutingDataSource类,并重写其中的determineCurrentLookupKey()方法,来实现数据源的切换

    案例

      1、搭建一个Springmvc + Spring + Mybatis  maven项目,POM文件中引入AOP相关依赖,参考:【Mybatis】MyBatis之整合Spring(八)

      1 <project xmlns="http://maven.apache.org/POM/4.0.0"
      2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
      4     <modelVersion>4.0.0</modelVersion>
      5     <groupId>com.test</groupId>
      6     <artifactId>test-spring-mybatis</artifactId>
      7     <packaging>war</packaging>
      8     <version>1.0.0-SNAPSHOT</version>
      9     <url>http://maven.apache.org</url>
     10 
     11     <!-- 定义maven变量 -->
     12     <properties>
     13         <!-- spring -->
     14         <spring.version>5.1.4.RELEASE</spring.version>
     15 
     16         <!-- Mybatis -->
     17         <mybatis.version>3.5.0</mybatis.version>
     18         <!-- Mybatis 整合 Spring -->
     19         <mybatis-spring.version>2.0.0</mybatis-spring.version>
     20 
     21         <!-- mysql -->
     22         <mysql.version>8.0.13</mysql.version>
     23 
     24         <!-- c3p0 连接池 -->
     25         <c3p0.version>0.9.5.4</c3p0.version>
     26 
     27         <!-- logback -->
     28         <slf4j-api.version>1.7.5</slf4j-api.version>
     29         <logback.version>0.9.30</logback.version>
     30 
     31         <!-- Servlet -->
     32         <servlet.version>3.0.1</servlet.version>
     33         <jsp-api.version>2.2</jsp-api.version>
     34 
     35         <!-- jstl -->
     36         <jstl.version>1.2</jstl.version>
     37         <standard.version>1.1.2</standard.version>
     38 
     39         <!-- test junit -->
     40         <junit.version>3.8.1</junit.version>
     41 
     42         <!-- jdk -->
     43         <jdk.version>1.8</jdk.version>
     44         <maven.compiler.plugin.version>2.3.2</maven.compiler.plugin.version>
     45     </properties>
     46 
     47 
     48     <dependencies>
     49 
     50         <!-- Spring IOC 核心容器 -->
     51         <dependency>
     52             <groupId>org.springframework</groupId>
     53             <artifactId>spring-core</artifactId>
     54             <version>${spring.version}</version>
     55         </dependency>
     56 
     57         <dependency>
     58             <groupId>org.springframework</groupId>
     59             <artifactId>spring-beans</artifactId>
     60             <version>${spring.version}</version>
     61         </dependency>
     62 
     63         <dependency>
     64             <groupId>org.springframework</groupId>
     65             <artifactId>spring-context</artifactId>
     66             <version>${spring.version}</version>
     67         </dependency>
     68 
     69         <dependency>
     70             <groupId>org.springframework</groupId>
     71             <artifactId>spring-expression</artifactId>
     72             <version>${spring.version}</version>
     73         </dependency>
     74 
     75         <!-- Spring AOP 切面 模块 -->
     76         <dependency>
     77             <groupId>org.springframework</groupId>
     78             <artifactId>spring-aop</artifactId>
     79             <version>${spring.version}</version>
     80         </dependency>
     81 
     82         <dependency>
     83             <groupId>org.aspectj</groupId>
     84             <artifactId>aspectjrt</artifactId>
     85             <version>1.9.2</version>
     86         </dependency>
     87         
     88         <dependency>
     89             <groupId>org.aspectj</groupId>
     90             <artifactId>aspectjweaver</artifactId>
     91             <version>1.9.2</version>
     92         </dependency>
     93 
     94         <!-- Spring WEB MVC 模块 -->
     95         <dependency>
     96             <groupId>org.springframework</groupId>
     97             <artifactId>spring-web</artifactId>
     98             <version>${spring.version}</version>
     99         </dependency>
    100 
    101         <dependency>
    102             <groupId>org.springframework</groupId>
    103             <artifactId>spring-webmvc</artifactId>
    104             <version>${spring.version}</version>
    105         </dependency>
    106 
    107         <!-- Spring 事物 模块 -->
    108         <dependency>
    109             <groupId>org.springframework</groupId>
    110             <artifactId>spring-tx</artifactId>
    111             <version>${spring.version}</version>
    112         </dependency>
    113 
    114         <!-- Spring ORM 对象关系映射 模块 -->
    115         <dependency>
    116             <groupId>org.springframework</groupId>
    117             <artifactId>spring-orm</artifactId>
    118             <version>${spring.version}</version>
    119         </dependency>
    120 
    121         <!-- Spring JDBC 模块 -->
    122         <dependency>
    123             <groupId>org.springframework</groupId>
    124             <artifactId>spring-jdbc</artifactId>
    125             <version>${spring.version}</version>
    126         </dependency>
    127 
    128         <!-- Mybatis -->
    129         <dependency>
    130             <groupId>org.mybatis</groupId>
    131             <artifactId>mybatis</artifactId>
    132             <version>${mybatis.version}</version>
    133         </dependency>
    134 
    135         <!-- Mybatis 整合 Spring -->
    136         <dependency>
    137             <groupId>org.mybatis</groupId>
    138             <artifactId>mybatis-spring</artifactId>
    139             <version>${mybatis-spring.version}</version>
    140         </dependency>
    141 
    142         <!-- mysql -->
    143         <dependency>
    144             <groupId>mysql</groupId>
    145             <artifactId>mysql-connector-java</artifactId>
    146             <version>${mysql.version}</version>
    147         </dependency>
    148 
    149         <!-- c3p0 连接池 -->
    150         <!-- https://mvnrepository.com/artifact/c3p0/c3p0 -->
    151         <dependency>
    152             <groupId>com.mchange</groupId>
    153             <artifactId>c3p0</artifactId>
    154             <version>${c3p0.version}</version>
    155         </dependency>
    156 
    157 
    158         <!-- logback -->
    159         <dependency>
    160             <groupId>org.slf4j</groupId>
    161             <artifactId>slf4j-api</artifactId>
    162             <version>${slf4j-api.version}</version>
    163             <type>jar</type>
    164             <scope>compile</scope>
    165         </dependency>
    166 
    167         <dependency>
    168             <groupId>ch.qos.logback</groupId>
    169             <artifactId>logback-core</artifactId>
    170             <version>${logback.version}</version>
    171             <type>jar</type>
    172         </dependency>
    173 
    174         <dependency>
    175             <groupId>ch.qos.logback</groupId>
    176             <artifactId>logback-classic</artifactId>
    177             <version>${logback.version}</version>
    178             <type>jar</type>
    179         </dependency>
    180 
    181         <dependency>
    182             <groupId>ch.qos.logback</groupId>
    183             <artifactId>logback-access</artifactId>
    184             <version>${logback.version}</version>
    185         </dependency>
    186 
    187 
    188         <!-- Servlet -->
    189         <dependency>
    190             <groupId>javax.servlet</groupId>
    191             <artifactId>javax.servlet-api</artifactId>
    192             <version>${servlet.version}</version>
    193             <scope>provided</scope>
    194         </dependency>
    195         <dependency>
    196             <groupId>javax.servlet.jsp</groupId>
    197             <artifactId>jsp-api</artifactId>
    198             <version>${jsp-api.version}</version>
    199             <scope>provided</scope>
    200         </dependency>
    201 
    202         <!-- jstl -->
    203         <dependency>
    204             <groupId>javax.servlet</groupId>
    205             <artifactId>jstl</artifactId>
    206             <version>${jstl.version}</version>
    207         </dependency>
    208 
    209         <dependency>
    210             <groupId>taglibs</groupId>
    211             <artifactId>standard</artifactId>
    212             <version>${standard.version}</version>
    213         </dependency>
    214 
    215         <!-- test -->
    216         <dependency>
    217             <groupId>junit</groupId>
    218             <artifactId>junit</artifactId>
    219             <version>${junit.version}</version>
    220             <scope>test</scope>
    221         </dependency>
    222 
    223     </dependencies>
    224 
    225     <build>
    226         <plugins>
    227             <!-- define the project compile level -->
    228             <plugin>
    229                 <groupId>org.apache.maven.plugins</groupId>
    230                 <artifactId>maven-compiler-plugin</artifactId>
    231                 <version>${maven.compiler.plugin.version}</version>
    232                 <configuration>
    233                     <source>${jdk.version}</source>
    234                     <target>${jdk.version}</target>
    235                 </configuration>
    236             </plugin>
    237         </plugins>
    238         <finalName>test_spring_mybatis</finalName>
    239     </build>
    240 </project>
    pom.xml

      2、编辑一个扩展AbstractRoutingDataSource类,DynamicDataSource.java

     1 package com.test.datasource;
     2 
     3 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
     4 
     5 /**
     6  *     动态数据源(依赖于spring)
     7  * @author chenheng
     8  * @date 2019-08-03 17:27:35
     9  *
    10  */
    11 public class DynamicDataSource extends AbstractRoutingDataSource {
    12 
    13     @Override
    14     protected Object determineCurrentLookupKey() {
    15          return DataSourceHolder.getDataSource();
    16     }
    17 
    18 }

      3、 封装一个的对数据源进行操作的类,DataSourceHolder.java

     1 package com.test.datasource;
     2 
     3 public class DataSourceHolder {
     4     
     5     // 线程本地环境
     6     private static final ThreadLocal<String> dataSources = new ThreadLocal<String>();
     7 
     8     // 设置数据源
     9     public static void setDataSource(String customerType) {
    10         dataSources.set(customerType);
    11     }
    12 
    13     // 获取数据源
    14     public static String getDataSource() {
    15         return (String) dataSources.get();
    16     }
    17 
    18     // 清除数据源
    19     public static void clearDataSource() {
    20         dataSources.remove();
    21     }
    22 }

      4、当需要切换数据源的时候执行啦。手动在代码中调用写死吗?调用setDataSource方法

        但是这种方法比较死板,所以我们可以应用spring aop来设置,把配置的数据源类型都设置成为注解标签,在service层中需要切换数据源的方法上,写上注解标签,调用相应方法切换数据源咯(就跟你设置事务一样)

    1 @TargetDataSource(name=TargetDataSource.SLAVE)
    2 public List<Employee> getEmpsFromSalve()

        编辑注解标签TargetDataSource.java

     1 package com.test.annotation;
     2 
     3 import java.lang.annotation.*;
     4 
     5 @Target({ElementType.METHOD, ElementType.TYPE})
     6 @Retention(RetentionPolicy.RUNTIME)
     7 @Documented
     8 public @interface TargetDataSource {
     9     
    10     String name() default TargetDataSource.MASTER;
    11  
    12     public static String MASTER = "dataSource1";
    13  
    14     public static String SLAVE = "dataSource2";
    15  
    16 }

      5、编辑切面的Bean,DataSourceExchange.java

     1 package com.test.datasource;
     2 
     3 import java.lang.reflect.Method;
     4 
     5 import org.springframework.aop.AfterReturningAdvice;
     6 import org.springframework.aop.MethodBeforeAdvice;
     7 
     8 import com.test.annotation.TargetDataSource;
     9 
    10 public class DataSourceExchange implements MethodBeforeAdvice, AfterReturningAdvice {
    11 
    12     @Override
    13     public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
    14         DataSourceHolder.clearDataSource();
    15     }
    16 
    17     @Override
    18     public void before(Method method, Object[] args, Object target) throws Throwable {
    19         // 这里TargetDataSource是自定义的注解
    20         if (method.isAnnotationPresent(TargetDataSource.class)) {
    21             TargetDataSource datasource = method.getAnnotation(TargetDataSource.class);
    22             DataSourceHolder.setDataSource(datasource.name());
    23         } else {
    24             if(target.getClass().isAnnotationPresent(TargetDataSource.class))
    25             {
    26                 TargetDataSource datasource = target.getClass().getAnnotation(TargetDataSource.class);
    27                 DataSourceHolder.setDataSource(datasource.name());
    28             }
    29         }
    30 
    31     }
    32 } 

      6、配置文件

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:aop="http://www.springframework.org/schema/aop"
     5     xmlns:context="http://www.springframework.org/schema/context"
     6     xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
     7     xmlns:tx="http://www.springframework.org/schema/tx"
     8     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     9         http://www.springframework.org/schema/beans/spring-beans.xsd
    10         http://mybatis.org/schema/mybatis-spring 
    11         http://mybatis.org/schema/mybatis-spring.xsd
    12         http://www.springframework.org/schema/aop
    13         http://www.springframework.org/schema/aop/spring-aop.xsd
    14         http://www.springframework.org/schema/tx 
    15         http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
    16         http://www.springframework.org/schema/context 
    17         http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    18 
    19     <!-- 引入数据库的配置文件 -->
    20     <context:property-placeholder location="classpath:dbconfig.properties" />
    21     
    22  
    23     <bean id="dataSource1" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    24         <property name="jdbcUrl" value="${datasource1.jdbc.url}"></property>
    25         <property name="driverClass" value="${datasource1.jdbc.driver}"></property>
    26         <property name="user" value="${datasource1.jdbc.username}"></property>
    27         <property name="password" value="${datasource1.jdbc.password}"></property>
    28     </bean>
    29     
    30     <bean id="dataSource2" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    31         <property name="jdbcUrl" value="${datasource2.jdbc.url}"></property>
    32         <property name="driverClass" value="${datasource2.jdbc.driver}"></property>
    33         <property name="user" value="${datasource2.jdbc.username}"></property>
    34         <property name="password" value="${datasource2.jdbc.password}"></property>
    35     </bean>
    36     
    37  
    38     
    39     <!-- 数据源:Spring用来控制业务逻辑。数据源、事务控制、aop -->
    40     <bean id="dataSource" class="com.test.datasource.DynamicDataSource">
    41         <property name="targetDataSources">
    42             <map key-type="java.lang.String">
    43                 <entry key="dataSource1" value-ref="dataSource1"></entry>
    44                 <entry key="dataSource2" value-ref="dataSource2"></entry>
    45             </map>
    46         </property>
    47         <!-- 默认目标数据源为你主库数据源 -->
    48         <property name="defaultTargetDataSource" ref="dataSource1"/>
    49     </bean>
    50   
    51     
    52     <!-- spring事务管理 -->
    53     <bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    54         <property name="dataSource" ref="dataSource"></property>
    55     </bean>
    56 
    57     <!-- 开启基于注解的事务 -->
    58     <tx:annotation-driven transaction-manager="dataSourceTransactionManager" order="2"/>
    59     
    60     <!-- 
    61     整合mybatis 
    62         目的:1、spring管理所有组件。mapper的实现类。
    63                 service==>Dao   @Autowired:自动注入mapper;
    64             2、spring用来管理事务,spring声明式事务
    65     -->
    66     <!--创建出SqlSessionFactory对象  -->
    67     <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
    68         <property name="dataSource" ref="dataSource"></property>
    69         <!-- configLocation指定全局配置文件的位置 -->
    70         <property name="configLocation" value="classpath:mybatis-config.xml"></property>
    71         <!--mapperLocations: 指定mapper文件的位置-->
    72         <property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
    73     </bean>
    74     
    75     <!--配置一个可以进行批量执行的sqlSession  -->
    76     <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    77         <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
    78         <constructor-arg name="executorType" value="BATCH"></constructor-arg>
    79     </bean>
    80     
    81     <!-- 扫描所有的mapper接口的实现,让这些mapper能够自动注入;
    82     base-package:指定mapper接口的包名
    83      -->
    84     <mybatis-spring:scan base-package="com.test.dao"/>
    85     
    86     
    87     <!-- 配置切面的Bean -->
    88     <bean id="dataSourceExchange" class="com.test.datasource.DataSourceExchange"/>
    89     
    90     
    91     <!-- 配置AOP -->
    92     <aop:config>
    93         <!-- 配置切点表达式  -->
    94         <aop:pointcut id="servicePointcut" expression="execution(* com.test.service.*.*(..))"/>
    95         <!-- 关键配置,切换数据源一定要比持久层代码更先执行(事务也算持久层代码) <aop:advisor advice-ref="txAdvice" pointcut-ref="service" order="2"/> -->
    96         <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="servicePointcut" order="1"/>
    97     </aop:config>
    98     
    99 </beans>

      注意:Spring中的事务是通过aop来实现的,当我们自己写aop拦截的时候,会遇到跟spring的事务aop执行的先后顺序问题,比如说动态切换数据源的问题,如果事务在前,数据源切换在后,会导致数据源切换失效,所以就用到了Order(排序)这个关键字

    1 <aop:advisor advice-ref="dataSourceExchange" pointcut-ref="servicePointcut" order="1"/>
    1 <!-- 开启基于注解的事务 -->
    2 <tx:annotation-driven transaction-manager="dataSourceTransactionManager" order="2"/>

      7、在service上加上注解即可使用

    1 @Transactional
    2 @TargetDataSource(name=TargetDataSource.SLAVE)
    3 public int addEmployeeFromSalve(Employee employee) {
    4     
    5     return employeeMapper.insert(employee);
    6 }

        

      

    数据流转顺序:

       1.xml<aop>拦截到数据源名称

       2.执行切面DataSourceExchange中的before方法,将数据源名称放入 DataSourceHolder中

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

      4.AbstractRoutingDataSource类中determineTargetDataSource()方法调用determineCurrentLookupKey()匹配到指定的数据库,并建立链接,即为切换到相应的数据库;

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

      

  • 相关阅读:
    无线网破解软件|一键式破解无线网|BT17软件包下载[笔记本+软件就行]
    Boost环境配置及遇到的问题解决方案
    HDU 4255 A Famous Grid
    uva 10306
    系统学习Linux的11点建议
    linux shell except tcl login ssh Automatic interaction
    常用网址记录
    am335x Qt SocketCAN Demo hacking
    a demo for how to use QThread
    OK335xS CAN device register and deiver match hacking
  • 原文地址:https://www.cnblogs.com/h--d/p/11304082.html
Copyright © 2011-2022 走看看