python 多数据源切换_Spring-Boot 多数据源配置+动态数据源切换+多数据源事物配置实现主从数据库存储分离...

一、基础介绍

多数据源字面意思,比如说二个数据库,甚至不同类型的数据库。在用SpringBoot开发项目时,随着业务量的扩大,我们通常会进行数据库拆分或是引入其他数据库,从而我们需要配置多个数据源。

二、项目目录截图

三、多数据源SQL结构设计如下(简单的主从关系):

PS:创建两个库用于搭建项目中主从使用不同的数据库,表可以随意定义。

四、配置编码

1.数据源自定义注解,DataSource.java

/**

* 数据源自定义注解

*/

@Target({ ElementType.METHOD, ElementType.TYPE })

@Retention(RetentionPolicy.RUNTIME)

@Documented

@Inherited

public @interface DataSource {

DataSourcesType name() default DataSourcesType.MASTER;

}

2.数据源类型枚举类定义,DataSourcesType.java

/**

* 数据源类型

*/

public enum DataSourcesType {

/**

* 主库

*/

MASTER,

/**

* 从库

*/

SLAVE

}

3.多数据源application.yml配置文件配置

# 数据源配置

spring:

datasource:

type: com.alibaba.druid.pool.DruidDataSource

driverClassName: com.mysql.cj.jdbc.Driver

druid:

master:

url: jdbc:mysql://127.0.0.1:3306/master?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8

username: root

password: 123456

slave:

enable: true

url: jdbc:mysql://127.0.0.1:3306/slave?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8

username: root

password: 123456

# 初始连接数

initialSize: 5

# 最小连接池数量

minIdle: 10

# 最大连接池数量

maxActive: 20

# 配置获取连接等待超时的时间

maxWait: 60000

# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒

timeBetweenEvictionRunsMillis: 60000

# 配置一个连接在池中最小生存的时间,单位是毫秒

minEvictableIdleTimeMillis: 300000

# 配置一个连接在池中最大生存的时间,单位是毫秒

maxEvictableIdleTimeMillis: 900000

validationQuery: SELECT 1 FROM DUAL

testWhileIdle: true

testOnBorrow: false

testOnReturn: false

# 打开PSCache,并且指定每个连接上PSCache的大小

poolPreparedStatements: true

maxPoolPreparedStatementPerConnectionSize: 20

# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙,此处是filter修改的地方

filters:

commons-log.connection-logger-name: stat,wall,log4j

# 通过connectProperties属性来打开mergeSql功能;慢SQL记录

connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000

# 合并多个DruidDataSource的监控数据

useGlobalDataSourceStat: true

# 配置 DruidStatFilter

web-stat-filter:

enabled: true

url-pattern: /*

exclusions: .js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*

stat-view-servlet:

enabled: true

url-pattern: /druid/*

# IP 白名单,没有配置或者为空,则允许所有访问

allow: 127.0.0.1

# IP 黑名单,若白名单也存在,则优先使用

deny: 192.168.31.253

# 禁用 HTML 中 Reset All 按钮

reset-enable: false

# 登录用户名/密码

login-username: root

login-password: 123

# 慢SQL记录

filter:

stat:

enabled: true

# 慢SQL记录

log-slow-sql: true

slow-sql-millis: 1000

merge-sql: true

wall:

config:

multi-statement-allow: true

4.数据源配置文件属性定义,DataSourceProperties.java

/**

* 数据源配置文件

*/

@Setter

@Configuration

@ConfigurationProperties(prefix = "spring.datasource.druid")

public class DataSourceProperties {

private int initialSize;

private int minIdle;

private int maxActive;

private int maxWait;

private int timeBetweenEvictionRunsMillis;

private int minEvictableIdleTimeMillis;

private int maxEvictableIdleTimeMillis;

private String validationQuery;

private boolean testWhileIdle;

private boolean testOnBorrow;

private boolean testOnReturn;

public DruidDataSource setDataSource(DruidDataSource datasource) {

datasource.setInitialSize(initialSize);

/** 配置初始化大小、最小、最大 */

datasource.setInitialSize(initialSize);

datasource.setMaxActive(maxActive);

datasource.setMinIdle(minIdle);

/** 配置获取连接等待超时的时间 */

datasource.setMaxWait(maxWait);

/** 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 */

datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);

/** 配置一个连接在池中最小、最大生存的时间,单位是毫秒 */

datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);

datasource.setMaxEvictableIdleTimeMillis(maxEvictableIdleTimeMillis);

/**

* 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。

*/

datasource.setValidationQuery(validationQuery);

/** 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。 */

datasource.setTestWhileIdle(testWhileIdle);

/** 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */

datasource.setTestOnBorrow(testOnBorrow);

/** 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 */

datasource.setTestOnReturn(testOnReturn);

return datasource;

}

5.多数据源切换处理,DynamicDataSourceContextHolder.java

/**

* 数据源切换处理

*/

public class DynamicDataSourceContextHolder {

public static final Logger log = LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);

