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. java.lang.reflect.UndeclaredThrowableException

    org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.Persiste ...

  2. hexo博客主题,git上传,报错Template render error的解决方案

    报错信息 INFO Start processing FATAL Something's wrong. Maybe you can find the solution here: http://hex ...

  3. PWM点灯

    目录 PWM脉冲宽调点灯 前言 1.什么是PWM 2.PWM的实现 3.PWM实现步骤(通用定时器) 3.1 打开定时器的时钟 3.2 配置端口 3.3 设置定时器 3.4 设置PWM 3.5 完整代 ...

  4. LCD与OLED的相爱相杀

    目前市面的显示技术主要分为LCD与OLED. 本文主要记录对LCD与OLED的学习. 导言:介绍一些专业名词和术语. 像素点:是指在由一个数字序列表示的图像中的一个最小单位,称为像素. 一张图片在显示 ...

  5. 2021-6-17 plc连接

    using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...

  6. 清理MySQL的binlog历史文件

    前言 系统版本:centos 7 MySQL版本:5.7 mysql的binlog文件最好不要手动删,避免删错导致bin log同步异常. 步骤 查看当前的binlog文件 show binary l ...

  7. Amiya 前端UI

    最近在使用一个基于Ant Design 二次封装的组件 Git文档地址 Index - Amiya (gitee.io)

  8. 关于Vue的就地更新策略的解析

    在Vue中使用v-for渲染列表时,默认使用就地更新策略.该策略默认是基于索引的,规定在列表绑定的数据元素顺序变化时,不会重新创建整个列表,而只是更新对应DOM元素上的数据.以下代码实现了一个TODO ...

  9. 无界AI绘画基础教程,和Midjourney以及Stable Diffusion哪个更好用?

    本教程收集于:AIGC从入门到精通教程汇总 简单的总结 Midjourney,Stable Diffusion,无界AI的区别? Midjourney,收费,上手容易,做出来高精度的图需要自己掌握好咒 ...

  10. Pandas 使用教程 JSON

    目录 JSON 转换为 CSV 简单 JSON 从 URL 中读取 JSON 数据: 字典转化为 DataFrame 数据 内嵌的 JSON 数据 复杂 JSON Pandas 可以很方便的处理 JS ...