mybatis拦截器ResultSetHandler详解

目录

前言

拦截器执行时期

配置拦截器

[ResultSetHandler 拦截器](#ResultSetHandler 拦截器)

可拦截的方法

执行时机

核心功能

项目实战

代码例子一

代码例子二


前言

上一篇我们学习了mybatis拦截器ParameterHandler的使用(https://blog.csdn.net/weixin_38357164/article/details/156654311?fromshare=blogdetail\&sharetype=blogdetail\&sharerId=156654311\&sharerefer=PC\&sharesource=weixin_38357164\&sharefrom=from_link)但是这个拦截器的执行生命周期中,只限定于对入参进行拦截操作,如果想对sql执行后的返回结果进行操作,比如数据库中存储的敏感信息,读入到程序内存中就需要进行脱敏处理,这时候就需要使用ResultSetHandler拦截器。

拦截器执行时期

MyBatis执行流程:

配置文件加载 (Configuration)

创建 SqlSessionFactory

获取 SqlSession

获取 Mapper 代理对象

执行数据库操作

├── Executor 拦截器

├── ParameterHandler 拦截器

├── StatementHandler 拦截器

└── ResultSetHandler 拦截器

配置拦截器

请参考:https://blog.csdn.net/weixin_38357164/article/details/156654311?fromshare=blogdetail\&sharetype=blogdetail\&sharerId=156654311\&sharerefer=PC\&sharesource=weixin_38357164\&sharefrom=from_link

ResultSetHandler 拦截器

ResultSetHandler 拦截器是 MyBatis 四大组件拦截器中专门用于处理查询结果的拦截器。它允许你在结果集从数据库返回到 Java 对象的转换过程中进行干预和定制。

可拦截的方法

该拦截器可以拦截的方法如下所示,我们经常使用的是 "处理普通查询的结果集" 的拦截方法就可以了。

java 复制代码
@Intercepts({
    // 处理普通查询的结果集
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", 
              args = {Statement.class}),
    
    // 处理存储过程的输出参数
    @Signature(type = ResultSetHandler.class, method = "handleOutputParameters", 
              args = {CallableStatement.class}),
    
    // 处理游标查询的结果集
    @Signature(type = ResultSetHandler.class, method = "handleCursorResultSets", 
              args = {Statement.class})
})

执行时机

// 结果集处理流程

Statement.executeQuery() 完成

ResultSetHandler.handleResultSets() 被调用

  1. 遍历 ResultSet

  2. 根据映射配置创建对象

  3. 设置对象属性值

  4. 处理关联映射(association/collection)

  5. 返回结果列表

拦截器(ResultSetHandler )会在此过程中介入

核心功能

  1. 结果集脱敏:保护敏感数据

  2. 数据转换:类型、格式转换

  3. 结果缓存:提高查询性能

  4. 动态处理:根据条件修改结果

  5. 监控统计:记录执行信息

项目实战

代码例子一

该方案先会判断指定的表,如果表匹配成功才会处理结果,否则不会处理。

java 复制代码
@Intercepts({
        @Signature(
                type = ResultSetHandler.class,
                method = "handleResultSets",
                args = {Statement.class}
        )
})
public class ResultCacheInterceptor extends InterceptorBase implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler handler = (StatementHandler) invocation.getTarget();
        BoundSql boundSql = handler.getBoundSql();
        String originalSql = boundSql.getSql();
        //这里可以拿到原始sql:originalSql,也可以通过sql解析出表名,对指定的表名进行操作。
        //但需要注意:
        // 1、sql解析必须对应当前数据库语法,比如mysql或oracle、国产DB等
        // 2、sql中可能还有多个表,注意多表时的处理逻辑

        // 执行原方法获取结果
        Object result = invocation.proceed();

        // 处理结果
        if(是否是指定的表){
            if (result instanceof List<?> list) {
            for (Object item : list) {
                processObject(item, false);
            }
        }
        }
        return result;
    }
}

代码例子二

该方案判断结果集对象类型,一般一个表的内容映射一个java bean对象。通过返回的映射的bean 类型是否是要处理的类型即可。

java 复制代码
@Intercepts({
        @Signature(
                type = ResultSetHandler.class,
                method = "handleResultSets",
                args = {Statement.class}
        )
})
public class ResultCacheInterceptor extends InterceptorBase implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        // 执行原方法获取结果
        Object result = invocation.proceed();

        // 处理结果
        if (result instanceof List<?> list) {
            for (Object item : list) {
                processObject(item, false);
            }
        }
        return result;
    }

    /**
     * 处理单个对象
     *
     * @param obj bean对象
     */
    @Override
    protected void processObject(Object obj, boolean isEnc) throws Exception {
        //先判断bean的类型,只对指定的返回的bean类型做处理,加速拦截器返回效率
        if (!(obj instanceof Subscriber)) return;
        Class<?> clazz = obj.getClass();
        for (Field field : clazz.getDeclaredFields()) {
            if (field.getName().equals("subscriberName")) {
                field.setAccessible(true);
                Object fieldValue = field.get(obj);
                if (fieldValue instanceof String originalName) {
                    originalName = "用 originalName 计算脱敏后的值";
                    field.set(obj, originalName);
                }
            }
        }
    }
}

======================

喜欢求个关注,回归持续更新

======================

相关推荐
代码or搬砖17 小时前
JVM垃圾回收器
java·jvm·算法
客卿12317 小时前
C语言刷题--合并有序数组
java·c语言·算法
Overt0p17 小时前
抽奖系统(6)
java·spring boot·redis·设计模式·rabbitmq·状态模式
独断万古他化17 小时前
【SpringBoot 日志】日志级别与配置:分类、使用及持久化全攻略
java·spring boot·后端·java-ee
SimonKing17 小时前
基于Netty的TCP协议的Socket服务端
java·后端·程序员
予枫的编程笔记17 小时前
Elasticsearch深度搜索与查询DSL实战:精准定位数据的核心技法
java·大数据·人工智能·elasticsearch·搜索引擎·全文检索
while(1){yan}17 小时前
拦截器(详解)
数据库·spring boot·spring·java-ee·拦截器
荒诞硬汉17 小时前
面向对象(三)
java·开发语言