通过实现MyBatis的Interceptor接口在SQL头部增加统一注释

这篇具有很好参考价值的文章主要介绍了通过实现MyBatis的Interceptor接口在SQL头部增加统一注释。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

背景

从事运维或DBA工作的童鞋会非常熟悉在SQL前部增加注释的操作。类似如下的SQL语句:

/* appUk:[testapp];host ip:[192.168.1.111];traceId:[dcb7f7a0cbe72817];spanId[dcb7f7a0cbe72817] */
INSERT INTO test_table ( project_id, tenant, c_project_id, g_ra_type, g_ra_version, g_ra_config, create_by, update_by ) VALUES ( ?, ?, ?, ?, ?, ?, ?, ? )

这种注释虽然不会影响SQL执行,但是会为运维和DBA工作带来极大的便利如:

  • 对慢查询进行优化时,可以通过注释信息快速找到研发团队及研发人员,同时研发人员也可以快速定位到对应业务请求。
  • 在出现数据库死锁,阻塞时,可快速定位问题源头。
  • 在进行复杂的数据库运维操作时(如数据库迁移),能够与研发团队进行高效合作
  • (还有很多,欢迎大家分享自己的经验)

特别是在一些有着规模化研发团队的公司内,这种注释在运维工作中变得极为重要。一些互联网大厂甚至会在SQL规范中明确要求增加该注释。

在笔者所在的团队,虽然团队规模不大,但是面对公司业务不断增长的形势,对运维工作进行规范化是非常必要的。所以笔者开始在公司开发环境下,找寻可行的解决方案。

环境

  • springboot 2.6.11
  • mybatis-plus:3.5.3.1
  • mybatis:3.5.11

业务目标

  • 在所有SQL头部增加注释,格式:/* xxxxxxxxxxxxxxxxxxxxxxxx */
  • 注释中包含:
    • appUk(应用标识)
    • host ip (宿主机IP地址)
    • 请求追踪信息(brave追踪协议)

技术实现

实现这种技术目标的解决方案有多种。笔者在这个技术方案中,使用了MyBatis的拦截器接口org.apache.ibatis.plugin.Interceptor。关于拦截器的详细使用方法,可以参看《mybatis:自定义实现拦截器插件Interceptor》这篇文章。

拦截器实现代码如下:

package test.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
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.slf4j.MDC;

import java.lang.reflect.Field;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.Connection;

/**
 * MyBatisPlusSqlAnnotationInterceptor
 * <p>
 * 用于为MyBatisPlus的SQL语句添加注释,标记语句的执行者
 *
 * @author John Chen
 * @since 2023/5/6
 */
@Intercepts({
        @Signature(
                type = StatementHandler.class,
                method = "prepare",
                args = {Connection.class, Integer.class}
        )
})
@Slf4j
public class MyBatisPlusSqlAnnotationInterceptor implements Interceptor {

    private final String appUk;

    private String ip = "UNKNOWN";

    public MyBatisPlusSqlAnnotationInterceptor(String appUk) {
        this.appUk = appUk;
        try {
            ip = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("获取本机IP失败", e);
        }
    }

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        String sql = boundSql.getSql();
        String annotation = String.format("/* **********appUk:[%s];host ip:[%s];traceId:[%s];spanId[%s]********** */",
                appUk, ip, MDC.get("traceId"), MDC.get("spanId"));
        // 在SQL语句前面加上注释
        sql = annotation + sql;
        // 用反射修改boundSql的sql属性
        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, sql);
        return invocation.proceed();
    }
}

在完成拦截器的主体代码后,需要将它注入到MyBatis的SqlSessionFactory中。在笔者的项目中,这块代码是通过Java Config的方式实现的。

public class MySqlConfig {

    @Value("${spring.application.name}")
    private String appUk;
    
    /**
     * 定义一个统一增加注释的拦截器
     */
    @Bean
    public MyBatisPlusSqlAnnotationInterceptor myBatisPlusSqlAnnotationInterceptor() {
        return new MyBatisPlusSqlAnnotationInterceptor(appUk);
    }
    
    /**
     * 构建SqlSessionFactory
     * <p>
     * 注意,这里构建后会影响自动配置类{@link com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration#sqlSessionFactory(DataSource)}
     * 的构建。后期如果增加插件,请务参照对照方法在内部增加注入配置
     */
    @Bean
    public SqlSessionFactory testIwrsSqlSessionFactory(DataSource dataSource, GlobalConfig globalConfig, MybatisPlusInterceptor paginationInterceptor, MyBatisPlusSqlAnnotationInterceptor myBatisPlusSqlAnnotationInterceptor) throws Exception {
        final MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setVfs(SpringBootVFS.class);
        sqlSessionFactoryBean.setMapperLocations(
                new PathMatchingResourcePatternResolver().getResources("classpath:/mapper/test/**Mapper.xml")
        );
        sqlSessionFactoryBean.setTypeAliasesPackage("test.common.entity.*");
        sqlSessionFactoryBean.setGlobalConfig(globalConfig);
        sqlSessionFactoryBean.setConfiguration(defaultMybatisConfiguration());
		//**核心代码看这里**
        Interceptor[] plugins = new Interceptor[]{
                paginationInterceptor
                //自动为SQL增加注释的拦截器
                , myBatisPlusSqlAnnotationInterceptor
        };
        sqlSessionFactoryBean.setPlugins(plugins);
        return sqlSessionFactoryBean.getObject();
    }

通过如上2步的配置,就完成了统一增加注释的功能文章来源地址https://www.toymoban.com/news/detail-435168.html

Tips

