by emanjusaka from ​ https://www.emanjusaka.top/archives/10 彼岸花开可奈何

本文欢迎分享与聚合,全文转载请留下原文地址。

自定义MyBatis拦截器可以在方法执行前后插入自己的逻辑,这非常有利于扩展和定制 MyBatis 的功能。本篇文章实现自定义一个拦截器去改变要插入或者查询的数据源。

@Intercepts

@Intercepts是Mybatis的一个注解,它的主要作用是标识一个类为拦截器。该注解通过一个@Signature注解(即拦截点),来指定拦截那个对象里面的某个方法。

具体来说,@Signature注解的属性type用于指定拦截器类型,可能的值包括:

  • Executor(sql的内部执行器)
  • ParameterHandler(拦截参数的处理)
  • StatementHandler(拦截sql的构建)
  • ResultSetHandler(拦截结果的处理)。

method属性表示在指定的拦截器类型中要拦截的方法

args属性表示拦截的方法对应的参数

实现步骤

  1. 实现org.apache.ibatis.plugin.Interceptor接口,重写一下的方法:

  2. 添加拦截器注解,@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})

  3. 配置文件中添加拦截器

    注意需要在 Spring Boot 的 application.yml 文件中配置 mybatis 配置文件的路径。

    mybatis拦截器目前不支持在application.yml配置文件中通过属性配置,目前只支持通过xml配置或者代码配置。

代码实现

Mybatis拦截器:

package top.emanjusaka.springboottest.mybatis.plugin;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import top.emanjusaka.springboottest.mybatis.annotation.DBTableStrategy; import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.regex.Matcher;
import java.util.regex.Pattern; /**
* @Author emanjusaka
* @Date 2023/10/18 17:25
* @Version 1.0
*/
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class DynamicMybatisPlugin implements Interceptor {
private Pattern pattern = Pattern.compile("(from|into|update)[\\s]{1,}(\\w{1,})", Pattern.CASE_INSENSITIVE); @Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
// 获取自定义注解判断是否进行分表操作
String id = mappedStatement.getId();
String className = id.substring(0, id.lastIndexOf("."));
Class<?> clazz = Class.forName(className);
DBTableStrategy dbTableStrategy = clazz.getAnnotation(DBTableStrategy.class);
if (null == dbTableStrategy || !dbTableStrategy.changeTable() || null == dbTableStrategy.tbIdx()) {
return invocation.proceed();
}
// 获取SQL
BoundSql boundSql = statementHandler.getBoundSql();
String sql = boundSql.getSql();
// 替换SQL表名
Matcher matcher = pattern.matcher(sql);
String tableName = null;
if (matcher.find()) {
tableName = matcher.group().trim();
}
assert null != tableName;
String replaceSql = matcher.replaceAll(tableName + "_" + dbTableStrategy.tbIdx());
// 通过反射修改SQL语句
Field field = boundSql.getClass().getDeclaredField("sql");
field.setAccessible(true);
field.set(boundSql, replaceSql);
field.setAccessible(false);
return invocation.proceed();
}
}

mapper的xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="top.emanjusaka.springboottest.score.repository.IScoreRepository">
<select id="selectAll" resultType="top.emanjusaka.springboottest.score.model.vo.ScoreVO">
select * from score
</select>
</mapper>

切换表名的注解:

package top.emanjusaka.springboottest.score.repository;

import org.apache.ibatis.annotations.Mapper;
import top.emanjusaka.springboottest.mybatis.annotation.DBTableStrategy;
import top.emanjusaka.springboottest.score.model.vo.ScoreVO; import java.util.List; /**
* @Author emanjusaka
* @Date 2023/10/18 17:45
* @Version 1.0
*/
@Mapper
@DBTableStrategy(changeTable = true,tbIdx = "2")
public interface IScoreRepository {
List<ScoreVO> selectAll();
}

测试代码:

package top.emanjusaka.springboottest;

import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import top.emanjusaka.springboottest.score.model.vo.ScoreVO;
import top.emanjusaka.springboottest.score.service.IScore; import javax.annotation.Resource;
import java.util.List; @SpringBootTest
class SpringBootTestApplicationTests {
@Resource
private IScore score;
@Test
void contextLoads() {
List<ScoreVO> list = score.selectAll();
list.forEach(System.out::println);
} }

运行结果

通过上图可以看出,现在表名已经修改成了score_2了。通过这种机制,我们可以应用到自动分表中。本文的表名的索引是通过注解参数传递的,实际应用中需要通过哈希散列计算。

本文原创,才疏学浅,如有纰漏,欢迎指正。如果本文对您有所帮助,欢迎点赞,并期待您的反馈交流,共同成长。

原文地址: https://www.emanjusaka.top/archives/10

微信公众号:emanjusaka的编程栈

