自定义mybatis插件实现读写分离

这篇具有很好参考价值的文章主要介绍了自定义mybatis插件实现读写分离。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

欢迎来到我的博客,代码的世界里,每一行都是一个故事


自定义mybatis插件实现读写分离,# mysql,mybatis,插件,读写分离,动态数据源

有时候我更想看到的是bug,比如做这个插件的时候

前言

在数据库的世界里,读写分离就像是一场神奇的变形术表演,能够让我们的应用程序更加稳定和高效。而MyBatis插件就像是一把神奇的魔杖,能够帮助我们实现数据库的读写分离。它就像是一位魔术师,能够在不同的数据库之间灵活切换,让我们的应用程序如虎添翼。现在,就让我们一起来揭开MyBatis插件的神秘面纱,探索它的魅力所在吧!

场景分析

要实现读写分离我们首先应该具备以下条件

1、多数据源场景,且可以动态切换数据源

2、在mybatis创建连接之前切换到想要的数据源

3、需要执行规则实现读写分离

大致就是上面的三点

前置配置讲解

# 数据源配置
spring.datasource.mysql.primary.url=jdbc:mysql://127.0.0.1:3361/base_sb?nullDatabaseMeansCurrent=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
spring.datasource.mysql.primary.username=root
spring.datasource.mysql.primary.password=123456
spring.datasource.mysql.primary.driver-class-name=com.mysql.cj.jdbc.Driver
# 数据源配置
spring.datasource.mysql.slave1.url=jdbc:mysql://127.0.0.1:3351/dingding_mid?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.mysql.slave1.username=root
spring.datasource.mysql.slave1.password=123456
spring.datasource.mysql.slave1.driver-class-name=com.mysql.cj.jdbc.Driver
@Bean(name = DataSourceType.PRIMARY)
@ConfigurationProperties(prefix = "spring.datasource.mysql.primary")
public DataSource primaryDataSource() {
  log.info("主数据库连接池创建中.......");
  return DruidDataSourceBuilder.create().build();
}

@Bean(name = DataSourceType.SECOND)
@ConfigurationProperties(prefix = "spring.datasource.mysql.slave1")
public DataSource secondDataSource() {
  log.info("second数据库连接池创建中.......");
  return DruidDataSourceBuilder.create().build();
}

上面是我的数据源,需要实现的就是当进行查询的时候我会走到slave1。

注意:我这里仅仅是为了展示效果,真正的读写分离是读库和写库一模一样,唯一的区别是读库read_only=1,也就是只读状态,并且他们的关系还是主从关系。

数据源切换实现

springboot整合多数据源的配置以及动态切换数据源,注解切换数据源

代码实现(插件)

package com.todoitbo.baseSpringbootDasmart.interceptor;

import com.todoitbo.baseSpringbootDasmart.multiDataSource.DataSourceContextHolder;
import com.todoitbo.baseSpringbootDasmart.multiDataSource.DataSourceType;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.sql.Connection;
import java.util.Properties;

/**
 * @author xiaobo
 */
@Intercepts({
    @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
@Component
public class RoutingInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

        // 使用MetaObject获取MappedStatement
        MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
        while (metaObject.hasGetter("h")) {
            Object object = metaObject.getValue("h");
            metaObject = SystemMetaObject.forObject(object);
        }
        while (metaObject.hasGetter("target")) {
            Object object = metaObject.getValue("target");
            metaObject = SystemMetaObject.forObject(object);
        }
        // 通过反射获取到当前MappedStatement高版本没这个类了
        // MappedStatement mappedStatement = (MappedStatement) MetaObjectUtils.getFieldValue(statementHandler, "delegate.mappedStatement");
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
        String sqlCommandType = mappedStatement.getSqlCommandType().toString();
        
        // 根据SQL命令类型,动态切换数据源
        if ("SELECT".equals(sqlCommandType)) {
            // 设置为数据库1的连接
            DataSourceContextHolder.setDataSource(DataSourceType.SECOND);
        } else {
            // 设置为数据库2的连接
            DataSourceContextHolder.setDataSource(DataSourceType.PRIMARY);
        }

        // 继续执行原有逻辑
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        // 当目标类是StatementHandler类型时,才包装目标类,否则直接返回
        if (target instanceof StatementHandler) {
            return Plugin.wrap(target, this);
        } else {
            return target;
        }
    }

    @Override
    public void setProperties(Properties properties) {
        // 这里可以接收到配置文件中的属性
    }
}

说明

当然,让我们逐一解析 RoutingInterceptor 类的主要方法:

  1. intercept(Invocation invocation): 这是拦截器的核心方法,当被拦截的方法(在本例中是 StatementHandlerprepare 方法)被调用时,这个方法会被执行。

    在这个方法中,首先获取了 StatementHandler 对象,然后通过 MetaObject 获取了 MappedStatement 对象。根据 MappedStatement 中的 sqlCommandType 判断当前执行的 SQL 是查询还是非查询,然后用 DataSourceContextHolder.setDataSource() 方法动态设置数据源,最后调用 invocation.proceed() 继续执行原有逻辑。

  2. plugin(Object target): 这个方法用于包装目标对象。当目标对象是 StatementHandler 类型时,使用 Plugin.wrap(target, this) 方法包装目标对象,这样当目标对象的方法被调用时,会先调用 intercept 方法。如果目标对象不是 StatementHandler 类型,直接返回目标对象。

  3. setProperties(Properties properties): 这个方法可以用于从配置文件中接收属性,但在这个拦截器中并未使用。

