springboot注解配置多数据源

在项目遇到多数据源的问题,主要是针对部分数据需要单独处理应用场景;

1.本次项目配置基于springboot+mybatis+maven集成的,首先在yml配置相关如下:其中需要注意单数据源是url,多数据源要改成 jdbc-url,否则会报异常(Error querying database.  Cause: java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName)

server:
  port: 8081

spring:
  datasource:
    database1:
      jdbc-url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowMultiQueries=true
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver
    database2:
      jdbc-url: jdbc:mysql://localhost:3306/test1?useUnicode=true&characterEncoding=utf8
      username: root
      password: 123456
      driver-class-name: com.mysql.jdbc.Driver

#mybatis config
mybatis:
  mapper-locations: classpath*:mapper/*/*.xml

  2.配置好了,需要在启动类先加上@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class})这个类位置:

import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
3.因为需要用到springboot的aop所以需要导包,在pom.xml文件导入
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

 4.多数据源的结构以下:

 

 内容直接给大家展示:

1.DataSourceAspect
package com.example.demo.config.db.dataSource;
 

import lombok.extern.slf4j.Slf4j;
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.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
 
import java.lang.reflect.Method;
 
/**
 * 多数据源配置, 拦截器配置
 * @author lxp
 * @create 2021-12-28 12:15
 **/
@Aspect
@Component
@Slf4j
// 优先级, 1标识最先执行
@Order(1)
public class DataSourceAspect {
    @Pointcut("execution(* com.example.demo.mapper..*.*(..)))")
    public void dataSourcePoint() {}
 
    @Before("dataSourcePoint()")
    public void before(JoinPoint joinPoint) {
        Object target = joinPoint.getTarget();
        MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
        // 执行方法名
        String methodName = methodSignature.getName();
        // 方法参数
        Class[] parameterTypes = methodSignature.getParameterTypes();
        try {
            // 获取方法, 直接getClass获取对象可能为代理对象
            Method method = target.getClass().getInterfaces()[0].getMethod(methodName, parameterTypes);
            // 添加默认数据源
            String dataSource = DataSourceType.Master.getName();
            if (null != method && method.isAnnotationPresent(MyDataSource.class)) {
                MyDataSource targetDataSource = method.getAnnotation(MyDataSource.class);
                dataSource = targetDataSource.value().getName();
            }
            // 此处添加线程对应的数据源到上下文
            // 在AbstractRoutingDataSource子类中拿到数据源, 加载后进行配置
            JdbcContextHolder.putDataSource(dataSource);
            log.info("generate data source : " + dataSource);
        } catch (Exception e) {
            log.info("error", e);
        }
 
    }
 
    /**
     * 清除数据源, 方法执行完成后, 清除数据源
     */
    @After("dataSourcePoint()")
    public void after(JoinPoint joinPoint) {
        JdbcContextHolder.clear();
    }
 
}

2.DataSourceConfig

package com.example.demo.config.db.dataSource;

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.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

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

/**
 * 数据源配置
 */
@Configuration
public class DataSourceConfig {

    @Bean(name =  "database1")
    @ConfigurationProperties(prefix = "spring.datasource.database1")
    public DataSource dataSource1() {
        //database1数据源
        return DataSourceBuilder.create().build();
    }

    @Bean(name = "database2")
    @ConfigurationProperties(prefix = "spring.datasource.database2")
    public DataSource dataSource2() {
        //database2数据源
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name="dynamicDataSource")
    @Primary    //优先使用,多数据源
    public DataSource dataSource() {
        DynamicDataSource dynamicDataSource = new DynamicDataSource();
        // 设置默认数据源为first数据源
        dynamicDataSource.setDefaultTargetDataSource(dataSource1());
        // 配置多数据源,
        // 添加数据源标识和DataSource引用到目标源映射
        Map<Object, Object> dataSourceMap = new HashMap<>();
        dataSourceMap.put(DataSourceType.Master.getName(), dataSource1());
        dataSourceMap.put(DataSourceType.Slave.getName(), dataSource2());
        dynamicDataSource.setTargetDataSources(dataSourceMap);
        return dynamicDataSource;

    }
    @Bean
    public PlatformTransactionManager transactionManager() {
        return new DataSourceTransactionManager(dataSource());
    }

}

3.DataSourceType

package com.example.demo.config.db.dataSource;

public enum DataSourceType {

    Master("database1"),
    Slave("database2");
    private String name;
    private DataSourceType(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

4.DynamicDataSource

package com.example.demo.config.db.dataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;


public class DynamicDataSource extends AbstractRoutingDataSource {

    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    @Override
    protected Object determineCurrentLookupKey() {
        logger.info("数据源为{}",JdbcContextHolder.getDataSource());
        return JdbcContextHolder.getDataSource();
    }
    
}
View Code

5.JdbcContextHolder

package com.example.demo.config.db.dataSource;
/**
 * 动态数据源的上下文 threadlocal
 */
public class JdbcContextHolder {
    
    private final static ThreadLocal<String> local = new ThreadLocal<>();
    
    public static void putDataSource(String name) {
        local.set(name);
    }
    
    public static String getDataSource() {
        return local.get();
    }
    /**
     * 清除数据源
     */
    public static void clear() {
        local.remove();
    }

}
View Code

6.MyDataSource

package com.example.demo.config.db.dataSource;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 数据源选择--自定义注解
 */
@Retention(RetentionPolicy.RUNTIME)  
@Target(ElementType.METHOD) 
public @interface MyDataSource {

    DataSourceType value();    //默认主表
    
}
View Code

最后应用就比较简单了,默认情况有默认的数据库源,需要调用别的就加注解即可

 

 

 

最后通过调用接口可以看到数据是不同数据库的数据,完整的过程基本都有,多数据源要把dao层和xml文件隔离开更好的区分!!!

 

posted @ 2022-01-21 15:20  执着的你  阅读(796)  评论(0编辑  收藏  举报