SpringBoot 自定义+动态切换数据源教程

1、添加maven依赖

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<scope>runtime</scope>

</dependency>

<dependency>

<groupId>com.alibaba</groupId>

<artifactId>druid</artifactId>

<version>1.1.3</version>

</dependency>

<!--properties动态注入-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-configuration-processor</artifactId>

<optional>true</optional>

</dependency>

<!--springBoot的aop-->

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-aop</artifactId>

</dependency>

2、配置application.yml

# 数据库访问配置

# 主数据源,默认的

druid:

datasource:

type: com.alibaba.druid.pool.DruidDataSource

driver-class-name: com.mysql.jdbc.Driver

url: jdbc:mysql://192.168.1.113:3306/test?useUnicode=true&characterEncoding=utf-8

username: root

password: root

# 下面为连接池的补充设置,应用到上面所有数据源中

# 初始化大小,最小,最大

initialSize: 5

minIdle: 5

maxActive: 20

# 配置获取连接等待超时的时间

maxWait: 60000

# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒

timeBetweenEvictionRunsMillis: 60000

# 配置一个连接在池中最小生存的时间,单位是毫秒

minEvictableIdleTimeMillis: 300000

validationQuery: SELECT 1 FROM DUAL

testWhileIdle: true

testOnBorrow: false

testOnReturn: false

# 打开PSCache,并且指定每个连接上PSCache的大小

poolPreparedStatements: true

maxPoolPreparedStatementPerConnectionSize: 20

# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙

filters: stat,wall,log4j

# 通过connectProperties属性来打开mergeSql功能;慢SQL记录

connectionProperties:

druid:

stat:

mergeSql: true

slowSqlMillis: 5000

# 合并多个DruidDataSource的监控数据

#多数据源

mysql-db:

datasource:

names: logic,dao

logic:

driver-class-name: com.mysql.jdbc.Driver

url: jdbc:mysql://192.168.1.113:3306/test1?useUnicode=true&characterEncoding=utf-8

username: root

password: root

dao:

driver-class-name: com.mysql.jdbc.Driver

url: jdbc:mysql://192.168.1.113:3306/test2?useUnicode=true&characterEncoding=utf-8

username: root

password: root

3、配置动态数据源

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

/**

* 动态数据源

* @author 陈梓平

* @date 2017/10/9.

*/

public class DynamicDataSource extends AbstractRoutingDataSource {

@Override

protected Object determineCurrentLookupKey() {

return DataSourceHolder.getDataSource();

}

}

4、配置数据源操作Holder

import java.util.ArrayList;

import java.util.List;

/**

* 数据源操作

* @author 陈梓平

* @date 2017/10/9.

*/

public class DataSourceHolder {

//线程本地环境

private static final ThreadLocal<String> contextHolders = new ThreadLocal<String>();

//数据源列表

public static List<String> dataSourceIds = new ArrayList<>();

//设置数据源

public static void setDataSource(String customerType) {

contextHolders.set(customerType);

}

//获取数据源

public static String getDataSource() {

return (String) contextHolders.get();

}

//清除数据源

public static void clearDataSource() {

contextHolders.remove();

}

/**

* 判断指定DataSrouce当前是否存在

* @param dataSourceId

* @return

* @author SHANHY

* @create 2016年1月24日

*/

public static boolean containsDataSource(String dataSourceId){

return dataSourceIds.contains(dataSourceId);

}

}

5、读取自定义数据源,并配置

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.MutablePropertyValues;

import org.springframework.beans.PropertyValues;

import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;

import org.springframework.boot.bind.RelaxedDataBinder;

import org.springframework.boot.bind.RelaxedPropertyResolver;

import org.springframework.context.EnvironmentAware;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.core.convert.ConversionService;

import org.springframework.core.convert.support.DefaultConversionService;

import org.springframework.core.env.Environment;

import org.springframework.stereotype.Component;

import javax.sql.DataSource;

import java.util.HashMap;

import java.util.Map;

/**

* 数据源配置

* @author 陈梓平

* @date 2017/10/9.

*/

@Component

@Configuration

public class DynamicDataSourceConfig implements EnvironmentAware {

private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceConfig.class);

// 默认数据源

private DataSource defaultDataSource;

// 属性值

private PropertyValues dataSourcePropertyValues;

// 如配置文件中未指定数据源类型,使用该默认值

private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";

private ConversionService conversionService = new DefaultConversionService();

private Map<String, DataSource> customDataSources = new HashMap<>();

@Override

public void setEnvironment(Environment environment) {

initDefaultDatasource(environment);

initOtherDatasource(environment);

}

private void initOtherDatasource(Environment environment) {

RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "mysql-db.datasource.");

String dsPrefixs = propertyResolver.getProperty("names");

for (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源

Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix+".");

DataSource ds = buildDataSource(dsMap);

customDataSources.put(dsPrefix, ds);

dataBinder(ds, environment);

}

}

private void initDefaultDatasource(Environment environment) {

// 读取主数据源

RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(environment, "druid.datasource.");

Map<String, Object> dsMap = new HashMap<>();

dsMap.put("type", propertyResolver.getProperty("type"));

dsMap.put("driver-class-name", propertyResolver.getProperty("driver-class-name"));

dsMap.put("url", propertyResolver.getProperty("url"));

dsMap.put("username", propertyResolver.getProperty("username"));

dsMap.put("password", propertyResolver.getProperty("password"));

defaultDataSource = buildDataSource(dsMap);

DataSourceHolder.dataSourceIds.add("ds1");

dataBinder(defaultDataSource, environment);

}

