Spring Boot2.x 动态数据源配置

原文链接: Spring Boot2.x 动态数据源配置


基于 Spring Boot 2.x、Spring Data JPA、druid、mysql 的动态数据源配置Demo,适合用于数据库的读写分离等应用场景。通过在Service层方法上添加自定义注解实现读写不同的数据库。

配置文件已配置好druid监控相关属性,监控页面链接:ip:8080/druid。账号:admin,密码:123456。详情查看 application.yml 文件。

配置 pom.xml 文件

      <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.21</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

配置application.yml文件

spring:
  datasource:
    druid:
      primary:
        driverClassName: com.mysql.cj.jdbc.Driver
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/primary?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
        filters: stat,wall
      local:
        driver-class-name: com.mysql.cj.jdbc.Driver
        username: root
        password: root
        url: jdbc:mysql://localhost:3306/local?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8
        filters: stat,wall
      stat-view-servlet:
        enabled: true
        login-username: admin
        login-password: 123456
        reset-enable: false
        url-pattern: /druid/*
      web-stat-filter:
        enabled: true
        # 添加过滤规则
        url-pattern: /*
        # 忽略过滤格式
        exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
  jpa:
    database: MYSQL
    hibernate:
      show_sql: true
      format_sql: true
      primary-dialect: org.hibernate.dialect.MySQL5InnoDBDialect
      secondary-dialect: org.hibernate.dialect.MySQL5InnoDBDialect
      # 打开后会自动在主库生成表
      # ddl-auto: update
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    # 打开后会自动在主库生成表
    # generate-ddl: true

项目目录结构

目录结构

DataSource.java

package dynamic.data.annotation;

import dynamic.data.common.ContextConst;

import java.lang.annotation.*;
/**
 * @Author: ChangXuan
 * @Decription:
 * @Date: 22:25 2020/2/23
 **/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    ContextConst.DataSourceType value() default ContextConst.DataSourceType.PRIMARY;
}

DynamicDataSourceAspect.java

package dynamic.data.aspect;

import dynamic.data.common.ContextConst;
import dynamic.data.datasource.DataSourceContextHolder;
import dynamic.data.annotation.DataSource;
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.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
/**
 * @Author: ChangXuan
 * @Decription:
 * @Date: 22:28 2020/2/23
 **/
@Component
@Aspect
public class DynamicDataSourceAspect {
    @Before("execution(* dynamic.data.service..*.*(..))")
    public void before(JoinPoint point){
        try {
            DataSource annotationOfClass = point.getTarget().getClass().getAnnotation(DataSource.class);
            String methodName = point.getSignature().getName();
            Class[] parameterTypes = ((MethodSignature) point.getSignature()).getParameterTypes();
            Method method = point.getTarget().getClass().getMethod(methodName, parameterTypes);
            DataSource methodAnnotation = method.getAnnotation(DataSource.class);
            methodAnnotation = methodAnnotation == null ? annotationOfClass:methodAnnotation;
            ContextConst.DataSourceType dataSourceType = methodAnnotation != null && methodAnnotation.value() !=null ? methodAnnotation.value() :ContextConst.DataSourceType.PRIMARY ;
            DataSourceContextHolder.setDataSource(dataSourceType.name());
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    @After("execution(* dynamic.data.service..*.*(..))")
    public void after(JoinPoint point){
        DataSourceContextHolder.clearDataSource();
    }
}

ContextConst.java

package dynamic.data.common;

/**
 * @Author: ChangXuan
 * @Decription:
 * @Date: 22:17 2020/2/23
 **/
public interface ContextConst {
    enum DataSourceType{
        PRIMARY,LOCAL
    }
}

DataSourceContextHolder .java

package dynamic.data.datasource;

/**
 * @Author: ChangXuan
 * @Decription:
 * @Date: 22:23 2020/2/23
 **/
public class DataSourceContextHolder {

    private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>();

    public static void setDataSource(String dbType){
        System.out.println("切换到["+dbType+"]数据源");
        contextHolder.set(dbType);
    }

    public static String getDataSource(){
        return contextHolder.get();
    }

    public static void clearDataSource(){
        contextHolder.remove();
    }
}

DynamicDataSource.java

package dynamic.data.datasource;

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

/**
* @Author: ChangXuan
* @Decription:
* @Date: 22:22 2020/2/23
**/
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.getDataSource();
}
}

MutiplyDataSource.java

package dynamic.data.datasource;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import dynamic.data.common.ContextConst;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.util.HashMap;
/**
 * @Author: ChangXuan
 * @Decription:
 * @Date: 22:15 2020/2/23
 **/
@Configuration
public class MutiplyDataSource {
    @Bean(name = "dataSourcePrimary")
    @ConfigurationProperties(prefix = "spring.datasource.druid.primary")
    public DataSource primaryDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean(name = "dataSourceLocal")
    @ConfigurationProperties(prefix = "spring.datasource.druid.local")
    public DataSource localDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Primary
    @Bean(name = "dynamicDataSource")
    public DataSource dynamicDataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        //配置默认数据源
        dynamicDataSource.setDefaultTargetDataSource(primaryDataSource());

        //配置多数据源
        HashMap<Object, Object> dataSourceMap = new HashMap();
        dataSourceMap.put(ContextConst.DataSourceType.PRIMARY.name(),primaryDataSource());
        dataSourceMap.put(ContextConst.DataSourceType.LOCAL.name(),localDataSource());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;
    }

    /**
     * 配置@Transactional注解事务
     * @return
     */
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dynamicDataSource());
    }
}

使用

在 DynamicDataSourceAspect.java 中配置的service下使用注解的方式指定执行的方法使用哪个数据库。示例参考下方代码:

使用示例
primary数据库
local

猜你喜欢

转载自www.cnblogs.com/chxuan/p/12389075.html