/**

*此类提供线程局部变量。这些变量不同于它们的正常对应关系是每个线程访问一个线程(通过get、set方法),有自己的独立初始化变量的副本。

*/

private static final ThreadLocal contextHolder = new ThreadLocal<>();

/**

* 设置当前线程的数据源变量

*/

public static void setDataSourceType(String dataSourceType) {

log.info("已切换到{}数据源", dataSourceType);

contextHolder.set(dataSourceType);

}

/**

* 获取当前线程的数据源变量

*/

public static String getDataSourceType() {

return contextHolder.get();

}

/**

* 删除与当前线程绑定的数据源变量

*/

public static void removeDataSourceType() {

contextHolder.remove();

}

}

6.获取数据源(依赖于 spring) 定义一个类继承AbstractRoutingDataSource实现determineCurrentLookupKey方法,该方法可以实现数据库的动态切换,DynamicDataSource.java

/**

* 获取数据源(依赖于 spring) 定义一个类继承AbstractRoutingDataSource实现determineCurrentLookupKey方法,该方法可以实现数据库的动态切换

*/

public class DynamicDataSource extends AbstractRoutingDataSource {

public static DynamicDataSource build() {

return new DynamicDataSource();

}

/**

* 获取与数据源相关的key

* 此key是Map resolvedDataSources 中与数据源绑定的key值

* 在通过determineTargetDataSource获取目标数据源时使用

*/

@Override

protected Object determineCurrentLookupKey() {

return DynamicDataSourceContextHolder.getDataSourceType();

}

}

7.数据源核心配置类,DataSourceConfiguration.java

/**

* 数据源配置类

*/

@Configuration

public class DataSourceConfiguration {

/**

* 主库

*/

@Bean

@ConfigurationProperties("spring.datasource.druid.master")

public DataSource masterDataSource(DataSourceProperties dataSourceProperties) {

return dataSourceProperties.setDataSource(DruidDataSourceBuilder.create().build());

}

/**

* 从库

*/

@Bean

@ConditionalOnProperty( prefix = "spring.datasource.druid.slave", name = "enable", havingValue = "true")//是否开启数据源开关---若不开启 默认适用默认数据源

@ConfigurationProperties("spring.datasource.druid.slave")

public DataSource slaveDataSource(DataSourceProperties dataSourceProperties) {

return dataSourceProperties.setDataSource(DruidDataSourceBuilder.create().build());

}

/**

* 设置数据源

*/

@Bean(name = "dynamicDataSource")

@Primary

public DynamicDataSource dynamicDataSource(DataSource masterDataSource, DataSource slaveDataSource) {

Map targetDataSources = new HashMap<>();

DynamicDataSource dynamicDataSource = DynamicDataSource.build();

targetDataSources.put(DataSourcesType.MASTER.name(), masterDataSource);

targetDataSources.put(DataSourcesType.SLAVE.name(), slaveDataSource);

//默认数据源配置 DefaultTargetDataSource

dynamicDataSource.setDefaultTargetDataSource(masterDataSource);

//额外数据源配置 TargetDataSources

dynamicDataSource.setTargetDataSources(targetDataSources);

dynamicDataSource.afterPropertiesSet();

return dynamicDataSource;

}

}

8.多数据源切面配置类,用于获取注解上的注解,进行动态切换数据源,DynamicDataSourceAspect.java

@Aspect

@Component

@Order(-1) // 保证该AOP在@Transactional之前执行

public class DynamicDataSourceAspect {

protected Logger logger = LoggerFactory.getLogger(getClass());

@Pointcut("@annotation(com.fuzongle.tankboot.common.annotation.DataSource)"

+ "|| @within(com.fuzongle.tankboot.common.annotation.DataSource)")

public void dsPointCut() {

}

@Around("dsPointCut()")

public Object around(ProceedingJoinPoint point) throws Throwable {

Method targetMethod = this.getTargetMethod(point);

DataSource dataSource = targetMethod.getAnnotation(DataSource.class);//获取要切换的数据源

if (dataSource != null) {

DynamicDataSourceContextHolder.setDataSourceType(dataSource.name().name());

}

try {

return point.proceed();

}

finally {

// 销毁数据源 在执行方法之后

DynamicDataSourceContextHolder.removeDataSourceType();

}

}

/**

* 获取目标方法

*/

private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {

Signature signature = pjp.getSignature();

MethodSignature methodSignature = (MethodSignature) signature;

Method agentMethod = methodSignature.getMethod();

return pjp.getTarget().getClass().getMethod(agentMethod.getName(), agentMethod.getParameterTypes());

}

}

9.编写业务逻辑,切换从库查询数据。

10.编写测试方法,调用查询业务,查看是否切换数据源是否生效。

PS:这种多数据源的动态切换确实可以解决数据的主从分库操作,但是却有一个致命的BUG,那就是事务不但失效而且无法实现

一致性,因为涉及到跨库,因此我们必须另想办法来实现事务的ACID原则

注意:

1.如果有任何不懂的地方可以关注公众号就可以加我微信,随时欢迎互相帮助。

