springboot+mybatis+aop配置动态数据源

废话不多说直接上代码

pom文件的引入:记得一定要用阿里的镜像哦,不然下载速度会让你崩溃

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.gbicc</groupId>
    <artifactId>demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.1.RELEASE</version>
        <relativePath />
    </parent>


    <properties>
        <mybatis-spring-boot>1.3.4</mybatis-spring-boot>
        <mysql-connector>5.1.39</mysql-connector>
        <java.version>1.8</java.version>
        <oracle.version>11.2.0.3</oracle.version>
        <druid.version>1.1.14</druid.version>
        <mybatis.spring.boot.starter.version>1.3.2</mybatis.spring.boot.starter.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <!-- SpringBoot Web容器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--阿里数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid.version}</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>



        <!-- MySQL 连接驱动依赖 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--oracle驱动-->
        <dependency>
            <groupId>com.oracle</groupId>
            <artifactId>ojdbc6</artifactId>
            <version>${oracle.version}</version>
        </dependency>

        <!-- SpringBoot Mybatis 依赖 -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot}</version>
        </dependency>

        <!--Spring框架基本的核心工具-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
        </dependency>

        <!-- lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- logback 依赖-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
        </dependency>

        <!--Slf4j 依赖-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
        </dependency>
    </dependencies>

    <!--阿里云镜像插件下载-->
    <pluginRepositories>
        <pluginRepository>
            <id>public</id>
            <name>aliyun nexus</name>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
            <releases>
                <enabled>true</enabled>
            </releases>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork> <!-- 如果没有该配置,devtools不会生效 -->
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

我的配置使用的yml文件,当然application.properties也是可以的

server:
  port: 80
spring:
  application:
    name: demo
  datasource:
    ## test1 database
    test1:
      jdbc-url: jdbc:oracle:thin:@127.0.0.1:1521:zwsj
      username: user_frame2
      password: user_frame2
      driverClassName: oracle.jdbc.OracleDriver
    test2:
      jdbc-url: jdbc:mysql://localhost:3306/ry-vue?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
      username: root
      password: root
      driver-class-name: com.mysql.jdbc.Driver
#mybatis配置
mybatis:
  # 搜索指定包别名
  typeAliasesPackage: com.gbicc.demo.**.domain
  # 配置mapper的扫描,找到所有的mapper.xml映射文件
  mapperLocations: classpath:mybatis/**/*Mapper.xml
  # 加载全局的配置文件
  configLocation: classpath:mybatis/mybatis-config.xml

多数据源配置动态加载

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

@Configuration
@MapperScan(basePackages = "com.gbicc.demo.mapper",sqlSessionFactoryRef = "SqlSessionFactory")
public class DataSourceConfig {


    //bean创建对象,交给spring管理
    @Bean(name = "test1DataSource")
    @Primary//指定那个为主数据源
    @ConfigurationProperties(prefix = "spring.datasource.test1")//绑定配置文件中的属性到bean中
    public DataSource getDateSource1(){
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "test2DataSource")
    @ConfigurationProperties(prefix = "spring.datasource.test2")//读取的数据源前缀, 跟yml文件对应
    public DataSource getDateSource2(){
        return DataSourceBuilder.create().build();
    }


    @Bean(name = "dynamicDataSource")
    public DynamicDataSource DataSource(@Qualifier("test1DataSource")DataSource test1datasource,
                        @Qualifier("test2DataSource")DataSource test2datasource ){
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataBaseType.TEST01,test1datasource);
        targetDataSources.put(DataBaseType.TEST02,test2datasource);
        DynamicDataSource dataSource=new DynamicDataSource();
        dataSource.setTargetDataSources(targetDataSources);
        dataSource.setDefaultTargetDataSource(test1datasource);
        return dataSource;
    }

    @Bean(name = "SqlSessionFactory")
    public SqlSessionFactory test1SqlSessionFactory(@Qualifier("dynamicDataSource") DataSource dynamicDataSource)
            throws Exception{
        SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
        bean.setDataSource(dynamicDataSource);
        bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis/**/*Mapper.xml"));
        return bean.getObject();
    }
