在springboot中,有时会碰到需要使用多种数据库的情况,这时候就需要配置多数据源了,小伙伴们知道该如何配置多数据源吗?下面小编就带你浏览一下。
首先来看看我们的配置文件是怎么样的:
# spring配置 spring: #主数据源 datasource: driver - class - name: com.mysql.jdbc.Driver url: jdbc: mysql: //server:3306/simon_jia_dev?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false username: root password: js = 123456 type: com.zaxxer.hikari.HikariDataSource hikari: maxLifetime: 1765000# 一个连接的生命时长( 毫秒), 超时而且没被使用则被释放( retired), 缺省: 30 分钟, 建议设置比数据库超时时长少30秒以上 maximumPoolSize: 20# 连接池中允许的最大连接数。 缺省值: 10; 推荐的公式:((core_count * 2) + effective_spindle_count) minimumIdle: 1# 连接池中允许的最小空闲连接数# 更多数据源 custom: datasource: names: ds1, ds2 ds1: driver - class - name: com.mysql.jdbc.Driver url: jdbc: mysql: //server:3306/simon_jia_dev?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false username: root password: 123456 type: com.zaxxer.hikari.HikariDataSource hikari: maxLifetime: 1765000# 一个连接的生命时长( 毫秒), 超时而且没被使用则被释放( retired), 缺省: 30 分钟, 建议设置比数据库超时时长少30秒以上 maximumPoolSize: 20# 连接池中允许的最大连接数。 缺省值: 10; 推荐的公式:((core_count * 2) + effective_spindle_count) minimumIdle: 1# 连接池中允许的最小空闲连接数\ ds2: driver - class - name: com.mysql.jdbc.Driver url: jdbc: mysql: //localhost:3306/student?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&useSSL=false username: root password: 123456 type: com.zaxxer.hikari.HikariDataSource hikari: maxLifetime: 1765000# 一个连接的生命时长( 毫秒), 超时而且没被使用则被释放( retired), 缺省: 30 分钟, 建议设置比数据库超时时长少30秒以上 maximumPoolSize: 20# 连接池中允许的最大连接数。 缺省值: 10; 推荐的公式:((core_count * 2) + effective_spindle_count) minimumIdle: 1# 连接池中允许的最小空闲连接数
接下来是所需的注解类:
package com.simonjia.common.datasource; 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; /** * @Author: SimonHu * @Description:在方法上使用,用于指定使用哪个数据源 */ @Target( { ElementType.METHOD , ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TargetDataSource { String name(); }
再来是动态数据源
package com.simonjia.common.datasource; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * @Author: SimonHu * @Description:动态数据源 */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType(); } }
数据源切面
package com.simonjia.common.datasource; 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: SimonHu * @Description: */ @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, TargetDataSource ds) throws Throwable { String dsId = ds.name(); if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) { logger.error("数据源[{}]不存在,使用默认数据源 > {}", ds.name(), point.getSignature()); } else { logger.debug("Use DataSource : {} > {}", ds.name(), point.getSignature()); DynamicDataSourceContextHolder.setDataSourceType(ds.name()); } } @After("@annotation(ds)") public void restoreDataSource(JoinPoint point, TargetDataSource ds) { logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature()); DynamicDataSourceContextHolder.clearDataSourceType(); } }
package com.simonjia.common.datasource; import java.util.ArrayList; import java.util.List; /** * @Author: SimonHu * @Description: */ public class DynamicDataSourceContextHolder { private static final ThreadLocal < String > contextHolder = new ThreadLocal < String > (); public static List < String > dataSourceIds = new ArrayList < > (); public static void setDataSourceType(String dataSourceType) { contextHolder.set(dataSourceType); } public static String getDataSourceType() { return contextHolder.get(); } public static void clearDataSourceType() { contextHolder.remove(); } /** * 判断指定DataSrouce当前是否存在 * * @param dataSourceId * @return * @author SHANHY */ public static boolean containsDataSource(String dataSourceId) { return dataSourceIds.contains(dataSourceId); } }
最后注册动态数据源
package com.simonjia.common.datasource; import java.util.HashMap; import java.util.Map; import javax.sql.DataSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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; /** * @Author: SimonHu * @Date: 2019/11/21 13:39 * @Description:动态数据源注册<br/> * * 启动动态数据源请在启动类中(如SpringBootSampleApplication) * * 添加 @Import(DynamicDataSourceRegister.class) */ public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceRegister.class); private ConversionService conversionService = new DefaultConversionService(); private PropertyValues dataSourcePropertyValues; // 如配置文件中未指定数据源类型,使用该默认值 private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource"; // private static final Object DATASOURCE_TYPE_DEFAULT = // "com.zaxxer.hikari.HikariDataSource"; // 数据源 private DataSource defaultDataSource; private Map < String, DataSource > customDataSources = new HashMap < > (); @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { 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); } // 创建DynamicDataSource 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); logger.info("Dynamic DataSource Registry"); } /** * 创建DataSource * * @param type * @param driverClassName * @param url * @param username * @param password * @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) { // 默认DataSource type = DATASOURCE_TYPE_DEFAULT; } 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; } /** * 加载多数据源配置 */ @Override public void setEnvironment(Environment env) { initDefaultDataSource(env); initCustomDataSources(env); } /** * 初始化主数据源 * * @author SHANHY * @create 2016年1月24日 */ private void initDefaultDataSource(Environment env) { // 读取主数据源 RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.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); dataBinder(defaultDataSource, env); } /** * 为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, "spring.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); } /** * 初始化更多数据源 * * @author SHANHY * @create 2016年1月24日 */ private void initCustomDataSources(Environment env) { // 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源 RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "custom.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, env); } } }
加上启动类
@SpringBootApplication @Import( { DynamicDataSourceRegister.class }) // 注册动态多数据源 public class JiaApplication extends WebMvcConfigurerAdapter { public static void main(String[] args) { SpringApplication.run(JiaApplication.class, args); } }
最后调用一下就成功了。
以上就是关于springboot配置多数据源的所有内容,相信你已经知道如何配置多数据源了吧,如果想知道更多java项目中常见问题,请记得持续关注奇Q工具网了解具体吧。
推荐阅读: