Two ways for Spring Boot to integrate Druid to realize multiple data sources

Table of contents

Project scenario:

1. How to integrate com.baomidou

1. maven dependency:

2. Configuration file:

3. Use annotations to switch data sources: 

2. Manually realize the original way of multiple data sources based on AOP

1. maven dependency:

2. Configuration file:

3. Multiple data source name classes

4. Custom annotations 

5. Configuration class

6. Dynamic data source configuration

 7. AOP aspect interception annotation

8. The Dao layer is the same as normal

 9. Service layer, plus multi-data source annotations

 10. Controller test

3. Results display


Project scenario:

Spring Boot集成Druid实现多数据源的两种方式:

1. Integrate com.baomidou and introduce dynamic-datasource dependency;

2. The original method, based on AOP, manually implements multiple data sources;


1. How to integrate com.baomidou

1、maven依赖:

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
    <version>3.5.1</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

2. Configuration file:

1. The spring.datasource.dynamic prefix is ​​fixed in the baomidou source code;

2.first and second are self-defined names and can be changed;

# 多数据源配置,默认master
spring.datasource.dynamic.primary = first
# 数据源1
spring.datasource.dynamic.datasource.first.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.first.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.dynamic.datasource.first.url = jdbc:mysql://127.0.0.1:3306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.dynamic.datasource.first.username = test
spring.datasource.dynamic.datasource.first.password = test1
# 数据源2
spring.datasource.dynamic.datasource.second.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.dynamic.datasource.second.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.dynamic.datasource.second.url = jdbc:mysql://127.0.0.1:18306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.dynamic.datasource.second.username = test
spring.datasource.dynamic.datasource.second.password = test1

# druid多数据源全局配置
spring.datasource.dynamic.druid.filter.stat.log-slow-sql = true
spring.datasource.dynamic.druid.filter.stat.slow-sql-millis = 1000
spring.datasource.dynamic.druid.min-evictable-idle-time-millis = 300000
spring.datasource.dynamic.druid.test-on-borrow = false
spring.datasource.dynamic.druid.filter.stat.merge-sql = false
spring.datasource.dynamic.druid.test-on-return = false
spring.datasource.dynamic.druid.initial-size = 10
spring.datasource.dynamic.druid.min-idle = 10
spring.datasource.dynamic.druid.max-wait = 60000
spring.datasource.dynamic.druid.pool-prepared-statements = true
spring.datasource.dynamic.druid.test-while-idle = true
spring.datasource.dynamic.druid.validation-query = select 1
spring.datasource.dynamic.druid.filter.wall.config.multi-statement-allow = true
spring.datasource.dynamic.druid.time-between-eviction-runs-millis = 60000
spring.datasource.dynamic.druid.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.dynamic.druid.max-active = 100
# druid监控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*

Verify that the configuration file is in effect: Spring Boot integrates Druid to check whether the configuration is in effect 

3. Use annotations to switch data sources: 

@DS annotation description:

1. Annotations are on methods, classes, interfaces, and enumerations. At the same time, there is a principle of proximity, and annotations on methods take precedence over annotations on classes;

2. Do not use the @DS annotation, the default primary data source;

class:

@DS("db2")
public class test(){}

 method:

@DS("db2")
public void test(){}

on the mapper method

@Select("SELECT * FROM TEST WHERE A = #{A}")
@DS("db2")
Map<String, Object> test(@Param("A") String A);

Multiple data sources may encounter transaction problems:
 the use of Spring Boot multi-data source transactions @DSTransactional


2. Manually realize the original way of multiple data sources based on AOP

1、maven依赖:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid-spring-boot-starter</artifactId>
    <version>1.1.10</version>
</dependency>

2. Configuration file:

1. Spring Boot 2.X version no longer supports configuration inheritance. If there are multiple data sources, all configurations of each data source need to be configured separately, otherwise the configuration will not take effect;

2. first and second are self-defined names, which correspond to the DynamicDataSourceConfig configuration class and can be changed;

# 多数据源配置
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
# 数据源1
spring.datasource.druid.first.name = first
spring.datasource.druid.first.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.druid.first.url = jdbc:mysql://127.0.0.1:3306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.first.username = test
spring.datasource.druid.first.password = test1
# 数据源2
spring.datasource.druid.second.name = second
spring.datasource.druid.second.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.druid.second.url = jdbc:mysql://127.0.0.1:18306/mp_dm?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.druid.second.username = test
spring.datasource.druid.second.password = test1

