美文网首页Mybatisalready
Mybatis拦截器原理及实例

Mybatis拦截器原理及实例

作者: 迦叶_金色的人生_荣耀而又辉煌 | 来源:发表于2022-08-09 20:46 被阅读0次

    拦截器类型

    1.Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed) 拦截执行器的方法
    2.ParameterHandler (getParameterObject, setParameters) 拦截参数的处理
    2.ResultSetHandler (handleResultSets, handleOutputParameters) 拦截结果集的处理
    4.StatementHandler (prepare, parameterize, batch, update, query) 拦截Sql语法和会话构建的处理
    执行顺序:Executor => StatementHandler => ParameterHandler => ResultSetHandler

    拦截器应用场景

    StatementHandler可改写sql 实现分页查询
    ParameterHandler可改写拦截参数的处理,新增创建时间、创建人、更新时间等
    ResultSetHandler数据库结果二次加密返回客户端,过滤掉审计字段、敏感字段

    拦截器增加分页功能样例

    @Intercepts({@Signature(type = StatementHandler.class, method = "prepare",
                            args = {Connection.class, Integer.class}),
                })
    @Component
    @Slf4j
    public class SqlInterceptor implements Interceptor {
        @Value("${jarye.pagehelper.rule}")
        private String pagehelperRule;
        
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
            MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
            MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
            //sql类型
            SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
            switch (sqlCommandType) {
                    // 判断sql语句是为查询类型
                case SELECT:
                    extendLimit(statementHandler);
                    break;
            }
            
            
            return invocation.proceed();
        }
        
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
        
        
        @Override
        public void setProperties(Properties properties) {
            
        }
        
        /**
        * 对sql实现 修改  加上limit
        */
        private void extendLimit(StatementHandler statementHandler) throws NoSuchFieldException, IllegalAccessException {
            // 获取到原生sql语句
            BoundSql boundSql = statementHandler.getBoundSql();
            Class<? extends BoundSql> aClass = boundSql.getClass();
            // 使用反射机制修改原生sqk语句
            Field sql = aClass.getDeclaredField("sql");
            sql.setAccessible(true);
            String oldSqlStr = boundSql.getSql();
            // 后面加上 limit
            sql.set(boundSql, oldSqlStr + "   " + pagehelperRule);
        }
    }
    

    拦截器修改参数样例

    @Intercepts({
            @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)
    })
    @Slf4j
    @Component
    public class ParamInterceptor implements Interceptor {
    
    
        private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
        private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
        private static final ReflectorFactory REFLECTOR_FACTORY = new DefaultReflectorFactory();
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            // 获取拦截器拦截的设置参数对象DefaultParameterHandler
            ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
    
            // 通过mybatis的反射来获取对应的值
            MetaObject metaResultSetHandler = MetaObject.forObject(parameterHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, REFLECTOR_FACTORY);
            MappedStatement mappedStatement = (MappedStatement) metaResultSetHandler.getValue("mappedStatement");
            // 如果方法是无惨的,则parameterObject会存在空的情况
            Object parameterObject = metaResultSetHandler.getValue("parameterObject");
            //sql类型
            SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
            // 回写parameterObject对象
            metaResultSetHandler.setValue("parameterObject", updateInsertParam(sqlCommandType, parameterObject));
            return invocation.proceed();
        }
    
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
    
        @Override
        public void setProperties(Properties properties) {
    
        }
    
        private Object updateInsertParam(SqlCommandType sqlCommandType, Object parameterObject) throws NoSuchFieldException, IllegalAccessException {
            if(null==parameterObject){
                return parameterObject;
            }
            Class<?> aClass = parameterObject.getClass();
            switch (sqlCommandType) {
                case INSERT:
                    // 使用反射获取到createDatetime修改时间为当前系统时间
                    Field createDatetime = aClass.getSuperclass().getDeclaredField("createDatetime");
                    createDatetime.setAccessible(true);
                    createDatetime.set(parameterObject, new Date());
                case UPDATE:
                    //updateDatetime字段 修改时间为当前系统时间
                    Field updateDatetime = aClass.getSuperclass().getDeclaredField("updateDatetime");
                    updateDatetime.setAccessible(true);
                    updateDatetime.set(parameterObject, new Date());
                    break;
            }
            return parameterObject;
        }
    }
    

    推荐阅读:
    <<<Mybatis的整体执行原理图解
    <<<SqlSessionFactory的创建过程原理
    <<<SqlSession的创建过程
    <<<sqlSession如何获得具体的Mapper接口信息
    <<<userMapper.getUser(1);底层实现原理
    <<<sqlSession.selectOne底层实现原理
    <<<Mybatis一级缓存知识汇总
    <<<Mybatis二级缓存知识汇总
    <<<Springboot整合Mybatis二级缓存
    <<<Mybatis常见面试题

    相关文章

      网友评论

        本文标题:Mybatis拦截器原理及实例

        本文链接:https://www.haomeiwen.com/subject/neznwrtx.html