SpringBoot 自定義+動(dòng)態(tài)切換數(shù)據(jù)源教程
更新時(shí)間:2021年12月02日 08:59:57 作者:CatalpaFlat
這篇文章主要介紹了SpringBoot 自定義+動(dòng)態(tài)切換數(shù)據(jù)源教程,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
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動(dòng)態(tài)注入--> <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
# 數(shù)據(jù)庫訪問配置 # 主數(shù)據(jù)源,默認(rèn)的 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 # 下面為連接池的補(bǔ)充設(shè)置,應(yīng)用到上面所有數(shù)據(jù)源中 # 初始化大小,最小,最大 initialSize: 5 minIdle: 5 maxActive: 20 # 配置獲取連接等待超時(shí)的時(shí)間 maxWait: 60000 # 配置間隔多久才進(jìn)行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒 timeBetweenEvictionRunsMillis: 60000 # 配置一個(gè)連接在池中最小生存的時(shí)間,單位是毫秒 minEvictableIdleTimeMillis: 300000 validationQuery: SELECT 1 FROM DUAL testWhileIdle: true testOnBorrow: false testOnReturn: false # 打開PSCache,并且指定每個(gè)連接上PSCache的大小 poolPreparedStatements: true maxPoolPreparedStatementPerConnectionSize: 20 # 配置監(jiān)控統(tǒng)計(jì)攔截的filters,去掉后監(jiān)控界面sql無法統(tǒng)計(jì),'wall'用于防火墻 filters: stat,wall,log4j # 通過connectProperties屬性來打開mergeSql功能;慢SQL記錄 connectionProperties: druid: stat: mergeSql: true slowSqlMillis: 5000 # 合并多個(gè)DruidDataSource的監(jiān)控?cái)?shù)據(jù) #多數(shù)據(jù)源 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、配置動(dòng)態(tài)數(shù)據(jù)源
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 動(dòng)態(tài)數(shù)據(jù)源 * @author 陳梓平 * @date 2017/10/9. */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DataSourceHolder.getDataSource(); } }
4、配置數(shù)據(jù)源操作Holder
import java.util.ArrayList; import java.util.List; /** * 數(shù)據(jù)源操作 * @author 陳梓平 * @date 2017/10/9. */ public class DataSourceHolder { //線程本地環(huán)境 private static final ThreadLocal<String> contextHolders = new ThreadLocal<String>(); //數(shù)據(jù)源列表 public static List<String> dataSourceIds = new ArrayList<>(); //設(shè)置數(shù)據(jù)源 public static void setDataSource(String customerType) { contextHolders.set(customerType); } //獲取數(shù)據(jù)源 public static String getDataSource() { return (String) contextHolders.get(); } //清除數(shù)據(jù)源 public static void clearDataSource() { contextHolders.remove(); } /** * 判斷指定DataSrouce當(dāng)前是否存在 * @param dataSourceId * @return * @author SHANHY * @create 2016年1月24日 */ public static boolean containsDataSource(String dataSourceId){ return dataSourceIds.contains(dataSourceId); } }
5、讀取自定義數(shù)據(jù)源,并配置
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; /** * 數(shù)據(jù)源配置 * @author 陳梓平 * @date 2017/10/9. */ @Component @Configuration public class DynamicDataSourceConfig implements EnvironmentAware { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceConfig.class); // 默認(rèn)數(shù)據(jù)源 private DataSource defaultDataSource; // 屬性值 private PropertyValues dataSourcePropertyValues; // 如配置文件中未指定數(shù)據(jù)源類型,使用該默認(rèn)值 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(",")) {// 多個(gè)數(shù)據(jù)源 Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix+"."); DataSource ds = buildDataSource(dsMap); customDataSources.put(dsPrefix, ds); dataBinder(ds, environment); } } private void initDefaultDatasource(Environment environment) { // 讀取主數(shù)據(jù)源 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); } /** * 創(chuàng)建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;// 默認(rèn)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綁定更多數(shù)據(jù) * @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); // 排除已經(jīng)設(shè)置的屬性 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(); // 默認(rèn)數(shù)據(jù)源 dynamicDataSource.setDefaultTargetDataSource(defaultDataSource); // 配置多數(shù)據(jù)源 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、動(dòng)態(tài)切換關(guān)鍵——AOP進(jìn)行切換
/** * 動(dòng)態(tài)數(shù)據(jù)源注解 * @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; /** * 設(shè)置數(shù)據(jù)源切面 * @author 陳梓平 * @date 2017/10/9. */ @Aspect @Order(-1)// 保證該AOP在@Transactional之前執(zhí)行 @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("數(shù)據(jù)源[{}]不存在,使用默認(rèn)數(shù)據(jù)源 > {}", 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)、單元測試調(diào)用
/** * 多數(shù)原測試 * @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)、測試結(jié)果
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
您可能感興趣的文章:
- SpringBoot基于AbstractRoutingDataSource實(shí)現(xiàn)多數(shù)據(jù)源動(dòng)態(tài)切換
- springboot+dynamicDataSource動(dòng)態(tài)添加切換數(shù)據(jù)源方式
- Springboot實(shí)現(xiàn)根據(jù)用戶ID切換動(dòng)態(tài)數(shù)據(jù)源
- Springboot動(dòng)態(tài)切換數(shù)據(jù)源的具體實(shí)現(xiàn)與原理分析
- 詳細(xì)聊聊SpringBoot中動(dòng)態(tài)切換數(shù)據(jù)源的方法
- springboot中mybatis多數(shù)據(jù)源動(dòng)態(tài)切換實(shí)現(xiàn)
- 詳解SpringBoot+Mybatis實(shí)現(xiàn)動(dòng)態(tài)數(shù)據(jù)源切換
- SpringBoot Mybatis動(dòng)態(tài)數(shù)據(jù)源切換方案實(shí)現(xiàn)過程
- SpringBoot多數(shù)據(jù)源配置并通過注解實(shí)現(xiàn)動(dòng)態(tài)切換數(shù)據(jù)源
相關(guān)文章
Java Class 解析器實(shí)現(xiàn)方法示例
這篇文章主要通過對class文件的分析,介紹了Java Class 解析器實(shí)現(xiàn)方法示例,具有一定參考價(jià)值,需要的朋友可以了解下。2017-09-09java BASE64Encoder詳細(xì)介紹及簡單實(shí)例
這篇文章主要介紹了java BASE64Encoder詳細(xì)介紹及簡單實(shí)例的相關(guān)資料,需要的朋友可以參考下2017-01-01SpringBoot中使用Redisson的實(shí)現(xiàn)示例
Redission是一個(gè)強(qiáng)大的Java庫,用于構(gòu)建和管理分布式系統(tǒng)中的緩存和任務(wù)調(diào)度,本文主要介紹了SpringBoot中使用Redisson的實(shí)現(xiàn)示例,感興趣的可以了解一下2023-12-12Spring七大事務(wù)傳遞機(jī)制深入分析實(shí)現(xiàn)原理
實(shí)際項(xiàng)目開發(fā)中,如果涉及到多張表操作時(shí),為了保證業(yè)務(wù)數(shù)據(jù)的一致性,大家一般都會(huì)采用事務(wù)機(jī)制,好多小伙伴可能只是簡單了解一下,遇到事務(wù)失效的情況,便會(huì)無從下手,下面這篇文章主要給大家介紹了關(guān)于Spring事務(wù)傳遞機(jī)制的相關(guān)資料,需要的朋友可以參考下2023-03-03