2.技术交流群QQ:422167709。

3.如果希望学习更多,希望微信扫码,长按扫码,帮忙关注一下,举手之劳,当您无助的时候真的能帮你。非常感谢您关注公众号 "编程小乐"。

Spring Boot 揭秘与实战 自己实现一个简单的自动配置模块

文章目录 1. 实战的开端 – Maven搭建 2. 参数的配置 - 属性参数类 3. 真的很简单 - 简单的服务类 4. 自动配置的核心 - 自动配置类 5. spring.factories 不要 ...

Spring Boot干货系列:(七)默认日志框架配置

Spring Boot干货系列:(七)默认日志框架配置 原创 2017-04-05 嘟嘟MD 嘟爷java超神学堂 前言 今天来介绍下Spring Boot如何配置日志logback,我刚学习的时候, ...

Spring Boot 项目学习 (二) MySql + MyBatis 注解 + 分页控件 配置

0 引言 本文主要在Spring Boot 基础项目的基础上,添加 Mysql .MyBatis(注解方式)与 分页控件 的配置,用于协助完成数据库操作. 1 创建数据表 这个过程就暂时省略了. 2 ...

Spring Boot教程(三十八)使用MyBatis注解配置详解(1)

之前在Spring Boot中整合MyBatis时,采用了注解的配置方式,相信很多人还是比较喜欢这种优雅的方式的,也收到不少读者朋友的反馈和问题,主要集中于针对各种场景下注解如何使用,下面就对几种常见 ...

Spring Boot 2.x基础教程:使用MyBatis的XML配置方式

上一篇我们介绍了如何在Spring Boot中整合我们国人最常用的MyBatis来实现对关系型数据库的访问.但是上一篇中使用了注解方式来实现,而对于很多MyBatis老用户还是习惯于XML的开发方式, ...

Spring Boot + Mybatis多数据源和动态数据源配置

文章转自 https://blog.csdn.net/neosmith/article/details/61202084 网上的文章基本上都是只有多数据源或只有动态数据源,而最近的项目需要同时使用两种 ...

Spring Boot:实现MyBatis动态数据源

综合概述 在很多具体应用场景中,我们需要用到动态数据源的情况,比如多租户的场景,系统登录时需要根据用户信息切换到用户对应的数据库.又比如业务A要访问A数据库,业务B要访问B数据库等,都可以使用动态数据 ...

学习Spring Boot:(二十四)多数据源配置与使用

前言 随着业务量增大,可能有些业务不是放在同一个数据库中,所以系统有需求使用多个数据库完成业务需求,我们需要配置多个数据源,从而进行操作不同数据库中数据. 正文 JdbcTemplate 多数据源 配 ...

Spring Boot教程(三十二)多数据源配置与使用

之前在介绍使用JdbcTemplate和Spring-data-jpa时,都使用了单数据源.在单数据源的情况下,Spring Boot的配置非常简单,只需要在application.propertie ...

随机推荐

python网络编程【一】

TCP/IP 是标准的协议,它可以使用世界范围内的计算机通过Internet或本地的网络通信 1.编写一个TCP客户端程序 #!/usr/bin/env python import socket, s ...

MMORPG大型游戏设计与开发(客户端架构 part8 of vegine)

脚本模块是游戏设计中争论比较多的话题,那是因为作为脚本本身所带来的利弊.其实这都无关紧要,取舍是人必须学会的一项技能,如果你不会取舍那么就让趋势给你一个满意的答复.自从魔兽世界以及传奇(世界)问世以来 ...

java 进程启用远程查看

用jconsole 1.启动脚本增加: JAVA_OPTS="-Djava.net.preferIPv4Stack=true -Djava.rmi.server.hostname=192.1 ...

andriod arcgis保存Mapview为图片

/** * 把一个View的对象转换成bitmap */ private Bitmap getViewBitmap(MapView v) { v.clearFocus(); v.setPressed( ...

模拟退火算法-[HDU1109]

模拟退火算法的原理模拟退火算法来源于固体退火原理,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到 ...

iOS 自定义UITabBarController的tabBar

#import @interface AppDelegate : UIResponder

centos 安装sphinx

官网下载 :http://sphinxsearch.com/downloads/sphinx-2.2.10-release.tar.gz/thankyou.html 安装sphinx 解压 tar z ...

jquery实现页面局部刷新

后台管理中总是使用frameset进行分成部分进行管理,但是感觉很不好用,尤其是页面间调转还要判断window.parent,太令我费神了,于是学习使用XMLHttpRequest进行页面局部刷新.代 ...

String和StringBuffer 常用方法总结

String和StringBuffer 常用方法总结 一.不可变长度String 1.字符串---->char数组 char[] chars=str.toCharArray(); 2.字符串中 ...

LeetCode-Valid Number - 有限状态机

判断合法数字,之前好像在哪里看到过这题, 记得当时还写了好久,反正各种改, 今天看到了大神的解法(https://github.com/fuwutu/LeetCode/blob/master/Vali ...

你可能感兴趣的:(python,多数据源切换)