springboot配置多個數(shù)據(jù)源兩種方式實現(xiàn)
在我們的實際業(yè)務(wù)中可能會遇到;在一個項目里面讀取多個數(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: 123456DruidDBConfig 連接池相關(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://m.fzitv.net/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)文章
使用google.kaptcha來生成圖片驗證碼的實現(xiàn)方法
這篇文章主要介紹了使用google.kaptcha來生成圖片驗證碼的實現(xiàn)方法,非常不錯具有一定的參考借鑒價值,需要的朋友可以參考下2018-09-09
Java多線程Runable售票系統(tǒng)實現(xiàn)過程解析
這篇文章主要介紹了Java多線程Runable售票系統(tǒng)實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-06-06
Spring boot工具類靜態(tài)屬性注入及多環(huán)境配置詳解
這篇文章主要為大家詳細介紹了Spring boot工具類靜態(tài)屬性注入,及多環(huán)境配置詳解,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-04-04
換了最新的idea如何將原來舊版本的idea設(shè)置導進新的idea中
這篇文章主要介紹了換了最新的idea如何將原來舊版本的idea設(shè)置導進新的idea中,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-11-11
Spring需要三個級別緩存解決循環(huán)依賴原理解析
這篇文章主要為大家介紹了Spring需要三個級別緩存解決循環(huán)依賴原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02

