自定义MyBatis拦截器更改表名

打印 上一主题 下一主题

主题 907|帖子 907|积分 2721

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属性表示拦截的方法对应的参数
实现步骤


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

  • 添加拦截器注解,@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
  • 配置文件中添加拦截器

    注意需要在 Spring Boot 的 application.yml 文件中配置 mybatis 配置文件的路径。
    mybatis拦截器目前不支持在application.yml配置文件中通过属性配置,目前只支持通过xml配置或者代码配置。
代码实现

Mybatis拦截器:
  1. package top.emanjusaka.springboottest.mybatis.plugin;
  2. import org.apache.ibatis.executor.statement.StatementHandler;
  3. import org.apache.ibatis.mapping.BoundSql;
  4. import org.apache.ibatis.mapping.MappedStatement;
  5. import org.apache.ibatis.plugin.Interceptor;
  6. import org.apache.ibatis.plugin.Intercepts;
  7. import org.apache.ibatis.plugin.Invocation;
  8. import org.apache.ibatis.plugin.Signature;
  9. import org.apache.ibatis.reflection.DefaultReflectorFactory;
  10. import org.apache.ibatis.reflection.MetaObject;
  11. import org.apache.ibatis.reflection.SystemMetaObject;
  12. import top.emanjusaka.springboottest.mybatis.annotation.DBTableStrategy;
  13. import java.lang.reflect.Field;
  14. import java.sql.Connection;
  15. import java.util.regex.Matcher;
  16. import java.util.regex.Pattern;
  17. /**
  18. * @Author emanjusaka
  19. * @Date 2023/10/18 17:25
  20. * @Version 1.0
  21. */
  22. @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
  23. public class DynamicMybatisPlugin implements Interceptor {
  24.     private Pattern pattern = Pattern.compile("(from|into|update)[\\s]{1,}(\\w{1,})", Pattern.CASE_INSENSITIVE);
  25.     @Override
  26.     public Object intercept(Invocation invocation) throws Throwable {
  27.         StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
  28.         MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
  29.         MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
  30.         // 获取自定义注解判断是否进行分表操作
  31.         String id = mappedStatement.getId();
  32.         String className = id.substring(0, id.lastIndexOf("."));
  33.         Class<?> clazz = Class.forName(className);
  34.         DBTableStrategy dbTableStrategy = clazz.getAnnotation(DBTableStrategy.class);
  35.         if (null == dbTableStrategy || !dbTableStrategy.changeTable() || null == dbTableStrategy.tbIdx()) {
  36.             return invocation.proceed();
  37.         }
  38.         // 获取SQL
  39.         BoundSql boundSql = statementHandler.getBoundSql();
  40.         String sql = boundSql.getSql();
  41.         // 替换SQL表名
  42.         Matcher matcher = pattern.matcher(sql);
  43.         String tableName = null;
  44.         if (matcher.find()) {
  45.             tableName = matcher.group().trim();
  46.         }
  47.         assert null != tableName;
  48.         String replaceSql = matcher.replaceAll(tableName + "_" + dbTableStrategy.tbIdx());
  49.         // 通过反射修改SQL语句
  50.         Field field = boundSql.getClass().getDeclaredField("sql");
  51.         field.setAccessible(true);
  52.         field.set(boundSql, replaceSql);
  53.         field.setAccessible(false);
  54.         return invocation.proceed();
  55.     }
  56. }
复制代码
mapper的xml:
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  3.         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  4. <mapper namespace="top.emanjusaka.springboottest.score.repository.IScoreRepository">
  5.     <select id="selectAll" resultType="top.emanjusaka.springboottest.score.model.vo.ScoreVO">
  6.         select * from score
  7.     </select>
  8. </mapper>
复制代码
切换表名的注解:
  1. package top.emanjusaka.springboottest.score.repository;
  2. import org.apache.ibatis.annotations.Mapper;
  3. import top.emanjusaka.springboottest.mybatis.annotation.DBTableStrategy;
  4. import top.emanjusaka.springboottest.score.model.vo.ScoreVO;
  5. import java.util.List;
  6. /**
  7. * @Author emanjusaka
  8. * @Date 2023/10/18 17:45
  9. * @Version 1.0
  10. */
  11. @Mapper
  12. @DBTableStrategy(changeTable = true,tbIdx = "2")
  13. public interface IScoreRepository {
  14.     List<ScoreVO> selectAll();
  15. }
复制代码
测试代码:
  1. package top.emanjusaka.springboottest;
  2. import org.junit.jupiter.api.Test;
  3. import org.springframework.boot.test.context.SpringBootTest;
  4. import top.emanjusaka.springboottest.score.model.vo.ScoreVO;
  5. import top.emanjusaka.springboottest.score.service.IScore;
  6. import javax.annotation.Resource;
  7. import java.util.List;
  8. @SpringBootTest
  9. class SpringBootTestApplicationTests {
  10.     @Resource
  11.     private IScore score;
  12.     @Test
  13.     void contextLoads() {
  14.         List<ScoreVO> list = score.selectAll();
  15.         list.forEach(System.out::println);
  16.     }
  17. }
复制代码
运行结果


通过上图可以看出,现在表名已经修改成了score_2了。通过这种机制,我们可以应用到自动分表中。本文的表名的索引是通过注解参数传递的,实际应用中需要通过哈希散列计算。
本文原创,才疏学浅,如有纰漏,欢迎指正。如果本文对您有所帮助,欢迎点赞,并期待您的反馈交流,共同成长。
原文地址: https://www.emanjusaka.top/archives/10
微信公众号:emanjusaka的编程栈

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

惊雷无声

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表