目录

前言

拦截器执行时期

配置拦截器

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 对象的转换过程中进行干预和定制。

可拦截的方法

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

@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. 监控统计:记录执行信息

项目实战

代码例子一

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

@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 类型是否是要处理的类型即可。

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

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

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

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

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