# druid数据源1
spring.datasource.druid.first.filter.stat.log-slow-sql = true
spring.datasource.druid.first.filter.stat.slow-sql-millis = 1000
spring.datasource.druid.first.min-evictable-idle-time-millis = 300000
spring.datasource.druid.test-on-borrow = false
spring.datasource.druid.first.filter.stat.merge-sql = false
spring.datasource.druid.test-on-return = false
spring.datasource.druid.first.initial-size = 10
spring.datasource.druid.first.min-idle = 10
spring.datasource.druid.first.max-wait = 60000
spring.datasource.druid.first.pool-prepared-statements = true
spring.datasource.druid.first.test-while-idle = true
spring.datasource.druid.first.validation-query = select 1
spring.datasource.druid.first.filter.wall.config.multi-statement-allow = true
spring.datasource.druid.first.time-between-eviction-runs-millis = 60000
spring.datasource.druid.first.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.druid.first.max-active = 100
# druid数据源2
spring.datasource.druid.second.filter.stat.log-slow-sql = true
spring.datasource.druid.second.filter.stat.slow-sql-millis = 1000
spring.datasource.druid.second.min-evictable-idle-time-millis = 300000
spring.datasource.druid.second.test-on-borrow = false
spring.datasource.druid.second.filter.stat.merge-sql = false
spring.datasource.druid.second.test-on-return = false
spring.datasource.druid.second.initial-size = 10
spring.datasource.druid.second.min-idle = 10
spring.datasource.druid.second.max-wait = 60000
spring.datasource.druid.second.pool-prepared-statements = true
spring.datasource.druid.second.test-while-idle = true
spring.datasource.druid.second.validation-query = select 1
spring.datasource.druid.second.filter.wall.config.multi-statement-allow = true
spring.datasource.druid.second.time-between-eviction-runs-millis = 60000
spring.datasource.druid.second.max-pool-prepared-statement-per-connection-size = 20
spring.datasource.druid.second.max-active = 100
# druid监控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*

Verify that the configuration file is in effect: Spring Boot integrates Druid to check whether the configuration is in effect 

3. Multiple data source name classes

package com.test.datasources;

/**
 * 增加多数据源,在此配置
 */
public interface DataSourceNames {
    String FIRST = "first";
    String SECOND = "second";

} 

4. Custom annotations 

package com.test.datasources.annotation;

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

/**
 * 多数据源注解
 */
//同时支持方法注解和类注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
    String value() default "";
}

5. Configuration class

package com.test.datasources;

import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

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.core.JdbcTemplate;

import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;

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

    @Bean
    @ConfigurationProperties("spring.datasource.druid.first")
    public DataSource firstDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @ConfigurationProperties("spring.datasource.druid.second")
    public DataSource secondDataSource(){
        return DruidDataSourceBuilder.create().build();
    }

    @Bean
    @Primary
    public DynamicDataSource dataSource(DataSource firstDataSource, DataSource secondDataSource) {
        Map<Object, Object> targetDataSources = new HashMap<>();
        targetDataSources.put(DataSourceNames.FIRST, firstDataSource);
        targetDataSources.put(DataSourceNames.SECOND, secondDataSource);
        return new DynamicDataSource(firstDataSource, targetDataSources);
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate() {
     return new JdbcTemplate(firstDataSource());
    }
}

6. Dynamic data source configuration

package com.test.datasources;

import java.util.Map;

import javax.sql.DataSource;

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

/**
 * 动态数据源,关键代码
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
    private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();

    public DynamicDataSource(DataSource defaultTargetDataSource, Map<Object, Object> targetDataSources) {
        super.setDefaultTargetDataSource(defaultTargetDataSource);
        super.setTargetDataSources(targetDataSources);
        super.afterPropertiesSet();
    }

    @Override
    protected Object determineCurrentLookupKey() {
        return getDataSource();
    }

    public static void setDataSource(String dataSource) {
        contextHolder.set(dataSource);
    }

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

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

}

 7. AOP aspect interception annotation

package com.test.datasources.aspect;

import java.lang.reflect.Method;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;

import com.test.datasources.DataSourceNames;
import com.test.datasources.DynamicDataSource;
import com.test.datasources.annotation.DataSource;

/**
 * 多数据源,切面处理类
 */
