jar包引入
<!-- mysql连接驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
<!-- 连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.13</version>
</dependency>
配置文件
application.yml的配置
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
druid:
one: #默认数据源 连接test库
url: jdbc:mysql://127.0.0.1:3306/test?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: test
password: 123456
two: #数据源二 连接admin库
url: jdbc:mysql://127.0.0.1:3306/admin?allowMultiQueries=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false
username: admin
password: 123456
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
#Oracle需要打开注释
#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
定义数据源的名称
package com.bds.common.dataSource;
/**
* 数据源名称
*/
public interface DataSourceNames {
String ONE = "test";//对应application.yml中的数据源one(默认数据源)
String TWO = "admin";//对应application.yml中的数据源two
//String THREE = "root";//对应application.yml中的数据源three
}
创建动态数据源
package com.bds.common.dataSource;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.Map;
/**
* 动态数据源
* @author
* @date
*/
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();
}
}
动态数据源配置
package com.bds.common.dataSource;
import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import com.bds.common.utils.JspyptUtils;
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 geYang
* @date 2018-05-14
*/
@Configuration
public class DynamicDataSourceConfig {
/**
* 创建 DataSource Bean
* */
@Bean
@ConfigurationProperties("spring.datasource.druid.one")
public DataSource oneDataSource(){
DataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
@Bean
@ConfigurationProperties("spring.datasource.druid.two")
public DataSource twoDataSource(){
DataSource dataSource = DruidDataSourceBuilder.create().build();
return dataSource;
}
/**
* 如果还有数据源,在这继续添加 DataSource Bean
* */
//@Bean
//@ConfigurationProperties("spring.datasource.druid.three")//application中的名为three的数据源连接
//public DataSource threeDataSource(){
// DataSource dataSource = DruidDataSourceBuilder.create().build();
// return dataSource;
//}
@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中继续添加
//targetDataSources.put(DataSourceNames.THREE, twoDataSource);
System.out.println("DataSources:" + targetDataSources);
return new DynamicDataSource(oneDataSource, targetDataSources);
}
}
多数据源定义注解和设置aop代理
定义注解
package com.bds.common.dataSource;
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;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
/**
* 多数据源注解
* @author
* @date
*/
@SuppressWarnings("unused")
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DataSource {
/**
* 默认数据源为one,可修改为其他数据源为默认数据源
*/
String value() default DataSourceNames.ONE;//默认值
}
aop代理
package com.bds.common.dataSource;
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;
/**
* 数据源AOP切面处理
* @author
* @date
*/
@Aspect
@Component
public class DataSourceAspect implements Ordered {
protected Logger logger = LoggerFactory.getLogger(getClass());
/**
* 切点: 所有配置 DataSource 注解的方法
*/
@Pointcut("@annotation(com.bds.common.dataSource.DataSource)")//修改为自己DataSource.java所在路径的包
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("***************");
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.bds;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.Import;
import org.springframework.web.bind.annotation.CrossOrigin;
import com.bds.common.dataSource.DynamicDataSourceConfig;
@Import({DynamicDataSourceConfig.class})//导入自己配置的数据源
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})//多数据源配置,跳过框架自带的数据库引用
@CrossOrigin//跨域
public class AdminApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(AdminApplication.class, args);
}
}
使用测试
因为已经配置好了多数据源注解,在实际应用场景中只需要通过注解来实现数据源的切换即可实现动态数据源。
使用默认数据源:不需要做任何处理
@Mapper
public interface LcAreaIndexDao extends BaseMapper<LcAreaIndexEntity> {
List<LcAreaIndexEntity> queryList(Map<String, Object> map);
int queryListNum(Map<String, Object> map);
}
使用动态数据源:在dao层的方法上添加注解
@Mapper
public interface LcAreaIndexDao extends BaseMapper<LcAreaIndexEntity> {
@DataSource(DataSourceNames.TWO)
List<LcAreaIndexEntity> queryList(Map<String, Object> map);
@DataSource(DataSourceNames.TWO)
int queryListNum(Map<String, Object> map);
}
以上就已经实现了动态数据源的配置。
@DataSource注解也可以加在service层的方法上使用,与dao层是一致的。