目录
[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 拦截器
配置拦截器
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() 被调用
↓
-
遍历 ResultSet
-
根据映射配置创建对象
-
设置对象属性值
-
处理关联映射(association/collection)
-
返回结果列表
↓
拦截器(ResultSetHandler )会在此过程中介入
核心功能
-
结果集脱敏:保护敏感数据
-
数据转换:类型、格式转换
-
结果缓存:提高查询性能
-
动态处理:根据条件修改结果
-
监控统计:记录执行信息
项目实战
代码例子一
该方案先会判断指定的表,如果表匹配成功才会处理结果,否则不会处理。
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);
}
}
}
}
}
======================
喜欢求个关注,回归持续更新
======================