package com.xynet.statistics.config.dataresources;

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
/**
* 动态数据源
* Copyright © 2019 xynet Tech Ltd. All rights reserved
* @author: sund
* @date: 2019年3月23日 下午4:35:39
* @remark:
*/
public class DynamicDataSource extends AbstractRoutingDataSource { /**
* 代码中的determineCurrentLookupKey方法取得一个字符串, 该字符串将与配置文件中的相应字符串进行匹配以定位数据源
*/
@Override
protected Object determineCurrentLookupKey() {
/**
* DynamicDataSourceContextHolder代码中使用setDataSourceType
* 设置当前的数据源,在路由类中使用getDataSourceType进行获取,
* 交给AbstractRoutingDataSource进行注入使用
*/
return DynamicDataSourceContextHolder.getDataSourceType();
}
}
package com.xynet.statistics.config.dataresources;

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; /**
* 数据源切面
*
* @Order(-5)保证该AOP在@Transactional之前执行 order 越小优先级越高
* Copyright © 2019 xynet Tech Ltd. All rights reserved
* @author: sund
* @date: 2019年3月23日 下午4:24:49
* @remark:
*/
@Aspect
@Order(-5)
@Component
public class DynamicDataSourceAspect { /**
* @Before("@annotation(ds)") @Before:在方法执行之前进行执行: @annotation(
* targetDataSource): 会拦截注解targetDataSource的方法,否则不拦截;
*
* @param point
* @param targetDataSource
* @throws Throwable
*/
@Before("@annotation(targetDataSource)")
public void changeDataSource(JoinPoint point, TargetDataSource targetDataSource) throws Throwable {
// 获取当前的指定的数据源;
String dsId = targetDataSource.value();
// 如果不在我们注入的所有的数据源范围之内,那么输出警告信息,系统自动使用默认的数据源。
if (!DynamicDataSourceContextHolder.containsDataSource(dsId)) {
System.out.println("数据源【" + targetDataSource.value() + "】不存在,使用默认数据源:" + point.getSignature());
} else {
System.out.println("Use DataSource:" + targetDataSource.value() + ":" + point.getSignature());
// 找到的话,那么设置到动态数据源上下文中指定的数据源
DynamicDataSourceContextHolder.setDataSourceType(targetDataSource.value());
}
} @After("@annotation(targetDataSource)")
public void restoreDataSource(JoinPoint point, TargetDataSource targetDataSource) {
System.out.println("Revert DataSource:" + targetDataSource.value() + ":" + point.getSignature());
// 方法执行完毕之后,销毁当前数据源信息,进行垃圾回收
DynamicDataSourceContextHolder.clearDataSourceType();
}
}
package com.xynet.statistics.config.dataresources;

import java.util.ArrayList;
import java.util.List; /**
* 动态数据源上下文
* Copyright © 2019 xynet Tech Ltd. All rights reserved
* @author: sund
* @date: 2019年3月23日 下午4:32:45
* @remark:
*/
public class DynamicDataSourceContextHolder { /**
* 当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,
* 所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
*/
private static final ThreadLocal<String> contextHolder = new ThreadLocal<String>(); /**
* 管理所有的数据源id
* 主要是为了判断数据源是否存在
*/
public static List<String> dataSourceIds = new ArrayList<String>(); /**
* 使用setDataSourceType设置当前的
* @param dataSourceType
*/
public static void setDataSourceType(String dataSourceType) {
contextHolder.set(dataSourceType);
} public static String getDataSourceType() {
return contextHolder.get();
} public static void clearDataSourceType() {
contextHolder.remove();
} /**
* 判断指定DataSource当前是否存在
* @param dataSourceId
* @return
*/
public static boolean containsDataSource(String dataSourceId) {
return dataSourceIds.contains(dataSourceId);
} }
package com.xynet.statistics.config.dataresources;

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.Configuration;
import org.springframework.context.annotation.Import;
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; import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map; /**
* 动态数据源注册 Copyright © 2019 xynet Tech Ltd. All rights reserved
*
* @author: sund
* @date: 2019年3月23日 下午4:34:37
* @remark:要在启动类上增加注解 @Import({DynamicDataSourceRegister.class})
*/
public class DynamicDataSourceRegister implements ImportBeanDefinitionRegistrar, EnvironmentAware { private static final Object DATASOURCE_TYPE_DEFAULT = "org.apache.tomcat.jdbc.pool.DataSource";
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) {
initDefaultDataSource(environment);
initCustomDataSources(environment);
} /**
* 加载主数据源配置.
*
* @param env
*/
private void initDefaultDataSource(Environment env) {
RelaxedPropertyResolver propertyResolver = new RelaxedPropertyResolver(env, "spring.datasource.");
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);
} /**
* 加载更多据源配置.
*
* @param env
*/
private void initCustomDataSources(Environment env) {
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);
}
} public DataSource buildDataSource(Map<String, Object> dsMap) {
Object type = dsMap.get("type");
if (type == null) {
type = DATASOURCE_TYPE_DEFAULT;
}
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);
dataBinder.setIgnoreInvalidFields(false);
dataBinder.setIgnoreUnknownFields(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) {
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();
// 添加属性:AbstractRoutingDataSource.defaultTargetDataSource
mpv.addPropertyValue("defaultTargetDataSource", defaultDataSource);
mpv.addPropertyValue("targetDataSources", targetDataSources);
registry.registerBeanDefinition("dataSource", beanDefinition);
System.out.println("============注册数据源成功==============");
}
}
package com.xynet.statistics.config.dataresources;

