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

    自定义MyBatis拦截器更改表名

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

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

    自定义MyBatis拦截器更改表名

    注意需要在 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);     }  }  

运行结果

自定义MyBatis拦截器更改表名

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

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

发表评论

评论已关闭。

相关文章