@Aspect
@Component
public class DataSourceAspect implements Ordered {
    protected Logger logger = LoggerFactory.getLogger(getClass());

    /*
     * @annotation匹配指定注解的方法
     * @within匹配指定注解的类
     * 注意:这里只拦截所注解的类,如果调用的是父类的方法,那么不会拦截,除非父类方法在子类中被覆盖。
     */
    @Pointcut("@annotation(com.test.datasources.annotation.DataSource) || @within(com.test.datasources.annotation.DataSource)")
    public void dataSourcePointCut() {

    }

    @Around("dataSourcePointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        Class<?> dataClass = Class.forName(signature.getDeclaringTypeName());
        
        DataSource dsMethod = method.getAnnotation(DataSource.class);
        DataSource dsClass = dataClass.getAnnotation(DataSource.class);
        if(dsMethod != null){
        	//方法优先,如果方法上存在注解,则优先使用方法上的注解
        	DynamicDataSource.setDataSource(dsMethod.value());
            logger.debug("set datasource is " + dsMethod.value());
        }else if(dsClass != null){
            //其次类优先,如果类上存在注解,则使用类上的注解
        	DynamicDataSource.setDataSource(dsClass.value());
            logger.debug("set datasource is " + dsClass.value());
        }else{
        	//如果都不存在,则使用默认
        	DynamicDataSource.setDataSource(DataSourceNames.FIRST);
            logger.debug("set datasource is " + DataSourceNames.FIRST);
        }

        try {
            return point.proceed();
        } finally {
            DynamicDataSource.clearDataSource();
            logger.debug("clean datasource");
        }
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

8. The Dao layer is the same as normal

package com.test.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import com.test.entity.Test1Entity;

@Mapper
public interface Test1Dao {
	
	@Select("select * from test1")
	Test1Entity getById(@Param("id")Integer id);
}
package com.test.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;

import com.test.entity.Test2Entity;

@Mapper
public interface Test2Dao {
	@Select("select * from test2")
	Test2Entity getById(@Param("id")Integer id);
}

 9. Service layer, plus multi-data source annotations

package com.test.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.test.datasources.DataSourceNames;
import com.test.datasources.annotation.DataSource;
import com.test.entity.Test1Entity;
import com.test.mapper.Test1Dao;

@Service
@DataSource(DataSourceNames.FIRST)
public class Test1Service {

	@Autowired
	private Test1Dao test1Dao;
	
	public Test1Entity getById(int id) {
		return test1Dao.getById(id);
	}
}
package com.test.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.test.datasources.DataSourceNames;
import com.test.datasources.annotation.DataSource;
import com.test.entity.Test2Entity;
import com.test.mapper.Test2Dao;

@Service
@DataSource(DataSourceNames.SECOND)
public class Test2Service  {

	@Autowired
	private Test2Dao test2Dao;
	
	public Test2Entity getById(int id) {
		return test2Dao.getById(id);
	}
}

 10. Controller test

package com.test.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.test.entity.Test1Entity;
import com.test.entity.Test2Entity;
import com.test.service.Test1Service;
import com.test.service.Test2Service;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

/**
 * 动态数据源测试
 */
@Api(tags = "动态数据源测试")
@RestController
@RequestMapping("/test")
public class DynamicController {

	@Autowired
	private Test1Service test1Service;
	@Autowired
	private Test2Service test2Service;
	
	@ApiOperation(value = "数据源1")
	@RequestMapping(value = "/test1", method = RequestMethod.GET)
	public String test1() {
		Test1Entity entity = test1Service.getById(1);
		return entity.getName();
	}
	
	@ApiOperation(value = "数据源2")
	@RequestMapping(value = "/test2", method = RequestMethod.GET)
	public String test2() {
		Test2Entity entity = test2Service.getById(2);
		return entity.getName();
	}
}

Multiple data sources may encounter transaction problems: https://blog.csdn.net/u011974797/article/details/130154340

3. Results display

The log will be printed when the service starts:

com.alibaba.druid.pool.DruidDataSource   : {dataSource-1,first} inited
com.alibaba.druid.pool.DruidDataSource   : {dataSource-2,second} inited

druid monitoring page:

 

Source address: https://download.csdn.net/download/u011974797/87680942

Guess you like

Origin blog.csdn.net/u011974797/article/details/130109195
Recommended