本文实现案例场景:
某系统除了须要从自己的主要数据库上读取和管理数据外。另一部分业务涉及到其它多个数据库,要求能够在不论什么方法上能够灵活指定详细要操作的数据库。

为了在开发中以最简单的方法使用,本文基于注解和AOP的方法实现,在spring boot框架的项目中。加入本文实现的代码类后,仅仅须要配置好数据源就能够直接通过注解使用,简单方便。

一配置二使用
1. 启动类注冊动态数据源
2. 配置文件里配置多个数据源
3. 在须要的方法上使用注解指定数据源

1、在启动类加入 @Import({DynamicDataSourceRegister.class, MProxyTransactionManagementConfiguration.class})

@SpringBootApplication
@Import({DynamicDataSourceRegister.class}) // 注冊动态多数据源
public class SpringBootSampleApplication {// 省略其它代码
}

2、配置文件配置内容为:
(不包含项目中的其它配置,这里仅仅是数据源相关的)

# 主数据源,默认的
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=123456# 很多其它数据源
custom.datasource.names=ds1,ds2
custom.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
custom.datasource.ds1.url=jdbc:mysql://localhost:3306/test1
custom.datasource.ds1.username=root
custom.datasource.ds1.password=123456custom.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
custom.datasource.ds2.url=jdbc:mysql://localhost:3306/test2
custom.datasource.ds2.username=root
custom.datasource.ds2.password=123456

3、用法

package org.springboot.sample.service;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;import org.springboot.sample.datasource.TargetDataSource;
import org.springboot.sample.entity.Student;
import org.springboot.sample.mapper.StudentMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;/*** Student Service** @author   单红宇(365384722)* @myblog  http://blog.csdn.net/catoop/* @create    2016年1月12日*/
@Service
public class StudentService {@Autowiredprivate JdbcTemplate jdbcTemplate;// MyBatis的Mapper方法定义接口@Autowiredprivate StudentMapper studentMapper;@TargetDataSource(name="ds2")public List<Student> likeName(String name){return studentMapper.likeName(name);}public List<Student> likeNameByDefaultDataSource(String name){return studentMapper.likeName(name);}/*** 不指定数据源使用默认数据源** @return* @author SHANHY* @create  2016年1月24日*/public List<Student> getList(){String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE   FROM STUDENT";return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){@Overridepublic Student mapRow(ResultSet rs, int rowNum) throws SQLException {Student stu = new Student();stu.setId(rs.getInt("ID"));stu.setAge(rs.getInt("AGE"));stu.setName(rs.getString("NAME"));stu.setSumScore(rs.getString("SCORE_SUM"));stu.setAvgScore(rs.getString("SCORE_AVG"));return stu;}});}/*** 指定数据源** @return* @author SHANHY* @create  2016年1月24日*/@TargetDataSource(name="ds1")public List<Student> getListByDs1(){String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE   FROM STUDENT";return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){@Overridepublic Student mapRow(ResultSet rs, int rowNum) throws SQLException {Student stu = new Student();stu.setId(rs.getInt("ID"));stu.setAge(rs.getInt("AGE"));stu.setName(rs.getString("NAME"));stu.setSumScore(rs.getString("SCORE_SUM"));stu.setAvgScore(rs.getString("SCORE_AVG"));return stu;}});}/*** 指定数据源** @return* @author SHANHY* @create  2016年1月24日*/@TargetDataSource(name="ds2")public List<Student> getListByDs2(){String sql = "SELECT ID,NAME,SCORE_SUM,SCORE_AVG, AGE   FROM STUDENT";return (List<Student>) jdbcTemplate.query(sql, new RowMapper<Student>(){@Overridepublic Student mapRow(ResultSet rs, int rowNum) throws SQLException {Student stu = new Student();stu.setId(rs.getInt("ID"));stu.setAge(rs.getInt("AGE"));stu.setName(rs.getString("NAME"));stu.setSumScore(rs.getString("SCORE_SUM"));stu.setAvgScore(rs.getString("SCORE_AVG"));return stu;}});}
}

