配置了一下druid的多數據源配置,嘗試了很多方法,Spring boot關於對Mysql和Sqlite多數據源的配置,記錄下來:
涉及技術點:
Springboot + Druid + Mysql +Sqlite
一、引入Jar包:
<!--Spring Boot依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.8.RELEASE</version>
</dependency>
<!--MYSQL連接依賴-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!--阿里數據源連接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<!--sqlite依賴-->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.28.0</version>
</dependency>
<!--aspects依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
二、配置參數:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
#druid相關配置
druid:
one:
name: ds_mysql
driver-class-name: com.mysql.cj.jdbc.Driver
#基本屬性
url: @jdbc.url@
username: @jdbc.username@
password: @jdbc.password@
test-while-idle: false
two:
name: ds_sqlite
driver-class-name: org.sqlite.JDBC
#基本屬性
url: jdbc:sqlite:db/test.db
username: test
password: test
test-while-idle: false
use-global-data-source-stat: true
#監控統計攔截的filters
filters: stat
#配置初始化大小/最小/最大
initial-size: 2
min-idle: 2
max-active: 20
#獲取連接等待超時時間
max-wait: 60000
#間隔多久進行一次檢測,檢測需要關閉的空閑連接
time-between-eviction-runs-millis: 60000
#一個連接在池中最小生存的時間
min-evictable-idle-time-millis: 300000
validation-query: SELECT 'x' FROM DUAL
# mysql需要設置校驗
# 指明是否在從池中取出連接前進行檢驗,如果檢驗失敗,則從池中去除連接並嘗試取出另一個.注意: 設置為true后如果要生效,validationQuery參數必須設置為非空字符串
test-while-idle: false
# 指明是否在歸還到池中前進行檢驗 注意: 設置為true后如果要生效,validationQuery參數必須設置為非空字符串
test-on-borrow: false
# 指明連接是否被空閑連接回收器(如果有)進行檢驗.如果檢測失敗,則連接將被從池中去除. 注意: 設置為true后如果要生效,validationQuery參數必須設置為非空字符串
test-on-return: false
#打開PSCache,並指定每個連接上PSCache的大小。oracle設為true,mysql設為false。分庫分表較多推薦設置為false
pool-prepared-statements: false
(參數配置,可參考: https://gitee.com/wenshao/druid/tree/master/druid-spring-boot-starter)
三、編寫配置文件:
1、定義數據源名稱常量 :
package com.meng.scaffold.config.datasource;
/**
* @Description: 數據源名稱
* @author: MengW9
* @Date: 2019-11-28
* @Time: 10:26
*/
public interface DataSourceNames {
String ONE = "ONE";
String TWO = "TWO";
}
2、創建動態數據源:
package com.meng.scaffold.config.datasource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* @program: scaffold
* @description: 動態數據源
* @author: MengW9
* @create: 2019-11-28 10:26
**/
public class DynamicDataSource extends AbstractRoutingDataSource {
private static final ThreadLocal<String> contextHolder = new ThreadLocal<>();
/**
* 配置DataSource, defaultTargetDataSource為主數據庫
*/
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();
}
}
3、動態數據源配置:
package com.meng.scaffold.config.datasource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
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 javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* @program: scaffold
* @description: 多數據源配置
* @author: MengW9
* @create: 2019-11-28 10:16
**/
@Configuration
public class DynamicDataSourceConfig {
/**
* 創建 DataSource Bean
*/
@Bean("oneDataSource")
@ConfigurationProperties("spring.datasource.druid.one")
public DataSource oneDataSource() {
return DruidDataSourceBuilder.create().build();
}
@Bean("twoDataSource")
@ConfigurationProperties("spring.datasource.druid.two")
public DataSource twoDataSource() {
return DruidDataSourceBuilder.create().build();
}
/**
* 如果還有數據源,在這繼續添加 DataSource Bean
*/
@Bean
@Primary
public DynamicDataSource dataSource(DataSource oneDataSource, DataSource twoDataSource) {
Map<Object, Object> targetDataSources = new HashMap<>(2);
targetDataSources.put(DataSourceNames.ONE, oneDataSource);
targetDataSources.put(DataSourceNames.TWO, twoDataSource);
// 還有數據源,在targetDataSources中繼續添加
System.out.println("DataSources:" + targetDataSources);
return new DynamicDataSource(oneDataSource, targetDataSources);
}
}
4、定義動態數據源注解:
package com.meng.scaffold.config.datasource;
import java.lang.annotation.*;
/**
* @description: 多數據源注解
* @author: MengW9
* @create: 2019-11-28 10:31
**/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DataSource {
String value() default DataSourceNames.ONE;
}
5、設置數據源 AOP 代理:
package com.meng.scaffold.config.datasource;
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 java.lang.reflect.Method;
/**
* @description: 數據源AOP切面處理
* @author: MengW9
* @create: 2019-11-28 10:33
**/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* 切點: 所有配置 DataSource 注解的方法
*/
@Pointcut("@annotation(com.meng.scaffold.config.datasource.DataSource)")
public void dataSourcePointCut() {
}
@Around("dataSourcePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
DataSource ds = method.getAnnotation(DataSource.class);
// 通過判斷 DataSource 中的值來判斷當前方法應用哪個數據源
DynamicDataSource.setDataSource(ds.value());
System.out.println("當前數據源: " + ds.value());
logger.debug("set datasource is " + ds.value());
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
logger.debug("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
四、修改啟動文件:
package com.meng.scaffold;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.annotation.Import;
import com.gy.fast.common.config.data.DynamicDataSourceConfig;
**
* 動態數據源配置,需要將自有的配置依賴(DynamicDataSourceConfig),將原有的依賴去除(DataSourceAutoConfiguration)
* exclude = DataSourceAutoConfiguration.class
* @author Meng
*/
@Import({DynamicDataSourceConfig.class})
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class DeviceApplication {
public static void main(String[] args) {
SpringApplication.run(DeviceApplication.class, args);
}
}
五、配置完成, 進行測試:
測試接口編寫:
package com.meng.scaffold.service;
import com.meng.scaffold.config.datasource.DataSource;
import com.meng.scaffold.config.datasource.DataSourceNames;
import com.meng.scaffold.dao.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @description: 測試多數據源
* @author: MengW9
* @create: 2019-11-28 10:39
**/
@Service
public class DataSourceTestService {
@Autowired
private UserService userService;
public User test1(Long userId) {
return userService.selectById(userId);
}
/**
* @Description: 多數據注解必須放在接口實現類的上面
* @Param: [userId]
* @Author: MengW9
*/
@DataSource(DataSourceNames.TWO)
public User test2(Long userId) {
return userService.selectById(userId);
}
}
編寫測試類:
package com.meng.scaffold;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.meng.scaffold.dao.User;
@RunWith(SpringRunner.class)
@SpringBootTest
public class DynamicDataSourceTest {
@Autowired
private DataSourceTestService dataSourceTestService;
@Test
public void test(){
// 數據源ONE
SysUser user1 = dataSourceTestService.test1(1L);
System.out.println(ToStringBuilder.reflectionToString(user1));
// 數據源TWO
SysUser user2 = dataSourceTestService.test2(1L);
System.out.println(ToStringBuilder.reflectionToString(user2));
// 數據源ONE
SysUser user3 = dataSourceTestService.test1(1L);
System.out.println(ToStringBuilder.reflectionToString(user3));
}
}
代碼地址:Git倉庫
參考:
