springboot配置多個數(shù)據(jù)源兩種方式實現(xiàn)
在我們的實際業(yè)務中可能會遇到;在一個項目里面讀取多個數(shù)據(jù)庫的數(shù)據(jù)來進行展示,spring對同時配置多個數(shù)據(jù)源是支持的。
本文中將展示兩種方法來實現(xiàn)這個功能。
springboot+mybatis
第一種方式:
在配置文件中配置多個數(shù)據(jù)源,然后通過配置類來獲取數(shù)據(jù)源以及mapper相關(guān)的掃描配置
pom.xml
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath/> </parent> <dependencies> <!-- druid數(shù)據(jù)源驅(qū)動 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.1.0</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!--mybatis SpringBoot依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- aop依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.1</version> </dependency> <!-- 通用mapper --> <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper-spring-boot-starter</artifactId> <version>1.1.5</version> </dependency> <!-- druid監(jiān)控依賴 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.28</version> </dependency> </dependencies>
application.yml
spring: datasource: type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=utf8 username: root password: 123456 url2: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=utf8 username2: root password2: 123456
DruidDBConfig 連接池相關(guān)配置
package com.xbz.common.config; import com.alibaba.druid.support.http.StatViewServlet; import com.alibaba.druid.support.http.WebStatFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.boot.web.servlet.ServletRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Druid監(jiān)控 */ @SuppressWarnings("AlibabaRemoveCommentedCode") @Configuration public class DruidDBConfig { private Logger logger = LoggerFactory.getLogger(DruidDBConfig.class); /** * 注冊ServletRegistrationBean * @return */ @Bean public ServletRegistrationBean druidServlet() { ServletRegistrationBean reg = new ServletRegistrationBean(); reg.setServlet(new StatViewServlet()); reg.addUrlMappings("/druid/*"); reg.addInitParameter("allow", ""); //白名單 return reg; } /** * 注冊FilterRegistrationBean * @return */ @Bean public FilterRegistrationBean filterRegistrationBean() { FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(); filterRegistrationBean.setFilter(new WebStatFilter()); filterRegistrationBean.addUrlPatterns("/*"); filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); filterRegistrationBean.addInitParameter("profileEnable", "true"); filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE"); filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION"); filterRegistrationBean.addInitParameter("DruidWebStatFilter", "/*"); return filterRegistrationBean; } }
MasterDbConfig 注意讀取數(shù)據(jù)庫連接相關(guān)的鍵,以及掃描實體、mapper等
package com.xbz.common.config; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; import java.sql.SQLException; @Configuration @MapperScan(basePackages = MasterDbConfig.PACKAGE , sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDbConfig { private Logger logger = LoggerFactory.getLogger(MasterDbConfig.class); // 精確到 master 目錄,以便跟其他數(shù)據(jù)源隔離 static final String PACKAGE = "com.xbz.**.dao.master"; private static final String MAPPER_LOCATION = "classpath*:mapper/master/*.xml"; private static final String DOMAIN_PACKAGE = "com.xbz.**.domain"; @Value("${spring.datasource.url}") private String dbUrl; @Value("${spring.datasource.username}") private String username; @Value("${spring.datasource.password}") private String password; @Value("${spring.datasource.driverClassName}") private String driverClassName; @Bean(name="masterDataSource") //聲明其為Bean實例 @Primary //在同樣的DataSource中,首先使用被標注的DataSource public DataSource masterDataSource() { DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(this.dbUrl); datasource.setUsername(username); datasource.setPassword(password); datasource.setDriverClassName(driverClassName); return datasource; } @Bean(name = "masterTransactionManager") @Primary public DataSourceTransactionManager masterTransactionManager() { return new DataSourceTransactionManager(masterDataSource()); } @Bean(name = "masterSqlSessionFactory") @Primary public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource masterDataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(masterDataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(MasterDbConfig.MAPPER_LOCATION)); sessionFactory.setTypeAliasesPackage(DOMAIN_PACKAGE); //mybatis 數(shù)據(jù)庫字段與實體類屬性駝峰映射配置 sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true); return sessionFactory.getObject(); } }
ClusterDbConfig
package com.xbz.common.config; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; import java.sql.SQLException; /** * 從數(shù)據(jù)源配置 * 若需要配置更多數(shù)據(jù)源 , 直接在yml中添加數(shù)據(jù)源配置再增加相應的新的數(shù)據(jù)源配置類即可 */ @Configuration @MapperScan(basePackages = ClusterDbConfig.PACKAGE , sqlSessionFactoryRef = "clusterSqlSessionFactory") public class ClusterDbConfig { private Logger logger = LoggerFactory.getLogger(ClusterDbConfig.class); // 精確到 cluster 目錄,以便跟其他數(shù)據(jù)源隔離 static final String PACKAGE = "com.xbz.**.dao.cluster"; private static final String MAPPER_LOCATION = "classpath*:mapper/cluster/*.xml"; private static final String DOMAIN_PACKAGE = "com.xbz.**.domain"; @Value("${spring.datasource.url2}") private String dbUrl; @Value("${spring.datasource.username2}") private String username; @Value("${spring.datasource.password2}") private String password; @Value("${spring.datasource.driverClassName}") private String driverClassName; @Bean(name="clusterDataSource") //聲明其為Bean實例 public DataSource clusterDataSource() { DruidDataSource datasource = new DruidDataSource(); datasource.setUrl(this.dbUrl); datasource.setUsername(username); datasource.setPassword(password); datasource.setDriverClassName(driverClassName); return datasource; } @Bean(name = "clusterTransactionManager") public DataSourceTransactionManager clusterTransactionManager() { return new DataSourceTransactionManager(clusterDataSource()); } @Bean(name = "clusterSqlSessionFactory") public SqlSessionFactory clusterSqlSessionFactory(@Qualifier("clusterDataSource") DataSource culsterDataSource) throws Exception { final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean(); sessionFactory.setDataSource(culsterDataSource); sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources(ClusterDbConfig.MAPPER_LOCATION)); sessionFactory.setTypeAliasesPackage(DOMAIN_PACKAGE); //mybatis 數(shù)據(jù)庫字段與實體類屬性駝峰映射配置 sessionFactory.getObject().getConfiguration().setMapUnderscoreToCamelCase(true); return sessionFactory.getObject(); } }
不同的數(shù)據(jù)源配置不佟的mapper掃描位置,然后需要哪一個數(shù)據(jù)源就注入哪一個mapper接口即可
這樣獲取的數(shù)據(jù)就是來自不同的數(shù)據(jù)源了,這種方法比較簡單。
方法二
配置一個默認使用的數(shù)據(jù)源,然后定義多個其他的數(shù)據(jù)源,使用aop形成注解式選擇數(shù)據(jù)源
# 默認數(shù)據(jù)源 spring: datasource: druid: # 數(shù)據(jù)庫訪問配置, 使用druid數(shù)據(jù)源 type: com.alibaba.druid.pool.DruidDataSource driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf8 username: root password: 123456 # 其他數(shù)據(jù)源 custom: datasource: druid: type: com.alibaba.druid.pool.DruidDataSource names: fishlog,fishgame fishlog: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=utf8 username: root password: 123456 fishgame: driverClassName: com.mysql.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=utf8 username: root password: 123456
然后啟動類中注入注解
注解類及參數(shù)value TargetDataSource
package com.sysmg.common.config.datasoure; 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; @Target({ ElementType.METHOD, ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String value(); }
DynamicDataSource
package com.sysmg.common.config.datasoure; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
DynamicDataSourceAspect切面實現(xiàn)
package com.sysmg.common.config.datasoure; 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.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @Aspect @Order(-10) // 保證該AOP在@Transactional之前執(zhí)行 @Component public class DynamicDataSourceAspect { @Before("@annotation(targetDataSource)") public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Throwable { String dsId = targetDataSource.value(); if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) { System.err.println("數(shù)據(jù)源[{}]不存在,使用默認數(shù)據(jù)源 > {}" + targetDataSource.value() + point.getSignature()); } else { System.out.println("UseDataSource : {} > {}" + targetDataSource.value() + point.getSignature()); DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value()); } } @After("@annotation(targetDataSource)") public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) { System.out.println("RevertDataSource : {} > {}"+targetDataSource.value()+point.getSignature()); DynamicDataSourceContextHolder.clearDataSourceType(); } }
DynamicDataSourceContextHolder
package com.sysmg.common.config.datasoure; import java.util.ArrayList; import java.util.List; public class DynamicDataSourceContextHolder { private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); public static List<String> dataSourceIds = new ArrayList<String>(); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } public static boolean containsDataSource(String dataSourceId){ return dataSourceIds.contains(dataSourceId); } }
DynamicDataSourceRegister數(shù)據(jù)源注冊類
package com.sysmg.common.config.datasoure; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.PropertyValues; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; 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.ImportBeanDefinitionRegistrar; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private static final Object DATASOURCE_TYPE_DEFAULT = "com.alibaba.druid.pool.DruidDataSource"; private ConversionService conversionService = new DefaultConversionService(); private PropertyValues dataSourcePropertyValues; private DataSource defaultDataSource; private Map<String, DataSource> customDataSources = new HashMap<String, DataSource>(); @Override public void setEnvironment(Environment environment) { System.out.println("DynamicDataSourceRegister.setEnvironment()"); initDefaultDataSource(environment); initCustomDataSources(environment); } private void initDefaultDataSource(Environment env) { RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.druid."); Map<String, Object> dsMap = new HashMap<String, Object>(); dsMap.put("type", propertyResolver.getProperty("type")); dsMap.put("driverClassName", propertyResolver.getProperty("driverClassName")); dsMap.put("url", propertyResolver.getProperty("url")); dsMap.put("username", propertyResolver.getProperty("username")); dsMap.put("password", propertyResolver.getProperty("password")); defaultDataSource = buildDataSource(dsMap); dataBinder(defaultDataSource, env); } private void initCustomDataSources(Environment env) { RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.datasource.druid."); String dsPrefixs = propertyResolver.getProperty("names"); for (String dsPrefix : dsPrefixs.split(",")) {// 多個數(shù)據(jù)源 Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + "."); DataSource ds = buildDataSource(dsMap); customDataSources.put(dsPrefix, ds); dataBinder(ds, env); } } @SuppressWarnings("unchecked") public DataSource buildDataSource(Map<String, Object> dsMap) { Object type = dsMap.get("type"); if (type == null) { type = DATASOURCE_TYPE_DEFAULT;// 默認DataSource } Class<? extends DataSource> dataSourceType; try { dataSourceType = (Class<? extends DataSource>) Class.forName((String) type); String driverClassName = dsMap.get("driverClassName").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.setConversionService(conversionService); dataBinder.setIgnoreNestedProperties(false);// false dataBinder.setIgnoreInvalidFields(false);// false dataBinder.setIgnoreUnknownFields(true);// true if (dataSourcePropertyValues == null) { Map<String, Object> rpr = new RelaxedPropertyResolver(env, "spring.datasource").getSubProperties("."); Map<String, Object> values = new HashMap<>(rpr); values.remove("type"); values.remove("driverClassName"); values.remove("url"); values.remove("username"); values.remove("password"); dataSourcePropertyValues = new MutablePropertyValues(values); } dataBinder.bind(dataSourcePropertyValues); } @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { System.out.println("DynamicDataSourceRegister.registerBeanDefinitions()"); Map<Object, Object> targetDataSources = new HashMap<Object, Object>(); targetDataSources.put("dataSource", defaultDataSource); DynamicDataSourceContextHolder.dataSourceIds.add("dataSource"); targetDataSources.putAll(customDataSources); for (String key : customDataSources.keySet()) { DynamicDataSourceContextHolder.dataSourceIds.add(key); } GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DynamicDataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource); mpv.addPropertyValue("targetDataSources", targetDataSources); registry.registerBeanDefinition("dataSource", beanDefinition); } }
這里使用的時候把需要使用的表名注入,如果是默認的數(shù)據(jù)源,則不需要加這個注解,具體的springboot自定義注解的建立,可參考http://www.dbjr.com.cn/article/239952.htm,這里的mapper文件和實體掃描可以跟默認的數(shù)據(jù)源配置相同即可
到此這篇關(guān)于springboot配置多個數(shù)據(jù)源兩種方式實現(xiàn)的文章就介紹到這了,更多相關(guān)springboot配置多個數(shù)據(jù)源內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot多數(shù)據(jù)源配置并通過注解實現(xiàn)動態(tài)切換數(shù)據(jù)源
- SpringBoot多數(shù)據(jù)源讀寫分離的自定義配置問題及解決方法
- 使用SpringBoot配置多數(shù)據(jù)源的經(jīng)驗分享
- spring注解 @PropertySource配置數(shù)據(jù)源全流程
- Spring配置數(shù)據(jù)源的三種方式(小結(jié))
- 解決springboot項目不配置數(shù)據(jù)源啟動報錯問題
- springboot整合多數(shù)據(jù)源配置方式
- Spring配置數(shù)據(jù)源流程與作用詳解
相關(guān)文章
maven如何利用springboot的配置文件進行多個環(huán)境的打包
這篇文章主要介紹了maven如何利用springboot的配置文件進行多個環(huán)境的打包,在Spring Boot中多環(huán)境配置文件名需要滿足application-{profiles.active}.properties的格式,其中{profiles.active}對應你的環(huán)境標識,本文給大家詳細講解,需要的朋友可以參考下2023-02-02MyBatis關(guān)閉一級緩存的兩種方式(分注解和xml兩種方式)
這篇文章主要介紹了MyBatis關(guān)閉一級緩存的兩種方式(分注解和xml兩種方式),mybatis默認開啟一級緩存,執(zhí)行2次相同sql,但是第一次查詢sql結(jié)果會加工處理這個時候需要關(guān)閉一級緩存,本文給大家詳細講解需要的朋友可以參考下2022-11-11使用jackson實現(xiàn)對象json之間的相互轉(zhuǎn)換(spring boot)
這篇文章主要介紹了使用jackson實現(xiàn)對象json之間的相互轉(zhuǎn)換(spring boot),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09Java數(shù)據(jù)結(jié)構(gòu)之常見排序算法(下)
這篇文章主要介紹了Java數(shù)據(jù)結(jié)構(gòu)之常見排序算法(下),與之相對有(上),想了解的朋友可以去本網(wǎng)站掃搜,在這兩篇文章里涵蓋關(guān)于八大排序算法的所有內(nèi)容,需要的朋友可以參考下2023-01-01