要注意的是。在使用MyBatis时。注解@TargetDataSource 不能直接在接口类Mapper上使用。

按上面的代码中StudentMapper为接口,代码例如以下:

package org.springboot.sample.mapper;import java.util.List;import org.springboot.sample.entity.Student;/*** StudentMapper。映射SQL语句的接口,无逻辑实现** @author 单红宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月20日*/
public interface StudentMapper {// 注解 @TargetDataSource 不能够在这里使用List<Student> likeName(String name);Student getById(int id);String getNameById(int id);}


请将以下几个类放到Spring Boot项目中。

DynamicDataSource.java
DynamicDataSourceAspect.java
DynamicDataSourceContextHolder.java
DynamicDataSourceRegister.java
TargetDataSource.java

package org.springboot.sample.datasource;import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;/*** 动态数据源** @author   单红宇(365384722)* @myblog  http://blog.csdn.net/catoop/* @create    2016年1月22日*/
public class DynamicDataSource extends AbstractRoutingDataSource {@Overrideprotected Object determineCurrentLookupKey() {return DynamicDataSourceContextHolder.getDataSourceType();}}
package org.springboot.sample.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.stereotype.Component;/*** 切换数据源Advice** @author 单红宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月23日*/
@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 org.springboot.sample.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* @author SHANHY* @create  2016年1月24日*/public static boolean containsDataSource(String dataSourceId){return dataSourceIds.contains(dataSourceId);}
}
package org.springboot.sample.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;/*** 动态数据源注冊<br/>* 启动动态数据源请在启动类中(如SpringBootSampleApplication)* 加入 @Import(DynamicDataSourceRegister.class)** @author 单红宇(365384722)* @myblog http://blog.csdn.net/catoop/* @create 2016年1月24日*/
public class DynamicDataSourceRegisterimplements 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<>();@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* @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;// 默认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);}/*** 初始化主数据源** @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);//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);}/*** 初始化很多其它数据源** @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);}}}
package org.springboot.sample.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   单红宇(365384722)* @myblog  http://blog.csdn.net/catoop/* @create    2016年1月23日*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {String name();
}

本文代码博主是经过測试后没有问题才发出来共享给大家的。对于连接池參数配置会应用到全部数据源上。
比方配置一个:

spring.datasource.maximum-pool-size=80

那么我们全部的数据源都会自己主动应用上。


补充:
假设你使用的是SpringMVC,并集成了Shiro。一般按网上的配置你可能是:

    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor"><property name="proxyTargetClass" value="true" /></bean><bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"><property name="securityManager" ref="securityManager"/></bean>

那么你请不要这样做,请按以下方法配置:

    <!-- AOP式方法级权限检查  --><!-- 不要使用 DefaultAdvisorAutoProxyCreator 会出现二次代理的问题。这里不详述。

mark by shanhy 2016-05-15 -->

<aop:config proxy-target-class="true"/> <!-- 或者你使用了 <aop:aspectj-autoproxy proxy-target-class="true" /> 也能够。 --> <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"> <property name="securityManager" ref="securityManager"/> </bean>

