zoukankan      html  css  js  c++  java
  • spring boot 实现mybatis拦截器

    spring boot 实现mybatis拦截器

    项目是个报表系统,服务端是简单的Java web架构,直接在请求参数里面加了个query id参数,就是mybatis mapper的query id,刚好对接接口的请求参数,没有使用接口模式。

    基于这种特性,分页使用了PageHelper插件,但是只使用获取指定范围记录这部分,查询的总条数是自己实现的(插件的总条数,需要创建查询对象实现),直接在查询后多发送一次数据库请求,使用自定义的拦截器,拦截查询的请求(默认注入一个参数,等于指定值),在正常的SQL前后 拼接上 "select count(1) coun from (" +sql+")",直接在数据库中,查询结果集的条数。如下:

    if ( mapParam.get("count") != null && mapParam.get("count").toString().equalsIgnoreCase("sum")) {
                    // 从StatementHandler中取出查询的SQL
                    String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
                    
                    sql = "select count(1) coun from ( " + sql + " )";
                    // 将修改的SQL放回StatementHandler中
                    metaStatementHandler.setValue("delegate.boundSql.sql", sql);
    }

    mybatis的 拦截器配置如下:

    <plugins>
            <plugin interceptor="com.github.pagehelper.PageInterceptor">
                <!-- 支持通过Mapper接口参数来传递分页参数 -->
                <property name="supportMethodsArguments" value="true"/>
                <property name="rowBoundsWithCount" value="true"/>
                <property name="reasonable" value="true"/>
            </plugin>
            <!-- mybatis写出sql记录控件(拦截器) -->
            <!-- 自己写的那个拦截器 -->
            <plugin interceptor="com.xx.SqlInterceptorCount">
                <!-- 方言 -->
                <property name="dialect" value="oracle"/>
            </plugin>
            
        </plugins>

    Java使用如下:

    // 从数据库查询
    result = QueryMapper.getObjFromDB(paras.get("qdi"),paras);
    // 查询当前sql 的总条数
    total = QueryMapper.getQdiSum(paras.get("qdi"),paras);

    查 total 的时候,给paras,默认注入一个count参数。

    paras.put("count", "sum");
    hashMap = (HashMap)session.selectList(qdi, paras).get(0);

    用起来还是很酸爽的。

    --------------朴素的分割线------------------

    进入主题

    为迎合时代的发展,领导的召唤,项目架构改版,迁入springboot,方便统一管理(监控)

    程序员就开干了啊。。。

    。。。

    。。。

    。。。

    。。。

    。。。

    // 程序员挥汗如雨中

    几天后,springboot的项目开发完了,但是遇到个问题,mybatis的拦截器不生效,配置如下:

    mybatis:
      mapper-locations: classpath:mapper/*.xml

    事实上,并没有配置,不过可以加个mybatis的配置文件,像这样,如下:

    mybatis:
      mapper-locations: classpath:mapper/*.xml
      configuration:
        interceptions: cn.utstarcom.statplatform7.interceptor.SqlInterceptorCount
      config-location: classpath:mybatis-config.xml

    mbatis-config.xml 内容如下:

    <plugins>
            <plugin interceptor="com.github.pagehelper.PageInterceptor">
                <!-- 支持通过Mapper接口参数来传递分页参数 -->
                <property name="supportMethodsArguments" value="true"/>
                <property name="rowBoundsWithCount" value="true"/>
                <property name="reasonable" value="true"/>
            </plugin>
            <!-- mybatis写出sql记录控件(拦截器) -->
            <!-- 自己写的那个拦截器 -->
            <plugin interceptor="com..SqlInterceptorCount">
                <!-- 方言 -->
                <property name="dialect" value="oracle"/>
            </plugin>
           
        </plugins>

    拦截器内容如下:

    @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
    public class SqlInterceptorCount implements Interceptor {
    
        private Logger logger = LoggerFactory.getLogger(SqlInterceptorCount.class);
        private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
        private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
        private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            try {
                StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
                MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY,
                        DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
    
    //            HashMap mapParam = (HashMap) metaStatementHandler.getValue("delegate.boundSql.parameterObject");
                Object paramObject = metaStatementHandler.getValue("delegate.boundSql.parameterObject");
                if (paramObject instanceof Map) {
                    HashMap paramMap = (HashMap)paramObject;
                    if (paramMap.get("count") != null && paramMap.get("count").toString().equalsIgnoreCase("sum")) {
                        // get sql from StatementHandler
                        String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
                        sql = "select count(1) coun from ( " + sql + " )";
                        // set moidfy sql to StatementHandler
                        metaStatementHandler.setValue("delegate.boundSql.sql", sql);
                    }
                }
            } catch (Exception e) {
                logger.error("throw exception and will not handle the mybatis sql");
            }
            // process the next query process
            return invocation.proceed();
        }
    
        /**
         *  
         * @param o
         * @return
         */
        @Override
        public Object plugin(Object o) {
    
            return null;
        }
    
        @Override
        public void setProperties(Properties properties) {
    
        }
    }

    事实上,mybatis的配置文件和配置的 interceptions,不能使用(我当然知道是),启动没有问题,数据库也可以操作,但是拦截器没有生效

    1、在又是一大圈的乱找,发现个说法,在拦截器上加个  @Component 注解就可以了。

    然后,还是不行,这个更惨,只要查询数据库,都会返回个null ,又没有报错信息,只是个null

    2、又是网上一通乱找,看到个说法:说拦截器只有 plugin方法执行了,intercept方法没有执行

    debug 一下,发现问题了,加上@Component 注解,plugin方法执行了,返回个null,知道为什么吗,看下plugin的代码:

    @Override
        public Object plugin(Object o) {
    
            return null;
        }

    我去

    不返回null,返回什么

    不返回null,返回什么

    不返回null,返回什么

    之后接简单了,plugin 方法是实现 Interceptor 接口的时候,IDEA生成的方法,有个默认实现,返回null

    改改:

     @Override
        public Object plugin(Object o) {
    
    //        logger.info("Interceptor : SqlInterceptorCount is running, but this is plugin method not intercept method");
            /*
                use Intercepts annotation,intercept StatementHandler method prepare
             */
            if (o instanceof StatementHandler) {
                return Plugin.wrap(o, this);
            }
            return o;
        }

    在传入对象是 STATEMENTHandler的对象的时候,把方法转发到intercept 方法,拦截器可以正常执行了。

    结论就是:

      springboot下的mybatis拦截器,和单独用mybatis的拦截器一样,但是不需要配置,直接在拦截器前面加个@Component 注解,在plugin 方法中将请求转发到intercept方法拦截即可,不需要其他配置。

    完整代码如下:

    @Component
    @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
    public class SqlInterceptorCount implements Interceptor {
    
        private Logger logger = LoggerFactory.getLogger(SqlInterceptorCount.class);
        private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
        private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();
        private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory();
    
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
    
            try {
                StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
                MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY,
                        DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY);
    
    //            HashMap mapParam = (HashMap) metaStatementHandler.getValue("delegate.boundSql.parameterObject");
                Object paramObject = metaStatementHandler.getValue("delegate.boundSql.parameterObject");
                if (paramObject instanceof Map) {
                    HashMap paramMap = (HashMap)paramObject;
                    if (paramMap.get("count") != null && paramMap.get("count").toString().equalsIgnoreCase("sum")) {
                        // get sql from StatementHandler
                        String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
                        sql = "select count(1) coun from ( " + sql + " )";
                        // set moidfy sql to StatementHandler
                        metaStatementHandler.setValue("delegate.boundSql.sql", sql);
                    }
                }
            } catch (Exception e) {
                logger.error("throw exception and will not handle the mybatis sql");
            }
            // process the next query process
            return invocation.proceed();
        }
    
        /**
         *  
         * @param o
         * @return
         */
         @Override
        public Object plugin(Object o) {
    
    //        logger.info("Interceptor : SqlInterceptorCount is running, but this is plugin method not intercept method");
            /*
                use Intercepts annotation,intercept StatementHandler method prepare
             */
            if (o instanceof StatementHandler) {
                return Plugin.wrap(o, this);
            }
            return o;
        }
    
        @Override
        public void setProperties(Properties properties) {
    
        }
    }
  • 相关阅读:
    2015腾讯暑期实习笔试题目
    二叉树的优点和缺点
    pandas对象保存到mysql出错提示“BLOB/TEXT column used in key specification without a key length”解决办法
    事务的隔离机制
    Flink Sink定制开发
    Presto实现定时从配置文件读取配置
    LDAP与Sentry API使用
    Presto压测报告
    PrestoSPI安全扩展
    项目重构总结
  • 原文地址:https://www.cnblogs.com/Springmoon-venn/p/9546326.html
Copyright © 2011-2022 走看看