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);
                }
            }
        }
    }
}

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

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

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

相关推荐
别动我齐刘海4 小时前
C++ 工程化学习
java·linux·前端·c++·学习·机器学习·机器人
code2cat5 小时前
Java进阶篇之AtomicLongFieldUpdater:把累计额度留在对象字段中
java·cas·并发编程
WeiXin_DZbishe5 小时前
基于springboot城市公交实时运营与调度系统-计算机毕设 附源码【课程设计】66173
java·vue.js·spring boot·后端·python·php·课程设计
泡茶喝茶写代码5 小时前
A股量化数据工程:从 REST 接口到策略信号(第 18 篇):放量缩量异动扫描
java·python·股票数据api·股票数据api接口·股票api数据接口·股票量化数据api·股票量化数据接口
wewowilling6 小时前
认识微服务
java·运维·微服务·springcloud
代码什么用6 小时前
Java Web 7-10
java·servlet
蜗牛互联网7 小时前
GPT-6 与 Intelligent UI:从回答到可审计操作的产品迁移
java·人工智能·后端
SL_staff8 小时前
运维救火实录:钉钉考勤API变更引发工资计算异常,我们用JVS-Logic可视化逻辑引擎实现分钟级热修复
java·mysql·全栈
SunnyDays10118 小时前
Java 查找并高亮 Word 文字:精确查找及正则表达式模糊查找
java·查找文字·word 文档·高亮文字·正则匹配查找·模糊查找