MyBatis拦截器

使用MyBatis 拦截器的作用

MyBatis 拦截器主要用于在MyBatis的SQL执行过程中插入自定义的行为或逻辑,无需直接修改MyBatis框架本身的代码。拦截器的设计目的是为了让开发者能够在不影响MyBatis核心功能的基础上,根据自身需求定制化特定的执行流程。

MyBatis的拦截器主要可以应用于以下几个方面:

  1. 增强或修改SQL:例如,可以根据运行时条件动态修改SQL语句,如添加额外的where条件,或者动态改变表名。

  2. 性能监控:可以用来记录SQL执行耗时、收集SQL统计信息,以便于对数据库操作进行性能分析。

  3. 权限控制:在执行SQL前检查用户权限,根据权限决定是否执行或修改SQL。

  4. 参数处理:在参数传递给SQL之前进行特殊处理,比如对敏感数据进行加密或解密,或者统一转换日期格式等。

  5. 结果集处理:在查询结果返回给客户端前进行加工,比如字段过滤、数据脱敏、结果合并等。

  6. 异常处理:统一处理数据库操作异常,提供统一的错误提示或者日志记录。

Mybatis提供了四种类型的拦截器

1. Executor(执行器拦截器)

- 用途:拦截MyBatis执行器方法的执行。

- 使用:允许拦截和自定义MyBatis执行器的行为。例如,可以添加缓存、日志记录或审计功能到执行器中。这些拦截器可以在MyBatis执行的不同阶段扩展或修改其行为。您可以通过实现MyBatis提供的相应接口并在MyBatis配置文件中进行配置来实现这些拦截器。

2. StatementHandler(语句拦截器):

-- 用途:拦截SQL语句的执行。

- 使用:可以在SQL语句执行之前修改或增强它们。例如,可以向WHERE子句添加额外的条件或记录执行的语句。分页等

3. ParameterHandler(参数拦截器):

- 用途:拦截SQL语句的参数设置。

- 使用:允许在将参数设置到SQL语句之前修改或验证它们。例如,可以对作为参数传递的敏感信息进行加密或解密。

4. ResultHandler(结果集拦截器):

- 用途:拦截从SQL语句返回的结果集的处理。

- 使用:可以在将结果集返回给应用程序之前修改或分析它们。例如,可以对结果集数据进行转换或执行额外的计算。

拦截的执行顺序是Executor->StatementHandler->ParameterHandler->ResultHandler   

MyBatis拦截器用到了两个注解:@Intercepts@Signature

  • @Intercepts:它用于标识当前的对象是一个拦截器,其配置值是一个@Signature的集合。
  • @Signature:它用于标识需要拦截的接口、方法、对应的参数列表。

四种类型的拦截器的实现

@Intercepts(
        { 
   
                @Signature(type = Executor.class, method = "query",
                        args = { 
   MappedStatement.class, Object.class,
                                RowBounds.class, ResultHandler.class}),
                @Signature(type = Executor.class, method = "query",
                        args = { 
   MappedStatement.class, Object.class, RowBounds.class,
                                ResultHandler.class, CacheKey.class, BoundSql.class}),
        }
)

 type的值与类名相同,method方法名相同,为了避免方法重载,args中指定了各个参数的类型和个数,可通过invocation.getArgs()获取参数数组。

Executor 拦截器实现

@Intercepts({@Signature(
           type= Executor.class,
           method = "query",
           args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
@Slf4j
@Component
public class ExecutorInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        String sql = ExecutorPluginUtils.getSqlByInvocation(invocation);
        //可以对sql重写
        log.error("拦截器ExecutorInterceptor:"+sql);
        //sql = "SELECT id from BUS_RECEIVER where id = ? ";
        ExecutorPluginUtils.resetSql2Invocation( invocation,  sql);
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object o) {

        return Plugin.wrap(o, this);
    }
    @Override
    public void setProperties(Properties properties) {

    }
}

 ParameterHandler 拦截器实现

@Intercepts({

        @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
@Component
@Slf4j
public class ParamInterceptor  implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        log.error("拦截器ParamInterceptor");
        //拦截 ParameterHandler 的 setParameters 方法 动态设置参数
        if (invocation.getTarget() instanceof ParameterHandler) {

            ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
            PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0];
            // 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射
            Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql");
            boundSqlField.setAccessible(true);
            BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);
            // 反射获取 参数对像
            Field parameterField =
                    parameterHandler.getClass().getDeclaredField("parameterObject");
            parameterField.setAccessible(true);
            Object parameterObject = parameterField.get(parameterHandler);
            if (parameterObject instanceof Map) {

                //将参数中的name值改为2
                ((Map) parameterObject).put("name","2");
            }
            // 改写的参数设置到原parameterHandler对象
            parameterField.set(parameterHandler, parameterObject);
            parameterHandler.setParameters(ps);
            log.error(JSON.toJSONString(boundSql.getParameterMappings()));
            log.error(JSON.toJSONString(parameterObject));
        }
        return invocation.proceed();
    }
    @Override
    public Object plugin(Object o) {

        return Plugin.wrap(o, this);
    }
    @Override
    public void setProperties(Properties properties) {

    }

ResultSetHandler 拦截器实现

@Intercepts({ 

@Signature(type = ResultSetHandler.class, method = "handleResultSets", args={ 
Statement.class})
})
@Component
@Slf4j
public class ResultInterceptor implements Interceptor { 

@Override
public Object intercept(Invocation invocation) throws Throwable { 

log.error("拦截器ResultInterceptor");
// ResultSetHandler resultSetHandler1 = (ResultSetHandler) invocation.getTarget();
//通过java反射获得mappedStatement属性值
//可以获得mybatis里的resultype
Object result = invocation.proceed();
if (result instanceof ArrayList) { 

ArrayList resultList = (ArrayList) result;
for (int i = 0; i < resultList.size(); i++) { 

Object oi = resultList.get(i);
Class c = oi.getClass();
Class[] types = { 
String.class};
Method method = c.getMethod("setAddress", types);
// 调用obj对象的 method 方法
method.invoke(oi, "china");
}
}
return result;
}
@Override
public Object plugin(Object target) { 

return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) { 

}
}

StatementHandler 拦截器实现

@Intercepts(
{ 
@Signature(
type = StatementHandler.class,
method = "prepare",
args = { 
Connection.class, Integer.class}
)
})
@Component
@Slf4j
public class StatementInterceptor implements Interceptor { 

@Override
public Object intercept(Invocation invocation) throws Throwable { 

StatementHandler statementHandler = 
(StatementHandler) PluginUtils.realTarget(invocation.getTarget());
MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler);
MappedStatement mappedStatement = 
(MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
//只拦截select方法
if (!SqlCommandType.SELECT.equals(mappedStatement.getSqlCommandType())) { 

return invocation.proceed();
}
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
//获取到sql
String originalSql = boundSql.getSql();
//可以对originalSql进行改写
log.error("拦截器StatementInterceptor:"+originalSql);
metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
Object parameterObject = boundSql.getParameterObject();
return invocation.proceed();
}
@Override
public Object plugin(Object target) { 

return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) { 

}
}

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰冰很社恐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值