Spring Boot 动态数据源(多数据源自己主动切换)相关推荐

  1. spring boot 动态切换数据源实现多租户开发

    之前的文章有介绍过spring boot 动态切换数据源spring boot 动态切换数据源(数据源信息从数据库中读取)_lgq2016的博客-CSDN博客,今天简单介绍一下动态数据源切换实战,主要 ...

  2. 13、Spring Boot 2.x 多数据源配置

    1.13 Spring Boot 2.x 多数据源配置 完整源码: Spring-Boot-Demos 转载于:https://www.cnblogs.com/Grand-Jon/p/9999779. ...

  3. Spring Boot 2.0 多数据源编程 jdbcUrl is required with driverClassName

    转载:https://my.oschina.net/chinesedragon/blog/1647846 Spring Boot 2.0 多数据源编程 在Spring Boot 1.5.x之前,多数据 ...

  4. Spring Boot整合Jpa多数据源

    Spring Boot整合Jpa多数据源 本文是Spring Boot整合数据持久化方案的最后一篇,主要和大伙来聊聊Spring Boot整合Jpa多数据源问题.在Spring Boot整合JbdcT ...

  5. Spring Boot 动态数据源(Spring 注解数据源)

    本文实现案例场景: 某系统除了需要从自己的主要数据库上读取和管理数据外,还有一部分业务涉及到其他多个数据库,要求可以在任何方法上可以灵活指定具体要操作的数据库. 为了在开发中以最简单的方法使用,本文基 ...

  6. spring boot 动态切换数据源(数据源信息从数据库中读取)

    项目要求从多个源库(oracle,haha,pg)里面读取schema,table,字段等信息,spring提供了AbstractRoutingDataSource类实现动态数据源,下面就简单介绍一下 ...

  7. spring boot 动态数据源

    由于项目中要用到spring boot结合mybatis做一个动态的数据源,所以自己做了一个,也踩了很多坑,这里把成果分享出来.如果是1.x的springboot版本可以看前面的,如果是2.x版本的可 ...

  8. Spring Boot骚操作-多数据源Service层封装

    原文:https://www.pdai.tech/md/spring/springboot-data-multi.html mysql, es, mongodb 三个数据源用配置文件方式连接,JPA只 ...

  9. 【spring boot】 禁用/关闭数据源/DataSource

    前言 spring boot 2.0.0.RELEASE maven 3.5 eclipse 4.9.0 用spring boot做程序,不需要连接数据库.该程序一直工作正常. 在某次修改程序后,出现 ...

最新文章

  1. Firefox beta 开始原生支持 Windows 10 ARM64
  2. 802.11概述及帧结构分析
  3. 数据结构2:中序线索化二叉树为什么要通过pre设置后继结点
  4. LeetCode 743. Network Delay Time
  5. easyui datagrid 每行数据添加 按钮
  6. 【深度探讨】阿里巴巴万级规模 K8s 集群全局高可用体系之美
  7. python线程创建对象_Python多线程编程基础:如何创建线程?
  8. Flutter - 生成二维码与识别二维码
  9. 直接拿来用!GitHub10个开源免费的后台管理面板
  10. datasnap——动态注册服务类
  11. 生鲜电商之毒,食行生鲜模式虽好、恐也难解
  12. 主管好当:一不指点工作,二不检查工作,三不改正错误
  13. swun 1766 我的悲剧不可能那么好数
  14. C++例4.11 求两个或三个正整数中的最大数,用带有默认参数的函数实现。
  15. html文章整体居中,html如何实现文本上下居中
  16. python编写一个函数把华氏温度转换成摄氏温度_编写一个函数把华氏温度转换成摄氏温度,温度转换公式为:c=(f-32)*5/9。在主函数中输入华氏温度值......
  17. js,JQ 图片转换base64 base64转换为file对象,blob对象
  18. H.266/VVC代码学习:普通量化和率失真优化量化(RDOQ)
  19. 第二章节 MongoDB的基本命令
  20. SQL Server numeric数据类型

热门文章

  1. 天天向上 专访Data Domain创始人李凯
  2. LeetCode--014--最长公共前缀(java)
  3. JavaScript常见集合操作
  4. iOS开发之窥探UICollectionViewController(四) --一款功能强大的自定义瀑布流
  5. 【MySQL】MySQL的group_concat使用例子
  6. 使用外部表关联MySQL数据到Oracle
  7. zookeeper学习笔记2
  8. 针对web服务器容灾自动切换方案
  9. MVC的Model Binder总结
  10. 2019.4.26学习笔记(路由器router)