import java.lang.annotation.*;

/**
* 自定义注解,数据源指定
* Copyright © 2019 xynet Tech Ltd. All rights reserved
* @author: sund
* @date: 2019年3月23日 下午4:37:16
* @remark:
*/
@Target({ ElementType.METHOD, ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface TargetDataSource {
String value();
}

将上面五个类建立好后在启动类中加入@Import({DynamicDataSourceRegister.class}),这个很关键不然加载不了多数据源,只会调用默认数据源

@EnableDiscoveryClient
@EnableHystrix
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker
@ServletComponentScan
@EnableRedisHttpSession
@EnableTransactionManagement
@EnableFeignClients
@EnableScheduling
@MapperScan(basePackages = "com.xynet.statistics.dao")
@Import({DynamicDataSourceRegister.class})
public class XynetServiceStatisticsApplication { public static void main(String[] args) {
SpringApplication.run(XynetServiceStatisticsApplication.class, args);
} @Bean
public AsyncTaskExecutor paraTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setThreadNamePrefix("statistics-paraTaskExecutor-Executor");
executor.setCorePoolSize(24);
executor.setQueueCapacity(100);
executor.setMaxPoolSize(500);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy()); executor.initialize();
/*
* // 设置拒绝策略 executor.setRejectedExecutionHandler(new
* RejectedExecutionHandler() {
*
* @Override public void rejectedExecution(Runnable r,
* ThreadPoolExecutor executor) { // ..... } }); // 使用预定义的异常处理类
* executor.setRejectedExecutionHandler(new
* ThreadPoolExecutor.CallerRunsPolicy());
*/
return executor;
}
}

数据源切换注解要加到service上不要加到Mapper中上否则不会生效


@Service
public class BigDataServiceImpl implements BigDataService {

    @Override
@TargetDataSource("slave1")
public List<T_jq_jqxx> selectJqbhByShbh(String shbh) {
return t_jq_jqxxMapper.selectJqbhByShbh(shbh);
}

application-dev.yaml 配置文件如下:

spring:
profiles: dev
#mysql
datasource:
type: com.alibaba.druid.pool.DruidDataSource
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT NOW()
testWhileIdle: true
testOnBorrow: true
testOnReturn: false
poolPreparedStatements: true
maxPoolPreparedStatementPerConnectionSize: 20
filters: stat,log4j
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
driverClassName: com.mysql.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://192.168.1.253:3306/xy-platform?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false #jpa
jpa:
show-sql: true
open-in-view: true #redis
redis:
database: 15
host: 192.168.1.253
password:
port: 6379
pool:
min-idle: 1
max-idle: 8
max-active: 100
max-wait: 1
timeout: 10000 custom:
datasource:
names: slave1,slave2
slave1:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://192.168.1.253:3306/test?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false
slave2:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.jdbc.Driver
username: root
password: 1234
url: jdbc:mysql://192.168.1.253:3306/sy?characterEncoding=utf-8&serverTimezone=GMT%2B8&useSSL=false #log
logging:
level:
root: INFO
com.xynet: DEBUG
file: logs/service-statistics.log #server
server:
port: 8081 app:
fileHome: C:\
tempFileHome: tmp/
fileDownloadUrl: C:\ auth:
#url: http://localhost:7011/remote/authRemoteService
url: http://192.168.1.253:8899/authentication/remote/authRemoteService eureka:
client:
#service-url.defaultZone: http://localhost:8761/eureka
service-url.defaultZone: http://admin:123@192.168.1.253:7010/eureka enabled: true
registerWithEureka: true
fetchRegistry: true
healthcheck.enabled: true instance:
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 5
prefer-ip-address: true
instance-id: ${spring.cloud.client.ipAddress}:${server.port}

最上附上项目结构图:标红的地方为要修改的地方

spring boot mybatis 多数据源配置的更多相关文章

  1. spring boot Mybatis多数据源配置

    关于 有时候,随着业务的发展,项目关联的数据来源会变得越来越复杂,使用的数据库会比较分散,这个时候就会采用多数据源的方式来获取数据.另外,多数据源也有其他好处,例如分布式数据库的读写分离,集成多种数据 ...

