目錄
項(xiàng)目場(chǎng)景:
一、集成com.baomidou的方式
1、maven依賴:
2、?配置文件:
3、?使用注解切換數(shù)據(jù)源:?
二、基于AOP手動(dòng)實(shí)現(xiàn)多數(shù)據(jù)源原生的方式
1、maven依賴:
2、?配置文件:
3、?多數(shù)據(jù)源名稱類
4、自定義注解?
5、配置類
6、動(dòng)態(tài)數(shù)據(jù)源配置
?7、AOP切面攔截注解
8、Dao層,跟正常一樣的
?9、service層,加上多數(shù)據(jù)源注解
?10、Controller測(cè)試
三、結(jié)果展示
項(xiàng)目場(chǎng)景:
Spring Boot集成Druid實(shí)現(xiàn)多數(shù)據(jù)源的兩種方式:
1、集成com.baomidou,引入dynamic-datasource依賴;
2、原生的方式,基于AOP手動(dòng)實(shí)現(xiàn)多數(shù)據(jù)源;
一、集成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、?配置文件:
1.spring.datasource.dynamic前綴,是baomidou源碼里固定的;
2.first、second是自定義的名稱,可以更改;
# 多數(shù)據(jù)源配置,默認(rèn)master
spring.datasource.dynamic.primary = first
# 數(shù)據(jù)源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
# 數(shù)據(jù)源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多數(shù)據(jù)源全局配置
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監(jiān)控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*
驗(yàn)證配置文件是否生效:Spring Boot集成Druid查看配置是否生效_濤哥是個(gè)大帥比的博客-CSDN博客?
3、?使用注解切換數(shù)據(jù)源:?
@DS注解說(shuō)明:
1.注解在方法上、類上、接口、枚舉,同時(shí)存在就近原則,方法上注解優(yōu)先于類上注解;
2.不使用@DS注解,默認(rèn)主數(shù)據(jù)源;
類上:
@DS("db2")
public class test(){}
?方法上:
@DS("db2")
public void test(){}
mapper方法上
@Select("SELECT * FROM TEST WHERE A = #{A}")
@DS("db2")
Map<String, Object> test(@Param("A") String A);
多數(shù)據(jù)源可能會(huì)碰到事務(wù)問(wèn)題:
?Spring Boot多數(shù)據(jù)源事務(wù)@DSTransactional的使用_濤哥是個(gè)大帥比的博客-CSDN博客
二、基于AOP手動(dòng)實(shí)現(xiàn)多數(shù)據(jù)源原生的方式
1、maven依賴:
<dependency>
?? ?<groupId>com.alibaba</groupId>
?? ?<artifactId>druid-spring-boot-starter</artifactId>
?? ?<version>1.1.10</version>
</dependency>
2、?配置文件:
1.Spring Boot 2.X 版本不再支持配置繼承,多數(shù)據(jù)源的話每個(gè)數(shù)據(jù)源的所有配置都需要單獨(dú)配置,否則配置不會(huì)生效;
2.first、second是自定義的名稱,對(duì)應(yīng)DynamicDataSourceConfig配置類,可以更改;
# 多數(shù)據(jù)源配置
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
# 數(shù)據(jù)源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
# 數(shù)據(jù)源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數(shù)據(jù)源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數(shù)據(jù)源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監(jiān)控全局配置
spring.datasource.druid.stat-view-servlet.enabled = true
spring.datasource.druid.stat-view-servlet.url-pattern = /druid/*
驗(yàn)證配置文件是否生效:Spring Boot集成Druid查看配置是否生效_濤哥是個(gè)大帥比的博客-CSDN博客?
3、?多數(shù)據(jù)源名稱類
package com.test.datasources;
/**
* 增加多數(shù)據(jù)源,在此配置
*/
public interface DataSourceNames {
String FIRST = "first";
String SECOND = "second";
}
4、自定義注解?
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;
/**
* 多數(shù)據(jù)源注解
*/
//同時(shí)支持方法注解和類注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String value() default "";
}
5、配置類
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;
/**
* 配置多數(shù)據(jù)源
*/
@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、動(dòng)態(tài)數(shù)據(jù)源配置
package com.test.datasources;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 動(dòng)態(tài)數(shù)據(jù)源,關(guān)鍵代碼
*/
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切面攔截注解
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;
/**
* 多數(shù)據(jù)源,切面處理類
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
/*
* @annotation匹配指定注解的方法
* @within匹配指定注解的類
* 注意:這里只攔截所注解的類,如果調(diào)用的是父類的方法,那么不會(huì)攔截,除非父類方法在子類中被覆蓋。
*/
@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){
//方法優(yōu)先,如果方法上存在注解,則優(yōu)先使用方法上的注解
DynamicDataSource.setDataSource(dsMethod.value());
logger.debug("set datasource is " + dsMethod.value());
}else if(dsClass != null){
//其次類優(yōu)先,如果類上存在注解,則使用類上的注解
DynamicDataSource.setDataSource(dsClass.value());
logger.debug("set datasource is " + dsClass.value());
}else{
//如果都不存在,則使用默認(rèn)
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、Dao層,跟正常一樣的
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層,加上多數(shù)據(jù)源注解
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測(cè)試
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;
/**
* 動(dòng)態(tài)數(shù)據(jù)源測(cè)試
*/
@Api(tags = "動(dòng)態(tài)數(shù)據(jù)源測(cè)試")
@RestController
@RequestMapping("/test")
public class DynamicController {
@Autowired
private Test1Service test1Service;
@Autowired
private Test2Service test2Service;
@ApiOperation(value = "數(shù)據(jù)源1")
@RequestMapping(value = "/test1", method = RequestMethod.GET)
public String test1() {
Test1Entity entity = test1Service.getById(1);
return entity.getName();
}
@ApiOperation(value = "數(shù)據(jù)源2")
@RequestMapping(value = "/test2", method = RequestMethod.GET)
public String test2() {
Test2Entity entity = test2Service.getById(2);
return entity.getName();
}
}
多數(shù)據(jù)源可能會(huì)碰到事務(wù)問(wèn)題:https://blog.csdn.net/u011974797/article/details/130154340
三、結(jié)果展示
服務(wù)啟動(dòng)會(huì)打印日志:
com.alibaba.druid.pool.DruidDataSource : {dataSource-1,first} inited
com.alibaba.druid.pool.DruidDataSource : {dataSource-2,second} inited
druid監(jiān)控頁(yè)面:
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-492957.html
源碼地址:https://download.csdn.net/download/u011974797/87680942文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-492957.html
到了這里,關(guān)于Spring Boot集成Druid實(shí)現(xiàn)多數(shù)據(jù)源的兩種方式的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!