【SpringBoot + Mybatis系列】插件机制 Interceptor

这篇具有很好参考价值的文章主要介绍了【SpringBoot + Mybatis系列】插件机制 Interceptor。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【SpringBoot + Mybatis系列】插件机制 Interceptor

在 Mybatis 中,插件机制提供了非常强大的扩展能力,在 sql 最终执行之前,提供了四个拦截点,支持不同场景的功能扩展

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

本文将主要介绍一下自定义 Interceptor 的使用姿势,并给出一个通过自定义插件来输出执行 sql,与耗时的 case

I. 环境准备


1. 数据库准备

使用 mysql 作为本文的实例数据库,新增一张表

CREATE TABLE `money` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
  `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
复制代码

2. 项目环境

本文借助 SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发

pom 依赖如下

<dependencies>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>
复制代码

db 配置信息 application.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password:
复制代码

II. 实例演示


关于 myabtis 的配套 Entity/Mapper 相关内容,推荐查看之前的系列博文,这里就不贴出来了,将主要集中在 Interceptor 的实现上

1. 自定义 interceptor

实现一个自定义的插件还是比较简单的,试下org.apache.ibatis.plugin.Interceptor接口即可

比如定义一个拦截器,实现 sql 输出,执行耗时输出

@Slf4j
@Component
@Intercepts(value = {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
})
public class ExecuteStatInterceptor implements Interceptor {
    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // MetaObject 是 Mybatis 提供的一个用于访问对象属性的对象
        MappedStatement statement = (MappedStatement) invocation.getArgs()[0];
        BoundSql sql = statement.getBoundSql(invocation.getArgs()[1]);
        long start = System.currentTimeMillis();
        List<ParameterMapping> list = sql.getParameterMappings();
        OgnlContext context = (OgnlContext) Ognl.createDefaultContext(sql.getParameterObject());
        List<Object> params = new ArrayList<>(list.size());
        for (ParameterMapping mapping : list) {
            params.add(Ognl.getValue(Ognl.parseExpression(mapping.getProperty()), context, context.getRoot()));
        }
        try {
            return invocation.proceed();
        } finally {
            System.out.println("------------> sql: " + sql.getSql() + "\n------------> args: " + params + "------------> cost: " + (System.currentTimeMillis() - start));
        }
    }
    @Override
    public Object plugin(Object o) {
        return Plugin.wrap(o, this);
    }
    @Override
    public void setProperties(Properties properties) {
    }
}
复制代码

注意上面的实现,核心逻辑在intercept方法,内部实现 sql 获取,参数解析,耗时统计

1.1 sql 参数解析说明

上面 case 中,对于参数解析,mybatis 是借助 Ognl 来实现参数替换的,因此上面直接使用 ognl 表达式来获取 sql 参数,当然这种实现方式比较粗暴

// 下面这一段逻辑,主要是OGNL的使用姿势
OgnlContext context = (OgnlContext) Ognl.createDefaultContext(sql.getParameterObject());
List<Object> params = new ArrayList<>(list.size());
for (ParameterMapping mapping : list) {
    params.add(Ognl.getValue(Ognl.parseExpression(mapping.getProperty()), context, context.getRoot()));
}
复制代码

除了上面这种姿势之外,我们知道最终 mybatis 也是会实现 sql 参数解析的,如果有分析过源码的小伙伴,对下面这种姿势应该比较熟悉了

源码参考自: org.apache.ibatis.scripting.defaults.DefaultParameterHandler#setParameters

BoundSql sql = statementHandler.getBoundSql();
DefaultParameterHandler handler = (DefaultParameterHandler) statementHandler.getParameterHandler();
Field field = handler.getClass().getDeclaredField("configuration");
field.setAccessible(true);
Configuration configuration = (Configuration) ReflectionUtils.getField(field, handler);
// 这种姿势,与mybatis源码中参数解析姿势一直
//
MetaObject mo = configuration.newMetaObject(sql.getParameterObject());
List<Object> args = new ArrayList<>();
for (ParameterMapping key : sql.getParameterMappings()) {
    args.add(mo.getValue(key.getProperty()));
}
复制代码

但是使用上面这种姿势,需要注意并不是所有的切点都可以生效;这个涉及到 mybatis 提供的四个切点的特性,这里也就不详细进行展开,在后面的源码篇,这些都是绕不过去的点

1.2 Intercepts 注解

接下来重点关注一下类上的@Intercepts注解,它表明这个类是一个 mybatis 的插件类,通过@Signature来指定切点

其中的 type, method, args 用来精确命中切点的具体方法

如根据上面的实例 case 进行说明

@Intercepts(value = {@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
})
复制代码

首先从切点为Executor,然后两个方法的执行会被拦截;这两个方法的方法名分别是query, update,参数类型也一并定义了,通过这些信息,可以精确匹配Executor接口上定义的类,如下

// org.apache.ibatis.executor.Executor
// 对应第一个@Signature
<E> List<E> query(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4) throws SQLException;
// 对应第二个@Signature
int update(MappedStatement var1, Object var2) throws SQLException;
复制代码
1.3 切点说明

mybatis 提供了四个切点,那么他们之间有什么区别,什么样的场景选择什么样的切点呢?

一般来讲,拦截ParameterHandler是最常见的,虽然上面的实例是拦截Executor,切点的选择,主要与它的功能强相关,想要更好的理解它,需要从 mybatis 的工作原理出发,这里将只做最基本的介绍,待后续源码进行详细分析

  • Executor:代表执行器,由它调度 StatementHandler、ParameterHandler、ResultSetHandler 等来执行对应的 SQL,其中 StatementHandler 是最重要的。
  • StatementHandler:作用是使用数据库的 Statement(PreparedStatement)执行操作,它是四大对象的核心,起到承上启下的作用,许多重要的插件都是通过拦截它来实现的。
  • ParameterHandler:是用来处理 SQL 参数的。
  • ResultSetHandler:是进行数据集(ResultSet)的封装返回处理的,它非常的复杂,好在不常用。

借用网上的一张 mybatis 执行过程来辅助说明

【SpringBoot + Mybatis系列】插件机制 Interceptor,mybatis

原文 blog.csdn.net/weixin_3949…

2. 插件注册

上面只是自定义插件,接下来就是需要让这个插件生效,也有下面几种不同的姿势

2.1 Spring Bean

将插件定义为一个普通的 Spring Bean 对象,则可以生效

2.2 SqlSessionFactory

直接通过SqlSessionFactory来注册插件也是一个非常通用的做法,正如之前注册 TypeHandler 一样,如下

@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    bean.setMapperLocations(
            // 设置mybatis的xml所在位置,这里使用mybatis注解方式,没有配置xml文件
            new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*.xml"));
    // 注册typehandler,供全局使用
    bean.setTypeHandlers(new Timestamp2LongHandler());
    bean.setPlugins(new SqlStatInterceptor());
    return bean.getObject();
}
复制代码
2.3 xml 配置

习惯用 mybatis 的 xml 配置的小伙伴,可能更喜欢使用下面这种方式,在mybatis-config.xml全局 xml 配置文件中进行定义

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//ibatis.apache.org//DTD Config 3.1//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <!-- 驼峰下划线格式支持 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    <typeAliases>
        <package name="com.git.hui.boot.mybatis.entity"/>
    </typeAliases>
    <!-- type handler 定义 -->
    <typeHandlers>
        <typeHandler handler="com.git.hui.boot.mybatis.handler.Timestamp2LongHandler"/>
    </typeHandlers>
    <!-- 插件定义 -->
    <plugins>
        <plugin interceptor="com.git.hui.boot.mybatis.interceptor.SqlStatInterceptor"/>
        <plugin interceptor="com.git.hui.boot.mybatis.interceptor.ExecuteStatInterceptor"/>
    </plugins>
</configuration>
复制代码

3. 小结

本文主要介绍 mybatis 的插件使用姿势,一个简单的实例演示了如果通过插件,来输出执行 sql,以及耗时

自定义插件实现,重点两步

  • 实现接口org.apache.ibatis.plugin.Interceptor
  • @Intercepts 注解修饰插件类,@Signature定义切点

插件注册三种姿势:

  • 注册为 Spring Bean
  • SqlSessionFactory 设置插件
  • myabtis.xml 文件配置

 文章来源地址https://www.toymoban.com/news/detail-568985.html

到了这里,关于【SpringBoot + Mybatis系列】插件机制 Interceptor的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 使用mybatis的@Interceptor实现拦截sql

    拦截器是一种基于 AOP(面向切面编程)的技术, 它可以在目标对象的方法执行前后插入自定义的逻辑。 1.注解@Intercepts @Intercepts({@Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class, Integer.class})}) ,表示在 SQL 执行之前进行拦截处理。 @Intercepts 的作用:声明

    2024年03月25日
    浏览(30)
  • springboot使用Mybatis-plus分页插件

    在  pom.xml   文件中添加 MyBatis Plus 和分页插件的依赖: 注意替换  {mybatis-plus-version}  为对应的版本号。 在 Spring Boot 的配置文件  application.yml   中添加分页插件的配置参数: 注意代码中的注释,其中   PaginationInterceptor  表示使用 MyBatis Plus 提供的分页插件。 在接口层使用

    2024年02月07日
    浏览(34)
  • 【极光系列】SpringBoot集成Mybatis

    浅夏的猫 @shawsongyue 直接下载可用 https://gitee.com/shawsongyue/aurora.git 详细参考我的另外一遍博客: https://blog.csdn.net/weixin_40736233/article/details/135582926?spm=1001.2014.3001.5501 1. 处理依赖pom.xml 2. 处理数据库表 登录mysql数据库,创建表 tianchi_resource,并插入数据 3. 处理配置application.yml 4.创

    2024年01月17日
    浏览(37)
  • 通过实现MyBatis的Interceptor接口在SQL头部增加统一注释

    从事运维或DBA工作的童鞋会非常熟悉在SQL前部增加注释的操作。类似如下的SQL语句: 这种注释虽然不会影响SQL执行,但是会为运维和DBA工作带来极大的便利如: 对慢查询进行优化时,可以通过注释信息快速找到研发团队及研发人员,同时研发人员也可以快速定位到对应业务

    2024年02月03日
    浏览(34)
  • SpringBoot中mybatis分页插件的使用--【pagehelper组件】

    SpringBoot知识范围-学习步骤【JSB系列之000】 用免费公开视频,卷飞培训班哈人!打死不报班,赚钱靠狠干! 只要自己有电脑,前后项目都能搞!N年苦学无人问,一朝成名天下知! 本文专指mybatis分页,可以是SSM的项目,也可以是springBoot 的项目里的mybatis,或者mybatis plus 韩顺平

    2024年02月14日
    浏览(35)
  • SpringBoot 使用MyBatis分页插件实现分页功能

    案例地址: https://gitee.com/vinci99/paging-pagehelper-demo/tree/master 这里使用application.properties类型配置文件来做例子 创建一个持久化对象TestUserPO 编写在Mapper中编写SQL语句查询与之对应的表 在业务层调用Mapper接口获取数据并分页;需要注意:PageHelper.startPage(pageNum,pageSize)必须写在查询

    2024年02月10日
    浏览(38)
  • springboot+maven插件调用mybatis generator自动生成对应的mybatis.xml文件和java类

    mybatis最繁琐的事就是sql语句和实体类,sql语句写在java文件里很难看,字段多的表一开始写感觉阻力很大,没有耐心,自动生成便成了最称心的做法。自动生成xml文件,dao接口,实体类,虽一直感觉不太优雅,但省去了很多麻烦,当表增加或修改字段的时候重新生成便轻松搞

    2024年02月14日
    浏览(32)
  • SpringBoot 3.1.7 集成 mybatis-generator-maven-plugin 插件

    SpringBoot 3.1.7  集成 mybatis-generator-maven-plugin 插件,使得方便数据库字段快速同步到项目中,不用手敲,节约开发时间 1. 浏览官网 在网上搜索mybatis-generator,找到Mybatis的官网地址如下 MyBatis Generator Core – Introduction to MyBatis Generator 2. 在maven的pom.xml中添加一个插件 打开看了看,

    2024年01月20日
    浏览(30)
  • 7.3 SpringBoot整合MyBatis分页插件github.pageHelper:实现图书列表API

    在软件开发中,分页是一个非常常见的需求,无论是在Web应用程序还是在移动应用程序中,我们经常需要将大量的数据分成多个页面进行展示。 本文主要实现图书列表API,使用 SpringBoot集成MyBatis分页插件github.pageHelper ,首先会从「 自己实现分页原理 」说起,再到「 使用gi

    2024年02月11日
    浏览(44)
  • 详解Mybatis-Plus中分页插件PaginationInterceptor, MybatisPlusInterceptor在SpringBoot中的使用

    我们在开发的过程中,经常会遇到分页操作,其分为逻辑分页和物理分页,具体可参考我的博文:

    2023年04月14日
    浏览(35)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包