自定义MyBatis拦截器更改表名的更多相关文章

  1. 基于mybatis拦截器分表实现

    1.拦截器简介 MyBatis提供了一种插件(plugin)的功能,但其实这是拦截器功能.基于这个拦截器我们可以选择在这些被拦截的方法执行前后加上某些逻辑或者在执行这些被拦截的方法时执行自己的逻辑. ...

  2. 玩转SpringBoot之整合Mybatis拦截器对数据库水平分表

    利用Mybatis拦截器对数据库水平分表 需求描述 当数据量比较多时,放在一个表中的时候会影响查询效率:或者数据的时效性只是当月有效的时候:这时我们就会涉及到数据库的分表操作了.当然,你也可以使用比较 ...

  3. Mybatis自定义SQL拦截器

    本博客介绍的是继承Mybatis提供的Interface接口,自定义拦截器,然后将项目中的sql拦截一下,打印到控制台. 先自定义一个拦截器 package com.muses.taoshop.com ...

  4. Mybatis拦截器实现分页

    本文介绍使用Mybatis拦截器,实现分页:并且在dao层,直接返回自定义的分页对象. 最终dao层结果: public interface ModelMapper { Page<Model&g ...

  5. "犯罪心理"解读Mybatis拦截器

    原文链接:"犯罪心理"解读Mybatis拦截器 Mybatis拦截器执行过程解析 文章写过之后,我觉得 "Mybatis 拦截器案件"背后一定还隐藏着某种设计动 ...

  6. 使用MyBatis拦截器后,摸鱼时间又长了。🐟

    场景 在后端服务开发时,现在很流行的框架组合就是SSM(SpringBoot + Spring + MyBatis),在我们进行一些业务系统开发时,会有很多的业务数据表,而表中的信息从新插入开始,整个 ...

  7. Mybatis拦截器介绍

    拦截器的一个作用就是我们可以拦截某些方法的调用,我们可以选择在这些被拦截的方法执行前后加上某些逻辑,也可以在执行这些被拦截的方法时执行自己的逻辑而不再执行被拦截的方法.Mybatis拦截器设计的一个初 ...

  8. Mybatis拦截器介绍及分页插件

    1.1    目录 1.1 目录 1.2 前言 1.3 Interceptor接口 1.4 注册拦截器 1.5 Mybatis可拦截的方法 1.6 利用拦截器进行分页 1.2     前言 拦截器的一 ...

  9. 详解Mybatis拦截器(从使用到源码)

    详解Mybatis拦截器(从使用到源码) MyBatis提供了一种插件(plugin)的功能,虽然叫做插件,但其实这是拦截器功能. 本文从配置到源码进行分析. 一.拦截器介绍 MyBatis 允许你在 ...

  10. Mybatis拦截器 mysql load data local 内存流处理

    Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所lo ...

随机推荐

  1. CentOS 8搭建Docker镜像私有仓库-registry

    目录 简介 安装Docker 添加docker yum源 安装docker 确保网络模块开机自动加载 使桥接流量对iptables可见 配置docker 验证docker是否正常 添加用户到docke ...

  2. SAS - PROC FCMP

    PROC FCMP 概述 PROC FCMP 可用于自定义函数(funcion)和子程序(subroutines).自定义函数和子程序的名称的最大长度为 32,长度超过 32 的名称虽然可以定义,但无 ...

  3. Java List集合根据某字段去重

    去重方法 单个字段为条件去重 /** * 单字段去重 * @param jackpotList1 新集合 * @param jackpotList 需要去重的集合 * @return */ priva ...

  4. 实现地图遮罩 leaflet

    1 前言 在地图中加载的底图是瓦片服务(固定大小的规则矩形),底图的范围很大,铺满了整个div,但是我们的感兴趣的部门可能只是其中一部分,如何在整个屏幕中突出感兴趣的部分-- 地图遮罩(遮挡图像中不感 ...

  5. 1. 通俗易懂的Redis基础

    通俗易懂的Redis基础教程(基于CentOS 7) 目录 通俗易懂的Redis基础教程(基于CentOS 7) 1 Redis是什么 1.1 NoSQL概念 1.2 NoSQL与SQL比较 1.3 ...

  6. WPF如何构建MVVM+模块化的桌面应用

    为何模块化 模块化是一种分治思想,不仅可以分离复杂的业务逻辑,还可以进行不同任务的分工.模块与模块之间相互独立,从而构建一种松耦合的应用程序,便于开发和维护. 开发技术 .Net 6 + WPF + ...

  7. 深入理解 Flutter 图片加载原理

    前言 随着Flutter稳定版本逐步迭代更新,京东APP内部的Flutter业务也日益增多,Flutter开发为我们提供了高效的开发环境.优秀的跨平台适配.丰富的功能组件及动画.接近原生的交互体验,但 ...

  8. 【pytorch】目标检测:新手也能彻底搞懂的YOLOv5详解

    YOLOv5是Glenn Jocher等人研发,它是Ultralytics公司的开源项目.YOLOv5根据参数量分为了n.s.m.l.x五种类型,其参数量依次上升,当然了其效果也是越来越好.从2020 ...

  9. 【日常踩坑】解决 pip 安装第三方包时因 SSL 报错

    目录 踩坑 什么是 SSL ? 为什么会报错 解决办法 1. 临时关闭代理.VPN 或者网络抓包等软件 2. 通过镜像的 HTTP 源来避免 SSL 认证问题 3. 切换至低版本 pip 参考资料 踩 ...

  10. Tarjan基础用法

    \(\operatorname{Tarjan}\) 基础用法 目录 \(\operatorname{Tarjan}\) 基础用法 \(\operatorname{Tarjan}\) 求最近公共祖先 前 ...