  • annotation变量中的注释内容,应当根据公司规定和项目实际情况进行调整。目的是能够将SQL和具体的团队、业务、请求进行快速关联,以方便跨职能,甚至跨部门的技术协作。
  • 一些云数据库,可能会通过SQL前部注释的方式完成一些特定功能。如阿里云的PolarDB for AI、PolarDB-X等。这种情况下,增加额外的注释是否会导致具体的功能无法正常运行,需要再实际落地过程中进行验证。

到了这里,关于通过实现MyBatis的Interceptor接口在SQL头部增加统一注释的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 动态头部:统一目标检测头部与注意力

    论文地址:https://arxiv.org/pdf/2106.08322.pdf ai阅读论文_论文速读_论文阅读软件-网易有道速读 创新点是什么? 这篇文档的创新点是提出了一种统一的方法,将对象检测头和注意力机制结合起来。作者在文中提出了一种称为Dynamic Head的方法,通过引入注意力机制来动态地选择和调整不

    2024年02月19日
    浏览(19)
  • 聊聊mybatis的Interceptor机制

    本文主要研究一下mybatis的Interceptor机制 org/apache/ibatis/plugin/Interceptor.java Interceptor定义了intercept方法,其参数为Invocation类型,同时默认提供了plugin方法,通过Plugin.wrap(target, this)进行包装 org/apache/ibatis/plugin/Invocation.java Invocation定义了target、method、args属性,提供了proceed方法则是

    2024年02月10日
    浏览(24)
  • 简单模拟Mybatis通过注解查询SQL

    通过自定义注解,简单模拟Mybatis通过注解查询SQL。 首先,创建自定义注解@MyDao和@MySelect。 MyDao.java MySelect.java 创建UserDao,使用上面的自定义注解。 UserDao.java MyInterceptor.java 处理@MySelect注解的方法实现。 InterfaceDynamicRegister.java 处理@MyDao的注解的Mapper。 TestController.java 调用use

    2024年02月16日
    浏览(24)
  • 【SpringBoot + Mybatis系列】插件机制 Interceptor

    【SpringBoot + Mybatis系列】插件机制 Interceptor 在 Mybatis 中,插件机制提供了非常强大的扩展能力,在 sql 最终执行之前,提供了四个拦截点,支持不同场景的功能扩展 Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) ParameterHandler (getParameterObject, setParameters

    2024年02月16日
    浏览(25)
  • 使用MyBatis的mapper接口调用时有哪些要求?Mybatis 动态 sql 有什么用?执行原理?有哪些动态 sql?

    1. Mapper接口方法名和mapper.xml中定义的每个 sql的id相同 2.Mapper接口方法的输入参数类型和mapper.xml中定义的每个sql 的 parameterType的类型相同 3.Mapper接口方法的输出参数类型和mapper.xml中定义的每个sql的 resultType的类型相同 4.Mapper.xml文件中的 namespace 即是 mapper接口的类路径 可以在

    2024年02月16日
    浏览(29)
  • MyBatis核心 - SqlSession如何通过Mapper接口生成Mapper对象

    书接上文 MyBatis – 执行流程 我们通过SqlSession获取到了UserMapper对象,代码如下: 我们看到,往 sqlSession.getMapper 传入UserMapper接口后,得到的是一个 userMapper 对象,这是怎么做到的呢? 查看SqlSession源码发现,SqlSession有两个实现类,在正常情况下使用的当然就是默认的 Default

    2024年02月14日
    浏览(29)
  • 如何设计 API 接口,实现统一格式返回?

    在移动互联网,分布式,微服务盛行的今天,现在项目绝大部分都采用的微服务框架,前分离分离方式,(题外话:前重新的工作分配越来越明确,现在的前端都称为大前端,技术栈以及生态圈都已经非常成熟;以前官员人员瞧不起前端人员,那现在高层人员要重新认识一下

    2024年02月12日
    浏览(32)
  • 通过myBatis将sql语句返回的值自动包装成一个java对象(1)以及SqlSessionFactory

    如果我们从数据库中读取了一个用户的两个属性——用户名和密码。此时我们希望把这两个属性放入一个java的用户对象中。我们可以选择new一个user对象,向构造器传入我们刚刚读取的两个属性。但一旦从数据库中读取的内容变多,我们就需要new很多新的对象。这时我们就可

    2024年01月18日
    浏览(34)
  • ORC工具(使用阿里云统一文字识别接口实现)

    废话不多,直接上代码。 转换的字符结果是一行。

    2024年02月12日
    浏览(28)
  • MyBatis 实现动态 SQL

     MyBatis 中的动态 SQL 就是 SQL语句可以根据不同的情况情况来拼接不同的sql。 本文会介绍 xml 和 注解 两种方式的动态SQL实现方式。 先创建一个数据表,SQL代码如下: 数据库表和JAVA对象的对应如下:   平时在注册账号时会有一些非必填项,而我们就可以使用 if 标签来跟据条

    2024年02月19日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包