写点什么

自定义 MyBatis 拦截器更改表名

作者:emanjusaka
  • 2023-10-23
    湖南
  • 本文字数:2822 字

    阅读完需:约 9 分钟

自定义MyBatis拦截器更改表名

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​​接口,重写一下的方法:

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

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


注意需要在 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;
@SpringBootTestclass 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 的编程栈


用户头像

emanjusaka

关注

还未添加个人签名 2022-04-29 加入

分享技术,共同成长。一个编程菜鸟的升级打怪之路~

评论

发布
暂无评论
自定义MyBatis拦截器更改表名_Java_emanjusaka_InfoQ写作社区