/**

* 创建DataSource

* @param dsMap

* @return

* @author SHANHY

* @create 2016年1月24日

*/

@SuppressWarnings("unchecked")

public DataSource buildDataSource(Map<String, Object> dsMap) {

try {

Object type = dsMap.get("type");

if (type == null)

type = DATASOURCE_TYPE_DEFAULT;// 默认DataSource

Class<? extends DataSource> dataSourceType;

dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);

String driverClassName = dsMap.get("driver-class-name").toString();

String url = dsMap.get("url").toString();

String username = dsMap.get("username").toString();

String password = dsMap.get("password").toString();

DataSourceBuilder factory = DataSourceBuilder.create().driverClassName(driverClassName).url(url)

.username(username).password(password).type(dataSourceType);

return factory.build();

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

return null;

}

/**

* 为DataSource绑定更多数据

* @param dataSource

* @param env

* @author SHANHY

* @create 2016年1月25日

*/

private void dataBinder(DataSource dataSource, Environment env){

RelaxedDataBinder dataBinder = new RelaxedDataBinder(dataSource);

//dataBinder.setValidator(new LocalValidatorFactory().run(this.applicationContext));

dataBinder.setConversionService(conversionService);

dataBinder.setIgnoreNestedProperties(false);//false

dataBinder.setIgnoreInvalidFields(false);//false

dataBinder.setIgnoreUnknownFields(true);//true

if(dataSourcePropertyValues == null){

Map<String, Object> rpr = new RelaxedPropertyResolver(env, "druid.datasource.").getSubProperties(".");

Map<String, Object> values = new HashMap<>(rpr);

// 排除已经设置的属性

values.remove("type");

values.remove("driver-class-name");

values.remove("url");

values.remove("username");

values.remove("password");

dataSourcePropertyValues = new MutablePropertyValues(values);

}

dataBinder.bind(dataSourcePropertyValues);

}

@Bean(name = "dataSource")

public DynamicDataSource dataSource() {

DynamicDataSource dynamicDataSource = new DynamicDataSource();

// 默认数据源

dynamicDataSource.setDefaultTargetDataSource(defaultDataSource);

// 配置多数据源

Map<Object, Object> dsMap = new HashMap(5);

dsMap.put("ds1", defaultDataSource);

dsMap.putAll(customDataSources);

for (String key : customDataSources.keySet())

DataSourceHolder.dataSourceIds.add(key);

dynamicDataSource.setTargetDataSources(dsMap);

return dynamicDataSource;

}

}

6、动态切换关键——AOP进行切换

/**

* 动态数据源注解

* @author 陈梓平

* @date 2017/10/9.

*/

@Retention(RetentionPolicy.RUNTIME)

@Target({

ElementType.METHOD

})

public @interface DS {

String name() default "ds1";

}

import com.chen.config.dynamicDS.DataSourceHolder;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.annotation.After;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.core.annotation.Order;

import org.springframework.stereotype.Component;

/**

* 设置数据源切面

* @author 陈梓平

* @date 2017/10/9.

*/

@Aspect

@Order(-1)// 保证该AOP在@Transactional之前执行

@Component

public class DynamicDataSourceAspect {

private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class);

@Before("@annotation(ds)")

public void changeDataSource(JoinPoint point, DS ds) throws Throwable {

String dsId = ds.name();

if (!DataSourceHolder.containsDataSource(dsId)) {

logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature());

} else {

logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature());

DataSourceHolder.setDataSource(ds.name());

}

}

@After("@annotation(ds)")

public void restoreDataSource(JoinPoint point, DS ds) {

logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature());

DataSourceHolder.clearDataSource();

}

}

7、使用

1)、配置mapper

/**

* @author 陈梓平

* @date 2017/10/9.

*/

public interface DynamicDSMapper {

Integer queryJournal();

String queryUser();

String queryType();

}

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.chen.mapper.DynamicDSMapper">

<select id="queryJournal" resultType="java.lang.Integer">

SELECT uid FROM journal

</select>

<select id="queryUser" resultType="java.lang.String">

SELECT name FROM user

</select>

<select id="queryType" resultType="java.lang.String">

SELECT parent FROM p_type

</select>

</mapper>

2)、配置service

/**

* @author 陈梓平

* @date 2017/10/9.

*/

@Service

public class DynamicServciceImpl implements DynamicServcice {

@Autowired

private DynamicDSMapper dynamicDSMapper;

@DS()

public Integer ds1() {

return dynamicDSMapper.queryJournal();

}

@DS(name = "logic")

public String ds2() {

return dynamicDSMapper.queryUser();

}

@DS(name = "dao")

public String ds3() {

return dynamicDSMapper.queryType();

}

}

3)、单元测试调用

/**

* 多数原测试

* @author 陈梓平

* @date 2017/10/9.

*/

@RunWith(SpringRunner.class)

@SpringBootTest

public class TestDynamicDS {

private Logger logger = LoggerFactory.getLogger(TestDynamicDS.class);

//

@Autowired

private DynamicServcice dynamicServcice;

@Test

public void test() {

// Integer integer = dynamicServcice.ds1();

// logger.info("integer:"+integer);

// String ds2 = dynamicServcice.ds2();

// logger.info("ds2:"+ds2);

String ds3 = dynamicServcice.ds3();

logger.info("ds3:"+ds3);

}

}

4)、测试结果

结果

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

以上是 SpringBoot 自定义+动态切换数据源教程 的全部内容, 来源链接: utcz.com/p/251236.html

回到顶部