接下来配置一个最重要的一个类,
继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法,AbstractRoutingDataSource 类中determineCurrentLookupKey方法的key为数据源的名称,value为动态的数据源,determineCurrentLookupKey该类中还有一个非常重要的属性:private Map<Object, Object> targetDataSources;这个属性是会将你的数据源对象接受,然后设置给datasource类。
package com.gbicc.demo.config;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;


import javax.sql.DataSource;
import java.util.Map;

/**\
 * 定义一个动态数据源:
 * 继承AbstractRoutingDataSource 抽象类,并重写determineCurrentLookupKey()方法
 */
public class DynamicDataSource extends AbstractRoutingDataSource  {

    @Override
    protected Object determineCurrentLookupKey() {
        DataBaseType dataBaseType = DataSourceType.getDataBaseType();
        return dataBaseType;
    }
}

创建DataSourceType类,当然更多人喜欢用DynamicDataSourceContextHolder进行命名,这里我用了DataSourceType,名称随意,对程序的运行没有影响的。该类的作用就是创建一个安全的线程,然后将数据源放置进去。



public class DataSourceType {
    // 使用ThreadLocal保证线程安全
    private static final ThreadLocal<DataBaseType> TYPE = new ThreadLocal<DataBaseType>();

    // 往当前线程里设置数据源类型
    public static void setDataBaseType(DataBaseType dataBaseType) {
        if (dataBaseType == null) {
            throw new NullPointerException();
        }
        System.err.println("[将当前数据源改为]:" + dataBaseType);
        TYPE.set(dataBaseType);
    }

    // 获取数据源类型
    public static DataBaseType getDataBaseType() {
        DataBaseType dataBaseType = TYPE.get() == null ? DataBaseType.TEST01 : TYPE.get();
        System.err.println("[获取当前数据源的类型为]:" + dataBaseType);
        return dataBaseType;
    }

    // 清空数据类型
    public static void clearDataBaseType() {
        TYPE.remove();
    }
}

创建一个枚举类。名称必须要和yml配置文件中的名字保持一直,否则无法加载

package com.gbicc.demo.config;

public enum DataBaseType {
    TEST01, TEST02
}

ok,到此多数据就配置完成了,接下来要配置AOP进行扫描注解。

DataSource这个注解吧,我是这么写的,好几种配置方式。我给大家举例三种方式吧,看你的具体场景使用,然后具体的使用。

第一种配置方式:

package com.gbicc.demo.config;

import java.lang.annotation.*;

/**
 * 切换数据注解 可以用于类或者方法级别 方法级别优先级 > 类级别
 */
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    public DataBaseType value() default DataBaseType.TEST01; //该值即key值,默认使用默认数据库
}

配置AOP拦截,不然注解无法使用。

package com.gbicc.demo.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DynamicDataSourceAspect {
    @Before("@annotation(dataSource)")//拦截我们的注解
    public void changeDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
        DataBaseType value = dataSource.value();
        if (value.equals(DataBaseType.TEST01)){
            DataSourceType.setDataBaseType(DataBaseType.TEST01);
        }else if (value.equals(DataBaseType.TEST02)){
            DataSourceType.setDataBaseType(DataBaseType.TEST02);
        }else {
            DataSourceType.setDataBaseType(DataBaseType.TEST01);//默认使用主数据库
        }

    }

    @After("@annotation(dataSource)") //清除数据源的配置
    public void restoreDataSource(JoinPoint point, DataSource dataSource) {
        DataSourceType.clearDataBaseType();


    }
}

OK,这样就算配置完成了。

使用的时候这么用就行,这个注解可以作用在类中,也是以作用再方法中。

@DataSource(value = DataBaseType.TEST02)   @DataSource(value = DataBaseType.数据库名称)
  @DataSource(value = DataBaseType.TEST02)
    public Integer test02hello(){
        return demoMapper.hello();
    }
    @DataSource(value = DataBaseType.TEST01)
    public Integer test01hello(){
        return demoMapper.hello();
    }

这是我的mapper层的代码:我的mysql和oracle都有一张sys_user表。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.gbicc.demo.mapper.DemoMapper">
    <select id="hello" resultType="Integer">
        SELECT COUNT(*) FROM sys_user
    </select>
</mapper>

第二种配置方式就比较简单粗暴了。

直接上代码:

package com.gbicc.demo.config;

import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;


