本篇内容主要讲解“SpringBoot自定义动态切换数据源的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“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;public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.getDataSource(); }}
4、配置数据源操作Holder
import java.util.ArrayList;import java.util.List;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(); } 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;@Component@Configurationpublic 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); } @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; } 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进行切换
@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;@Aspect@Order(-1)// 保证该AOP在@Transactional之前执行@Componentpublic 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
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
@Servicepublic 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)、单元测试调用
@RunWith(SpringRunner.class)@SpringBootTestpublic 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自定义动态切换数据源的方法是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!