現在老板的需求是那么的講究,需要讀寫分離,那么我們如何配置多個數據源的,下面用springboot搭框架,配置多個數據源。
參考鏈接:https://blog.csdn.net/xuSir_1/article/details/78986972;
1.springboot配置多數據源 首先看一下application-dev.yml 配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
druid:
first: #數據源1
url: jdbc:mysql://192.168.1.252:3306/b2q?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8
username: root
password: b2q123456
second: #數據源2
url: jdbc:mysql://localhost:3306/b2q?allowMultiQueries=true&useUnicode=true&useSSL=false&characterEncoding=UTF-8
username: root
password: root
# password: Quali-tyCz87897013!
initial-size: 10
max-active: 100
min-idle: 10
max-wait: 60000
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
lower_case_table_names: 0
#validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
stat-view-servlet:
enabled: true
url-pattern: /druid/*
#login-username: admin
#login-password: admin
filter:
stat:
log-slow-sql: true
slow-sql-millis: 1000
merge-sql: false
wall:
config:
multi-statement-allow: true
上面的方式實現就已經配置了兩個 數據源了,下面來看下代碼的實現
2、配置一個注解,方便使用,直接在需要配置的方法上面加上數據源即可
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
String name() default "";
}
2-1:配置管理多數據源的名稱,方便管理。
/**
* 增加多數據源,在此配置
*/
public interface DataSourceNames {
String FIRST = "first";
String SECOND = "second";
}
3、動態數據源加載
package com.wsh.b2q.admin.datasources;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 動態數據源
* @author huojg
*/
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();
}
}
這里有必要說一下AbstractRoutingDataSource這個類,加載一個圖片:
可以看到AbstractRoutingDataSource獲取數據源之前會先調用determineCurrentLookupKey方法查找當前的lookupKey,這個lookupKey就是數據源標識。
因此通過重寫這個查找數據源標識的方法就可以讓spring切換到指定的數據源了。
3-1:重要一點:把上面的代碼加載到配置中
package com.wsh.b2q.admin.datasources;
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;
/**
* 配置多數據源
* @author huojg
*/
@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);
}
}
4、最最重要的一步,就是使用spring的aop原理,切面方式加載數據源
package com.wsh.b2q.admin.datasources.aspect;
import com.wsh.b2q.admin.datasources.DataSourceNames;
import com.wsh.b2q.admin.datasources.DynamicDataSource;
import com.wsh.b2q.admin.datasources.annotation.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;
/**
* 多數據源,切面處理類
* @author huojg
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
@Pointcut("@annotation(com.wsh.b2q.admin.datasources.annotation.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);
if(ds == null){
DynamicDataSource.setDataSource(DataSourceNames.FIRST);
logger.debug("set datasource is " + DataSourceNames.FIRST);
}else {
DynamicDataSource.setDataSource(ds.name());
logger.debug("set datasource is " + ds.name());
}
try {
return point.proceed();
} finally {
DynamicDataSource.clearDataSource();
logger.debug("clean datasource");
}
}
@Override
public int getOrder() {
return 1;
}
}
5、最后一步就是使用了在你的service的實現類 serviceImpl上面進行注解,這里是重點(我剛開一直放在dao上面,因為我用的是mybatis,以為就是要放在這個上面,結果一直出不來,最后才知道應該放在serviceImpl上面)
@Override
@DataSource(name="second")
public List<IntegralExchangeRule> list(Map<String,Object> map) {
return dao.list(map);
}
OK,現在已經全部配置完成,可以使用了
有必要說一下:我上面的DataSourceAspect這個類里面around方法里面,已經默認是數據源1,如果你不配置@DaeSource(name=""),它默認會使用第一個數據源,否則的話,按照你的數據源名稱去使用的。