以上就是 RoutingInterceptor 类的主要方法。这个类实现了 MyBatis 的 Interceptor 接口,通过 @Intercepts@Signature 注解指定了要拦截的方法,然后在 intercept 方法中实现了动态数据源路由的逻辑。

注意

重点提一下需要注意的点

如果你除了这个拦截插件用到切换数据源之外还有别的,比如上面提到的数据源的切换,你定义了一个AOP,这个切点是service上,而你的这个service下又有数据库操作,那么这个很容易导致切换数据源失败

实现效果

自定义mybatis插件实现读写分离,# mysql,mybatis,插件,读写分离,动态数据源

因为我的AOP干扰整整解决了1个多小时,弱弱的说自己一句好菜。终于等到了这个异常,也就是我的查询走的表是另一个库的表,而这个库并没有这个表。大公告成,完美收工文章来源地址https://www.toymoban.com/news/detail-857003.html

到了这里,关于自定义mybatis插件实现读写分离的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 多数据源切换、读写分离-02

    使用dynamic进行数据源切换、读写分离 特性 1、支持数据源分组 ,适用于多种场景纯粹多库读写分离一主多从混合模式。 2、支持数据库敏感配置信息 加密 ENC()。 3、支持每个数据库独立初始化表结构schema和数据库database。 4、支持无数据源启动,支持懒加载数据源(需要的时

    2024年02月16日
    浏览(29)
  • spring boot shardingsphere mybatis-plus druid mysql 搭建mysql数据库读写分离架构

    ##关于window mysql主从搭建简单教程 传送门 window mysql5.7 搭建主从同步环境-CSDN博客 ##父pom.xml ##模块pom.xml ##yml配置 ##mapper.xml ##TestMapper ##TestService ##TestController ##浏览器访问 ##数据库

    2024年01月21日
    浏览(52)
  • 利用 Mybatis-Plus 的动态数据源实现多数据源配置

    目录 一、导入依赖 二、Application.yaml配置文件 三、切换数据源 四、其他方法 4.1 配置多个数据源 4.2 定义Datasource和EntityManager 4.3 在需要使用数据源的地方注入不同的EntityManager 官网:https://baomidou.com/pages/a61e1b/#dynamic-datasource 默认是使用配置文件中master参数设置的数据库。

    2024年02月13日
    浏览(32)
  • Spring Boot + MyBatis-Plus实现数据库读写分离

    🎉Spring Boot + MyBatis-Plus实现数据库读写分离 ☆* o(≧▽≦)o *☆嗨~我是IT·陈寒🍹 ✨博客主页:IT·陈寒的博客 🎈该系列文章专栏:架构设计 📜其他专栏:Java学习路线 Java面试技巧 Java实战项目 AIGC人工智能 数据结构学习 🍹文章作者技术和水平有限,如果文中出现错误,希望

    2024年02月05日
    浏览(54)
  • 若依分离版——配置多数据源(mysql和oracle),实现一个方法操作多个数据源

    目录 一、若依平台配置           二、编写oracle数据库访问的各类文件  三.  一个方法操作多个数据源 一、若依平台配置 1、在ruoyi-admin的pom.xml添加oracle依赖 2、引入ojdbc6.jar包 在ruoyi-admin的resource下创建lib文件夹,将ojdbc6.jar包保存在此目录下 3.  刷新maven 刷新maven保证ruo

    2024年02月05日
    浏览(32)
  • springboot+mybatis实现mysql和oracle多数据源

    在实际项目中很多时候会涉及到多个数据库的访问,或者数据库读写分离的形式。 下面通过使用 Aspect+注解来实现mysql+oracle的多数据源配置(注意:事务一致性未提供) 首先要去oracle官网下载ojdbc的jar包,根据oracle的版本去下载,或者在下载的oracle的jdbc包下的lib里面有,然后

    2024年02月07日
    浏览(44)
  • mysql面试题45:读写分离常见方案、哪些中间件可以实现读写分离

    该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 读写分离是一种常见的数据库架构方案,旨在分担数据库的读写压力,提高系统的性能和可扩展性。以下是两种常见的读写分离方案: 主从复制方案

    2024年02月07日
    浏览(32)
  • 项目实现读写分离操作(mysql)

    Master(主库)----(数据同步)— Slave(从库) Mysql主从复制 mysql主从复制 介绍 mysql主从复制是一个异步的复制过程,底层是基于mysql数据库自带的 二进制日志 功能。就是一台或多台mysql数据库(Slave,即从库)从另一台mysql数据库(Master,即主库)进行日志的复制然后再解析日

    2024年02月05日
    浏览(31)
  • mycat实现mysql读写分离

    mycat集群+Haproxy+Keepalived+mysql1主2从 环境规划 centos7.9 1主2从,读写分离 名称 ip 端口 mysql-master 192.168.1.220 3306 mysql-slave1 192.168.1.221 3306 mysql-slave2 192.168.1.222 3306 mycat-1 192.168.1.221 8066 mycat-2 192.168.1.222 8066 haproxy-1 192.168.1.221 18066 haproxy-2 192.168.1.222 18066 keepalived-1 192.168.1.221 keepalived-2 1

    2024年01月19日
    浏览(30)
  • laravel实现mysql读写分离(二)

    Laravel5 读写分离主要有两个过程: 第一步,根据 database.php 配置,创建写库和读库的链接 connection 第二步,调用 select 时先判断使用读库还是写库,而 insert/update/delete 统一使用写库 主要文件:Illuminate/Database/Connectors/ConnectionFactory.php 1:判断 database.php 是否配置了读写分离数据库

    2024年02月03日
    浏览(70)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包