• 自定义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接口,重写一下的方法:

    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:

    
    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 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 list = score.selectAll();
            list.forEach(System.out::println);
        }
    
    }
    
    

    运行结果

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

    本文原创,才疏学浅,如有纰漏,欢迎指正。如果本文对您有所帮助,欢迎点赞,并期待您的反馈交流,共同成长。
    原文地址: https://www.emanjusaka.top/archives/10
    微信公众号:emanjusaka的编程栈

  • 相关阅读:
    美团动态线程池实践思路,开源了
    定制SQLmap和WAF绕过
    什么是MogaFX外汇储备?定义和重要性
    智慧铁路:机车整备场数字孪生
    js设计模式:享元模式
    记录一次某MFC软件算法逆向之旅
    石头科技: 决胜百亿扫地机器人“价值高地”
    asp.net core之异常处理
    C 标准库 - <stdio.h> 详解
    蓝桥杯每日一题2023.9.27
  • 原文地址:https://www.cnblogs.com/emanjusaka/p/page_10.html