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

一、基础介绍

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

二、项目目录截图

83182d599ceb8ddc13872cbb7510b4ae.png

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

6a72fbe6aa87c01358a0dffe40bd1129.png

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

四、配置编码

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

/**

* 数据源自定义注解*/

@Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documented

@Inherited

public @interfaceDataSource {

DataSourcesType name()defaultDataSourcesType.MASTER;

}

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

/**

* 数据源类型*/

publicenum 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:123456slave:

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:900000validationQuery:SELECT 1 FROMDUAL

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")publicclass DataSourceProperties {

privateintinitialSize;

privateintminIdle;

privateintmaxActive;

privateintmaxWait;

privateinttimeBetweenEvictionRunsMillis;

privateintminEvictableIdleTimeMillis;

privateintmaxEvictableIdleTimeMillis;

private String validationQuery;

private boolean testWhileIdle;

private boolean testOnBorrow;

private boolean testOnReturn;publicDruidDataSource 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);returndatasource;

}

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

/**

* 数据源切换处理*/

publicclass DynamicDataSourceContextHolder {public static final Logger log =LoggerFactory.getLogger(DynamicDataSourceContextHolder.class);/**

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

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

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

contextHolder.set(dataSourceType);

}/**

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

publicstatic String getDataSourceType() {returncontextHolder.get();

}/**

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

publicstatic void removeDataSourceType() {

contextHolder.remove();

}

}

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

/**

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

publicclass DynamicDataSource extends AbstractRoutingDataSource {publicstatic DynamicDataSource build() {returnnew DynamicDataSource();

}/**

* 获取与数据源相关的key

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

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

@Overrideprotected Object determineCurrentLookupKey() {returnDynamicDataSourceContextHolder.getDataSourceType();

}

}

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

/**

* 数据源配置类*/

@Configuration

publicclass DataSourceConfiguration {/**

* 主库*/

@Bean

@ConfigurationProperties("spring.datasource.druid.master")publicDataSource 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")publicDataSource slaveDataSource(DataSourceProperties dataSourceProperties) {return dataSourceProperties.setDataSource(DruidDataSourceBuilder.create().build());

}/**

* 设置数据源*/

@Bean(name ="dynamicDataSource")@Primary

publicDynamicDataSource 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();returndynamicDataSource;

}

}

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

@Aspect

@Component

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

publicclass DynamicDataSourceAspect {

protected Logger logger=LoggerFactory.getLogger(getClass());@Pointcut("@annotation(com.fuzongle.tankboot.common.annotation.DataSource)"+ "|| @within(com.fuzongle.tankboot.common.annotation.DataSource)")publicvoid dsPointCut() {

}@Around("dsPointCut()")publicObject 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 {returnpoint.proceed();

}

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

DynamicDataSourceContextHolder.removeDataSourceType();

}

}/**

* 获取目标方法*/private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {

Signature signature=pjp.getSignature();

MethodSignature methodSignature=(MethodSignature) signature;

Method agentMethod=methodSignature.getMethod();returnpjp.getTarget().getClass().getMethod(agentMethod.getName(), agentMethod.getParameterTypes());

}

}

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

8edbc15dd9fb071ef06746946a0600b4.png

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

359a487164498cea8a9f5139cdf4467d.png

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

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

注意:

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

2.技术交流群QQ:422167709。

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

0f9ab34ef6bb273c541672d80902d9a9.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值