Spring boot 多数据源配置(分配置文件和数据库表两种情况)
来源:互联网 发布:爬山虎软件 编辑:程序博客网 时间:2024/06/01 09:10
由于项目有很多数据源需要配置,所以参考网上的资料研究了下。现在有2种实现方案,一种是数据源信息在项目中不会很多就2-3个那种,那就直接读取配置文件就行了,还有一种是有很多数据源的那种成百上千的可以考虑写到数据表中。
一、配置在配置文件中使用方法
1、配置文件application.properties(需要注意所有数据源共享主数据源的其它属性配置,如果需要不同的数据源配置不同的数据源配置,需要修改代码)
# 数据库访问配置# 主数据源,默认的spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.url= jdbc:mysql://127.0.0.1:3306/test0?useUnicode=true&characterEncoding=UTF-8spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.username=rootspring.datasource.password=root1234# 更多数据源com.pgl.edi.datasource.names=test1,test2 com.pgl.edi.datasource.test1.type=com.alibaba.druid.pool.DruidDataSourcecom.pgl.edi.datasource.test1.driver-class-name=com.mysql.jdbc.Drivercom.pgl.edi.datasource.test1.url=jdbc:mysql://127.0.0.1:3306/test1?useUnicode=true&characterEncoding=UTF-8com.pgl.edi.datasource.test1.username=rootcom.pgl.edi.datasource.test1.password=root1234com.pgl.edi.datasource.test2.type=com.alibaba.druid.pool.DruidDataSourcecom.pgl.edi.datasource.test2.driver-class-name=com.mysql.jdbc.Drivercom.pgl.edi.datasource.test2.url= jdbc:mysql://127.0.0.1:3306/test2?useUnicode=true&characterEncoding=UTF-8com.pgl.edi.datasource.test2.username=rootcom.pgl.edi.datasource.test2.password=root1234# 下面为连接池的补充设置,应用到上面所有数据源中# 初始化大小,最小,最大spring.datasource.initialSize=5spring.datasource.minIdle=5spring.datasource.maxActive=20# 配置获取连接等待超时的时间spring.datasource.maxWait=60000# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒spring.datasource.timeBetweenEvictionRunsMillis=60000# 配置一个连接在池中最小生存的时间,单位是毫秒spring.datasource.minEvictableIdleTimeMillis=300000spring.datasource.validationQuery=SELECT 1 FROM t_userspring.datasource.testWhileIdle=truespring.datasource.testOnBorrow=truespring.datasource.testOnReturn=false# 打开PSCache,并且指定每个连接上PSCache的大小spring.datasource.poolPreparedStatements=truespring.datasource.maxPoolPreparedStatementPerConnectionSize=20# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙spring.datasource.filters=stat,wall,log4j
2、然后是注册数据源类DynamicDataSourceRegister 如下
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;/** * * 功能描述:动态数据源注册 * 启动动态数据源请在启动类中(如Start) * 添加 @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 DataSource defaultDataSource;private Map<String, DataSource> customDataSources = new HashMap<>();@Overridepublic 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);}// 创建DynamicDataSourceGenericBeanDefinition 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 */@SuppressWarnings("unchecked")public DataSource buildDataSource(Map<String, Object> dsMap) {try {Object type = dsMap.get("type");if (type == null)type = DATASOURCE_TYPE_DEFAULT;// 默认DataSourceClass<? 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;}/** * 加载多数据源配置 */@Overridepublic void setEnvironment(Environment env) {initDefaultDataSource(env);initCustomDataSources(env);}/** * 初始化主数据源 * */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 */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);// falsedataBinder.setIgnoreInvalidFields(false);// falsedataBinder.setIgnoreUnknownFields(true);// trueif (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);}/** * 初始化更多数据源 * */private void initCustomDataSources(Environment env) {// 读取配置文件获取更多数据源,也可以通过defaultDataSource读取数据库获取更多数据源RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "com.pgl.edi.datasource.");//这里写自己的配置的数据源前缀String dsPrefixs = propertyResolver.getProperty("names");//根据自己定义的获取数据源keyfor (String dsPrefix : dsPrefixs.split(",")) {// 多个数据源Map<String, Object> dsMap = propertyResolver.getSubProperties(dsPrefix + ".");DataSource ds = buildDataSource(dsMap);customDataSources.put(dsPrefix, ds);dataBinder(ds, env);}}}
上面是注册多数据源代码,下面是如何使用数据源需要以下几个类DynamicDataSourceContextHolder、DynamicDataSourceAspect、DynamicDataSource、DataSource。
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<>();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 */public static boolean containsDataSource(String dataSourceId) {return dataSourceIds.contains(dataSourceId);}}
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, DataSource 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, DataSource ds) { logger.debug("Revert DataSource : {} > {}", ds.name(), point.getSignature()); DynamicDataSourceContextHolder.clearDataSourceType(); }}
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() { return DynamicDataSourceContextHolder.getDataSourceType();}}
mport 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)@Documentedpublic @interface DataSource {String name();}
以上就是这四个类的代码。使用的时候需要注意在启动类上使用@Import(DynamicDataSourceRegister.class),在要使用方法用注解的方式@DataSource(name="test1"),这样就可以使用了。如果没有指定数据源则使用默认数据源。还有一点配置文件中的names就是我们所使用数据源的key。
二、数据源信息配置在数据库中1、配置主数据源,主数据源主要是访问存储其他数据源信息的。
# 主数据源,默认的spring.datasource.type=com.alibaba.druid.pool.DruidDataSourcespring.datasource.url= jdbc:mysql://127.0.0.1:3306/test0?useUnicode=true&characterEncoding=UTF-8spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.username=rootspring.datasource.password=root1234
然后建立数据库dataource表如下
CREATE TABLE `datasource` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID', `type` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '数据源类型', `driver_class_name` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '数据库驱动类', `url` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '连接url', `username` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '用户名', `password` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '密码', `rmark` varchar(255) COLLATE utf8_bin DEFAULT NULL COMMENT '备注', `dsname` varchar(255) COLLATE utf8_bin NOT NULL COMMENT '数据源名称', `status` varchar(255) COLLATE utf8_bin NOT NULL DEFAULT '1' COMMENT '状态', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
在项目配置文件配置datasource表所在数据库信息,在这里也可以配置数据源的其他信息,比如初始化大小、最大连接大小等信息。(需要注意所有数据源共享主数据源的其它属性配置,如果需要不同的数据源配置不同的数据源配置,需要修改代码)。需要修改的地方就一个DynamicDataSourceRegister类中initCustomDataSources方法需要修改为如下
private void initCustomDataSources(Environment env) {// 读取库表中datasource获取更多数据源Map<String, Map<String, Object>> customInfo=getCustomDataSourceInfo();for (String key : customInfo.keySet()) { Map<String, Object> dsMap = customInfo.get(key); DataSource ds = buildDataSource(dsMap);customDataSources.put(key, ds);dataBinder(ds, env); } }private Map<String, Map<String, Object>> getCustomDataSourceInfo() {Map<String, Map<String, Object>> customMap = new HashMap<>();String sql = "select type,`driver_class_name`,url,username,`password`,`dsname` from datasource where status=1";JdbcTemplate jdbcTemplate=new JdbcTemplate(defaultDataSource);List<DataSourceInfo> infos=jdbcTemplate.query(sql, new RowMapper<DataSourceInfo>() {@Overridepublic DataSourceInfo mapRow(ResultSet rs, int rowNum) throws SQLException {DataSourceInfo info=new DataSourceInfo();info.setType(rs.getString("type"));info.setDriverClassName(rs.getString("driver_class_name"));info.setUrl(rs.getString("url"));info.setPassword(rs.getString("password"));info.setUsername(rs.getString("username"));info.setDsName(rs.getString("dsname"));return info;}});for(DataSourceInfo info:infos) {Map<String, Object> dsMap = new HashMap<>();dsMap.put("type", info.getType());dsMap.put("driver-class-name", info.getDriverClassName());dsMap.put("url", info.getUrl());dsMap.put("username", info.getUsername());dsMap.put("password", info.getPassword());customMap.put(info.getDsName(), dsMap);}return customMap;}使用方式和第一种一样。
在很多时候我们可能不喜欢用注解的方式指定使用的数据源,我们可以修改DynamicDataSourceAspect类可以使用如下配置
@Aspect@Order(-10) // 保证该AOP在@Transactional之前执行@Componentpublic class DynamicDataSourceAspect {private static final Logger logger = LoggerFactory.getLogger(DynamicDataSourceAspect.class); //由于我项目中使用方法名后缀是FromDB进入AOP处理且默认第一个参数为使用数据源的key //这里可以自己修改想要以什么方式匹配使用数据源@Pointcut("execution(* com.pgl.demo.service.impl..*.*FromDB(..))")public void excude() {}@Before("excude()")public void changeDataSource(JoinPoint point) throws Throwable {Object obj = point.getArgs()[0];if(obj instanceof String) {String dsName=obj.toString();if (!DynamicDataSourceContextHolder.containsDataSource(dsName)) {logger.error("数据源[{}]不存在,使用默认数据源 > {}", obj, point.getSignature());throw new Throwable("数据源["+dsName+"]不存在");} else {logger.info("Use DataSource : {} > {}", dsName, point.getSignature());DynamicDataSourceContextHolder.setDataSourceType(dsName);}logger.info("-----------args DataSource : {} > {}", dsName, point.getSignature());}else {logger.error("数据源[{}]不存在", obj); throw new Throwable("数据源["+obj+"]不存在");}}@After("excude()")public void restoreDataSource(JoinPoint point) {Object obj = point.getArgs()[0]; logger.info("Revert DataSource : {} > {}", obj.toString(), point.getSignature()); DynamicDataSourceContextHolder.clearDataSourceType();}}
使用这种方式的话要拦截的方法必须不能private的。
阅读全文
0 0
- Spring boot 多数据源配置(分配置文件和数据库表两种情况)
- Spring Boot + Mybatis多数据源和动态数据源配置
- Spring Boot + Mybatis多数据源和动态数据源配置
- spring boot Mybatis多数据源配置
- spring boot多数据源配置
- spring boot 多数据源配置
- spring boot + mybatis 多数据源配置
- Spring Boot多数据源配置
- Spring boot配置多数据源
- Spring Boot + Spring Data JPA项目配置多数据源
- spring-boot 使用 spring-data-jpa多数据源配置
- Spring Boot,Spring Data JPA多数据源支持配置
- [Spring-Boot] Spring MyBatis 多数据源的配置和管理
- Spring Boot 多数据源
- Spring Boot 多数据源
- Spring Boot多数据源
- Spring Boot 多数据源
- Spring Boot 多数据源
- 《李彦宏喊话互联网公司:AI时代只关心软件就out了》
- 滴滴推出“宝贝专车”,提前2小时预约且不收额外费用
- 乌镇人机决战进行时,首战即决战!
- Ubuntu下pip3的安装、升级、卸载
- Err1055,出现这个问题往往是在执行sql语句时候,在最后一行会出现这个问题。
- Spring boot 多数据源配置(分配置文件和数据库表两种情况)
- 3.4 Spark RDD Action操作6-saveAsHadoopFile、saveAsHadoopDataset
- 背包问题——动态规划
- 数据算法之瞎扯淡
- java1.4、1.5
- ELA算法用于保险欺诈-伪造照片检测及EXIF信息显示
- 3.4 Spark RDD Action操作7-saveAsNewAPIHadoopFile、saveAsNewAPIHadoopDataset
- Angular 4入门教程系列:6:Tour Of Heroes之服务和依赖注入
- PAT乙级1017. A除以B(20)