zoukankan      html  css  js  c++  java
  • 实现Mybatis接口模式下的数据库调用分离

    目标:

    多项目,多数据库,多连接池,程序级动态切换数据库调用

    环境基础:

             框架:SPRING+MYBATIS+MYSQL/ORACLE

    设想:

    Mapper分包处理不同的库

    BaseDao分包处理不同的库

    BaseService分包处理不同的库

    实现:

    多个数据源管理结构:

     配置文件:

     1 <!-- MyBatis begin && Alias-->
     2     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
     3         <property name="dataSource" ref="multipleDataSource"/>
     4         <property name="typeAliasesPackage" value="com.thinkgem.jeesite,com.qysxy.product"/>
     5         <!--com.thinkgem.jeesite.common.persistence.BaseEntity-->
     6         <property name="typeAliasesSuperType" value="com.thinkgem.jeesite.common.persistence.SuperEntity"/>
     7         <property name="mapperLocations" value="classpath:/mappings/**/*.xml"/>
     8         <property name="configLocation" value="classpath:/mybatis-config.xml"></property>
     9     </bean>
    10 
    11     <!--多数据源管理配置-->
    12     <bean id="multipleDataSource" class="com.qysxy.framework.spring.support.MultipleDataSource">
    13         <property name="defaultTargetDataSource" ref="dataSource"/>
    14         <property name="targetDataSources">
    15             <map>
    16                 <entry key="MYSQL-PROJECT1" value-ref="dataSource"/>
    17                 <entry key="MYSQL-PROJECT2" value-ref="dataSource-project2"/>
    18             </map>
    19         </property>
    20     </bean>
    21 
    22 
    23     <!-- 数据源配置, 使用 BoneCP 数据库连接池 -->
    24     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> 
    25         <!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
    26         <property name="driverClassName" value="${jdbc.driver}" />
    27         
    28         <!-- 基本属性 url、user、password -->
    29         <property name="url" value="${jdbc.url}" />
    30         <property name="username" value="${jdbc.username}" />
    31         <property name="password" value="${jdbc.password}" />
    32         
    33         <!-- 配置初始化大小、最小、最大 -->
    34         <property name="initialSize" value="${jdbc.pool.init}" />
    35         <property name="minIdle" value="${jdbc.pool.minIdle}" /> 
    36         <property name="maxActive" value="${jdbc.pool.maxActive}" />
    37         
    38         <!-- 配置获取连接等待超时的时间 -->
    39         <property name="maxWait" value="60000" />
    40         
    41         <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    42         <property name="timeBetweenEvictionRunsMillis" value="60000" />
    43         
    44         <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    45         <property name="minEvictableIdleTimeMillis" value="300000" />
    46         
    47         <property name="validationQuery" value="${jdbc.testSql}" />
    48         <property name="testWhileIdle" value="true" />
    49         <property name="testOnBorrow" value="false" />
    50         <property name="testOnReturn" value="false" />
    51         
    52         <!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用)
    53         <property name="poolPreparedStatements" value="true" />
    54         <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> -->
    55         
    56         <!-- 配置监控统计拦截的filters -->
    57         <property name="filters" value="stat" /> 
    58     </bean>
    59 
    60     <!-- 数据源配置, 使用 BoneCP 数据库连接池,针对平台插拔系统2 -->
    61     <bean id="dataSource-project2" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
    62         <!-- 数据源驱动类可不写,Druid默认会自动根据URL识别DriverClass -->
    63         <property name="driverClassName" value="${jdbc.driver}" />
    64 
    65         <!-- 基本属性 url、user、password -->
    66         <property name="url" value="${jdbc.url.project2}" />
    67         <property name="username" value="${jdbc.username}" />
    68         <property name="password" value="${jdbc.password}" />
    69 
    70         <!-- 配置初始化大小、最小、最大 -->
    71         <property name="initialSize" value="${jdbc.pool.init}" />
    72         <property name="minIdle" value="${jdbc.pool.minIdle}" />
    73         <property name="maxActive" value="${jdbc.pool.maxActive}" />
    74 
    75         <!-- 配置获取连接等待超时的时间 -->
    76         <property name="maxWait" value="60000" />
    77 
    78         <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
    79         <property name="timeBetweenEvictionRunsMillis" value="60000" />
    80 
    81         <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
    82         <property name="minEvictableIdleTimeMillis" value="300000" />
    83 
    84         <property name="validationQuery" value="${jdbc.testSql}" />
    85         <property name="testWhileIdle" value="true" />
    86         <property name="testOnBorrow" value="false" />
    87         <property name="testOnReturn" value="false" />
    88 
    89         <!-- 打开PSCache,并且指定每个连接上PSCache的大小(Oracle使用)
    90         <property name="poolPreparedStatements" value="true" />
    91         <property name="maxPoolPreparedStatementPerConnectionSize" value="20" /> -->
    92 
    93         <!-- 配置监控统计拦截的filters -->
    94         <property name="filters" value="stat" />
    95     </bean>
    96 
    97 <!--切面注解驱动-->
    98     <aop:aspectj-autoproxy proxy-target-class="false"/>
     1 import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
     2 
     3 /**
     4  * @author fuguangli
     5  * @description 数据库选择路由
     6  * @Create date:    2017/2/24
     7  */
     8 public class MultipleDataSource extends AbstractRoutingDataSource {
     9     @Override
    10     protected Object determineCurrentLookupKey() {
    11         return DatabaseContextHolder.getDataBaseType();
    12     }
    13 }
     1 public class DatabaseContextHolder {
     2     public static String DATABASE_MYSQL_PROJECT1 = "MYSQL-PROJECT1";//默认
     3     public static String DATABASE_MYSQL_PROJECT2 = "MYSQL-PROJECT2";
     4 
     5     private static final ThreadLocal<String> contextHolder = new ThreadLocal();
     6 
     7     public static void setDataBaseType(String context) {
     8         contextHolder.set(context);
     9     }
    10 
    11     public static String getDataBaseType() {
    12         if (contextHolder.get() == null) {
    13             return DATABASE_MYSQL_PROJECT1;
    14         }
    15 
    16         return contextHolder.get();
    17     }
    18 
    19     public static void clearDatabaseType() {
    20         contextHolder.remove();
    21     }
    22 
    23 }
    1 @Test
    2     public void changeDataSource() {
    3         Long time  = System.currentTimeMillis();
    4         System.out.println("start ="+time);
    5         DatabaseContextHolder.setDataBaseType(DatabaseContextHolder.DATABASE_MYSQL_PROJECT2);
    6         List<Object> list = testService.findBySql("select * from jee_user");
    7         System.out.println("end ="+(System.currentTimeMillis()-time));//185-126
    8     }

    此时,可以进行切换数据库,但是还没有做到真正的程序自动切换,此时利用spring的AOP进行分包检测,自动切换数据源。

     

    原理:

    目前,spring AOP的使用条件:

    1、代理目标必须是实现类

    2、代理的方法是实现的接口的方法

    3、程序需要调用实现的接口方法

    4、代理的类的所有上层接口不能有过代理

     

    还有,在本次框架中需要注意的是,mybatis采用的接口调用,所以mybatis框架对DAO接口已经进行了一次java反射代理,所以,为了动态的切换数据源,需要重新写一个接口和实现类调用DAO方法,然后对此类进行代理。

     

    此时有两种方法实现代理:

    一、代理mybatis的DAO接口

    基本实现就是,你需要写一个接口和实现类去封装mybatis的调用接口,很像是我们经常做的service层。

     1 import com.qysxy.framework.spring.support.DatabaseContextHolder;
     2 import org.aspectj.lang.JoinPoint;
     3 import org.aspectj.lang.annotation.*;
     4 import org.springframework.stereotype.Component;
     5 
     6 import java.lang.annotation.Annotation;
     7 
     8 /**
     9  * @author fuguangli
    10  * @description 切面通知类
    11  * @Create date:    2017/2/24
    12  */
    13 @Component("dataSourceAdvice ")
    14 @Aspect
    15 public class DataSourceAdvice{
    16 
    17     @Pointcut("execution(*com.thinkgem.jeesite.modules.test.service.*Service.*(..))")
    18     private   void pointcut(){}
    19 
    20 //    @Pointcut("@annotation(daoAOP)")
    21 //    private   void pointcut(DaoAOP daoAOP){}
    22 
    23     /**
    24      * 在调用方法执行前执行,不能阻止方法的调用。
    25      * @param joinPoint
    26      */
    27     @Before("pointcut()")
    28     public void doBefore(JoinPoint joinPoint) {
    29        DatabaseContextHolder.setDataBaseType(DatabaseContextHolder.DATABASE_MYSQL_PROJECT2);
    30 
    31         System.out.println("--------调用数据库之前切换设置数据源--------");
    32     }
    33 }

    二、自定义注解,实现切面

    其实原理和上面是一样的,也是需要写一层去封装mybatis调用接口,然后做切面配置的时候,加上自定义注解。

     

    贴上自定义注解:

     1 import java.lang.annotation.*;
     2 
     3 /**
     4  * @author fuguangli
     5  * @description 切面注解
     6  * @Create date:    2017/2/27
     7  */
     8 @Target(ElementType.METHOD)
     9 @Retention(RetentionPolicy.RUNTIME)
    10 @Documented
    11 public @interface DaoAOP {
    12     String value() default "";
    13 }
  • 相关阅读:
    痞子衡嵌入式:恩智浦i.MX RTxxx系列MCU特性那些事(1)- 概览
    痞子衡嵌入式:16MB以上NOR Flash使用不当可能会造成软复位后i.MXRT无法正常启动
    《痞子衡嵌入式半月刊》 第 12 期
    不能错过的分布式ID生成器(Leaf ),好用的一批!
    实用!一键生成数据库文档,堪称数据库界的Swagger
    安排上了!PC人脸识别登录,出乎意料的简单
    又被逼着优化代码,这次我干掉了出入参 Log日志
    图文并茂,带你认识 JVM 运行时数据区
    一文说通C#中的异步编程补遗
    一文说通C#中的异步编程
  • 原文地址:https://www.cnblogs.com/yuan951/p/6475259.html
Copyright © 2011-2022 走看看