@Aspect
@Component
public class DataSourceAop {
    @Before("execution(* com.gbicc.demo.service.Impl..*.test01*(..))")
    public void setDataSource2test01() {
        System.err.println("test01业务");
        DataSourceType.setDataBaseType(DataSourceType.DataBaseType.TEST01);
    }
    @Before("execution(* com.gbicc.demo.service.Impl..*.test02*(..))")
    public void setDataSource2test02() {
        System.err.println("test02业务");
        DataSourceType.setDataBaseType(DataSourceType.DataBaseType.TEST02);
    }
}

直接AOP扫描包,不再使用自定义注解,当然这个时候记得将DynamicDataSourceAspect删除了。这个时候把,切换数据源就要写代码了,在你的方法需要切换数据源的地方要这么写:

public Integer test02hello(){
   DataSourceType.setDataBaseType(DataBaseType.TEST01) 
return demoMapper.hello(); 
}

我的上面AOP的扫描注解包是直接扫面test02开头的方法查询的从表数据库,test01开头的查询的主表库。

16:51:29.503 [main] INFO  o.a.c.h.Http11NioProtocol - [log,173] - Starting ProtocolHandler ["http-nio-80"]
16:51:29.510 [main] INFO  o.a.t.u.n.NioSelectorPool - [log,173] - Using a shared selector for servlet write/read
16:51:29.525 [main] INFO  c.g.DemoApplication - [logStarted,59] - Started DemoApplication in 2.046 seconds (JVM running for 3.777)
16:51:34.978 [http-nio-80-exec-1] INFO  o.a.c.c.C.[.[.[/] - [log,173] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[将当前数据源改为]:TEST01
[获取当前数据源的类型为]:TEST01
16:51:35.022 [http-nio-80-exec-1] INFO  c.z.h.HikariDataSource - [getConnection,110] - HikariPool-1 - Starting...
16:51:35.102 [http-nio-80-exec-1] INFO  c.z.h.pool.PoolBase - [getAndSetNetworkTimeout,525] - HikariPool-1 - Driver does not support get/set network timeout for connections. (oracle.jdbc.driver.T4CConnection.getNetworkTimeout()I)
16:51:35.106 [http-nio-80-exec-1] INFO  c.z.h.HikariDataSource - [getConnection,123] - HikariPool-1 - Start completed.
[将当前数据源改为]:TEST02
1数据源当前用户数为:29
[获取当前数据源的类型为]:TEST02
16:51:35.192 [http-nio-80-exec-1] INFO  c.z.h.HikariDataSource - [getConnection,110] - HikariPool-2 - Starting...
16:51:35.193 [http-nio-80-exec-1] WARN  c.z.h.u.DriverDataSource - [<init>,68] - Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation.
16:51:35.840 [http-nio-80-exec-1] INFO  c.z.h.HikariDataSource - [getConnection,123] - HikariPool-2 - Start completed.
2数据源当前用户数为:2
3数据源当前用户数为:29
[获取当前数据源的类型为]:TEST01

这是我的程序运行的结果。

第三种配置:DataSource类的代码换了换方式而已:

/**
 * 切换数据注解 可以用于类或者方法级别 方法级别优先级 > 类级别
 */
@Target({ElementType.METHOD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "test01"; //该值即key值,默认使用默认数据库
}

当然这个类的判断也要修改一下哦:DynamicDataSourceAspect

package com.gbicc.demo.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class DynamicDataSourceAspect {
    @Before("@annotation(dataSource)")//拦截我们的注解
    public void changeDataSource(JoinPoint point, DataSource dataSource) throws Throwable {
        String value = dataSource.value();
        if (value.equals("test01")){
            DataSourceType.setDataBaseType(DataBaseType.TEST01);
        }else if (value.equals("test02")){
            DataSourceType.setDataBaseType(DataBaseType.TEST02);
        }else {
            DataSourceType.setDataBaseType(DataBaseType.TEST01);//默认使用主数据库
        }

    }

    @After("@annotation(dataSource)") //清除数据源的配置
    public void restoreDataSource(JoinPoint point, DataSource dataSource) {
        DataSourceType.clearDataBaseType();


    }
}

如有什么疑问欢迎加入qq群来咨询我哦:35142661

github地址:https://github.com/iuoooo/datasource.git

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值