  2. Spring Boot + Mybatis 多数据源配置实现读写分离

    本文来自网易云社区 作者:王超 应用场景:项目中有一些报表统计与查询功能,对数据实时性要求不高,因此考虑对报表的统计与查询去操作slave db,减少对master的压力. 根据网上多份资料测试发现总 ...

  3. spring boot mybatis多多数据源解决方法

    在我们的项目中不免会遇到需要在一个项目中使用多个数据源的问题,像我在得到一个任务将用户的聊天记录进行迁移的时候,就是用到了三个数据源,当时使用的AOP的编程方式根据访问的方法的不同进行动态的切换数据源 ...

  4. spring boot +mybatis(通过properties配置) 集成

    注:日常学习记录贴,下面描述的有误解的话请指出,大家一同学习. 因为我公司现在用的是postgresql数据库,所以我也用postgresql进行测试 一.前言 1.Spring boot 会默认读取 ...

  5. Spring MVC+Mybatis 多数据源配置

    文章来自:https://www.jianshu.com/p/fddcc1a6b2d8 1. 继承AbstractRoutingDataSource AbstractRoutingDataSource ...

  6. spring boot jpa 多数据源配置

    在实际项目中往往会使用2个数据源,这个时候就需要做额外的配置了.下面的配置在2.0.1.RELEASE 测试通过 1.配置文件 配置两个数据源 spring.datasource.url=jdbc:m ...

  7. spring boot + mybatis + druid配置实践

    最近开始搭建spring boot工程,将自身实践分享出来,本文将讲述spring boot + mybatis + druid的配置方案. pom.xml需要引入mybatis 启动依赖: < ...

  8. Spring Boot 2.X(五):MyBatis 多数据源配置

    前言 MyBatis 多数据源配置,最近在项目建设中,需要在原有系统上扩展一个新的业务模块,特意将数据库分库,以便减少复杂度.本文直接以简单的代码示例,如何对 MyBatis 多数据源配置. 准备 创 ...

  9. Spring Boot + Mybatis 配置多数据源

    Spring Boot + Mybatis 配置多数据源 Mybatis拦截器,字段名大写转小写 package com.sgcc.tysj.s.common.mybatis; import java ...

随机推荐

  1. alertdialog 设置最大高度

    设置最大高度,有很多方法,我个人比较喜欢的是下面这种方式 ,这里的view即添加到 view.addOnLayoutChangeListener(new View.OnLayoutChangeList ...

  2. soj1564. HOUSING

    1564. HOUSING Constraints Time Limit: 1 secs, Memory Limit: 32 MB Description For the Youth Olympic ...

  3. 解析html和采集网页的神兵利器

    HtmlAgilityPack是一个基于.Net的.第三方免费开源的微型类库,主要用于在服务器端解析html文档(在B/S结构的程序中客户端可以用Javascript解析html).截止到本文发表时, ...

  4. 20155306 2016-2017-2 《Java程序设计》第七周学习总结

    20155306 2016-2017-2 <Java程序设计>第七周学习总结 教材学习内容总结 第十三章 时间与日期 三种时间: 格林威治标准时间(GMT)的正午是太阳抵达天空最高点之时, ...

  5. 对于最近一星期jsp培训有感

    这周上课是jsp的培训,找的是外面的一个培训机构的讲师,人挺好的,讲的也不错,不过交给我们的任务是一星期做出一个网站模型——新闻发布系统. 由于我们组本科学计算机的人实在太少了,所以这个重担落在我的身 ...

  6. 蓝牙4.0 BLE入门

    在BLE协议中有两个角色,一个是周边(Periphery),另外一个是中央(Central).一个中央可以同时连接多个周边,但一个周边某一时刻只能连接一个中央.但是不管periphery还是centr ...

  7. HDU 3790 最短路径问题 (最短路)

    题目链接 Problem Description 给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的. ...

  8. Ubuntu 14.04 Nvidia显卡驱动安装及设置

    更换主板修复grub 引导后,无法从Nvidia进入系统(光标闪烁), 可能是显卡驱动出了问题. 1. 进入BIOS设置, 从集成显卡进入系统 将显示器连接到集显的VGI口, 并在BIOS中设置用集显 ...

  9. vue-cli环境搭建初探!

    1.先安装nodejs环境 https://npm.taobao.org/mirrors/node (选择版本) 下一步 下一步 默认安装就行 2.检查node和npm的是否成功安装 node -v ...

  10. HaoZipC不是内部或外部命令

    Win7专业版,32位,HaoZip V3.2 将安装命令下HaoZipC.exe,HaoZip.dll,lang\HaoZipLang_chs.dll三个文件拷贝到C:\Windows\System ...