(资料图片)
自定义Mybatis-plus插件(限制最大查询数量)需求背景一次查询如果结果返回太多(1万或更多),往往会导致系统性能下降,有时更会内存不足,影响系统稳定性,故需要做限制。
解决思路1.经分析最后决定,应限制一次查询返回的最大结果数量不应该超出1万,对于一次返回结果大于限制的时候应该抛出异常,而不应该截取(limit 10000)最大结果(结果需求不匹配)。
2.利用mybatis拦截器技术,统一拦截sql,并真对大结果的查询先做一次count查询。
步骤一1.1 定义拦截器PreCheckBigQueryInnerInterceptorpublic class PreCheckBigQueryInnerInterceptor implements InnerInterceptor {}
1.2 重写willDoQuery方法public boolean willDoQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { // 解析sql Statement stmt = CCJSqlParserUtil.parse(boundSql.getSql()); if (stmt instanceof Select) { PlainSelect selectStmt = (PlainSelect) ((Select) stmt).getSelectBody(); if (Objects.nonNull(selectStmt.getLimit())) { //包含limit查询 return true; } for (SelectItem selectItem : selectStmt.getSelectItems()) { //计数查询 count(); SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; if (selectExpressionItem.getExpression() instanceof Function) { //包含function查询 return true; } } Long aLong = doQueryCount(executor, ms, parameter, rowBounds, resultHandler, boundSql); if (aLong == 0L) { return false; } if (aLong > 20) { throw new RuntimeException("单个查询结果大于20条!!!"); } } return true; }
1.3 代码解析1.3.1 利用CCJSqlParserUtil解析sql,并判断sql类型,只对Select的SQL拦击.1.3.2 对于已有limit的sql查询,直接放行.1.3.3 对于包含function查询(例如count(1)计算,max()...),直接放行.1.3.4 否则判断为大结果查询,执行(doQueryCount)与查询数量.1.3.5 对于大于指定数量的结果,抛出异常.1.4 定义doQueryCount方法private Long doQueryCount(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { MappedStatement countMs = buildAutoCountMappedStatement(ms); String countSqlStr = autoCountSql(true, boundSql.getSql()); PluginUtils.MPBoundSql mpBoundSql = PluginUtils.mpBoundSql(boundSql); BoundSql countSql = new BoundSql(countMs.getConfiguration(), countSqlStr, mpBoundSql.parameterMappings(), parameter); PluginUtils.setAdditionalParameter(countSql, mpBoundSql.additionalParameters()); CacheKey cacheKey = executor.createCacheKey(countMs, parameter, rowBounds, countSql); Object result = executor.query(countMs, parameter, rowBounds, resultHandler, cacheKey, countSql).get(0); System.out.println(result); return (result == null ? 0L : Long.parseLong(result.toString())); }
代码解读:参考PaginationInnerInterceptor(mybatis-plus)分页插件1.4.1:构造MappedStatement对象buildAutoCountMappedStatement(ms),MappedStatement相当于一个存储 SQL 语句、输入参数和输出结果映射等信息的封装体,它对应一条 SQL 语句,并包含了该 SQL 语句执行所需的所有信息。如下代码
注意:必须重新构造,不能直接使用入参中的ms
1.4.2:autoCountSql(true, boundSql.getSql()) 定义并优化计数查询语句String.format("SELECT COUNT(1) FROM (%s) TOTAL", originalSql);
1.4.3: 执行查询executor.query步骤二1.1 注册拦截器PreCheckBigQueryInnerInterceptor@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));//分页插件(Mybatis-plus) interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());//防止全表更新(Mybatis-plus) interceptor.addInnerInterceptor(new PreCheckBigQueryInnerInterceptor());//防止全表查询(自定义插件) return interceptor;}
知识小结:MybatisPlusInterceptorpublic class MybatisPlusInterceptor implements Interceptor { @Setter private List interceptors = new ArrayList<>();}
他是基于mybatis的Interceptor接口做的拦截器,上文中我们 注册拦截器PreCheckBigQueryInnerInterceptor的拦截器其实添加到MybatisPlusInterceptor.interceptors集合中。
为啥重写willDoQuery见代码而不是beforeQuerypublic Object intercept(Invocation invocation) throws Throwable { ...... for (InnerInterceptor query : interceptors) { if (!query.willDoQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql)) { return Collections.emptyList(); } query.beforeQuery(executor, ms, parameter, rowBounds, resultHandler, boundSql); } ...... return invocation.proceed(); }
2.1 willDoQuery先于beforeQuery方法,且一定会执行
自定义Mybatis-plus插件(限制最大查询数量)需求背景一次查询如果...
1、平行志愿即在普通类院校各录取批次分别设置一个平行院校志愿和一...
格隆汇4月19日丨有投资者向睿昂基因提问对于国内肿瘤行业的需求,请...
1、刀鱼春季吃最好。2、初入春天的时候,无论是南方还是北方,此时...
近日,株洲市渌口区摄影家协会组织20余名会员前往南洲镇昭陵社区采风。
今天来聊聊关于伽利略用他的望远镜首先用来观察什么,伽利略用他的...
4月19日进口大豆到国内港口C&F及升贴水报价①美湾大豆(10月船期)C...
01:044月19日,在成都第31届世界大学生夏季运动会倒计时100天之际,...
作为一款备受追捧的智能手机品牌,Oppo的设计一向都以简洁、精致、...
2023年4月18日,观众在湖北省博物馆展厅参观。本文图片新华社、视觉...
1、“简直了”可以表示各种强烈的感情,惊讶,羡慕,钦佩,鄙视,无...
正统三国怎么军屯屯田活动玩法技巧攻略,
北京冬奥会已经闭幕,但这场冰雪盛会却以别样的中华文化魅力收获来...
欢迎观看本篇文章,小勉来为大家解答以上问题。山寨的意思,山寨是...
杭州亚运会代表团团长大会将于4月25日开幕杭州网发布时间:2023-04-...
1、抽筋学名为「肌肉痉挛」(musclecramp),是指肌肉突然、不自主的...
核心观点:1、新产品开发对于企业的可持续发展、企业获得可持续的竞...
同花顺数据中心显示,龙元建设4月18日获融资买入437 39万元,占当...
隆扬电子(301389)04月19日在投资者关系平台上答复了投资者关心的问题。
2023上海车展于今日正式开幕,本届上海车展上,领克正式发布了旗下...
天津北方网讯:昨天,在市政府新闻办举行的天开高教科创园新闻发布...
又一个地产首富,下落不明
德国大众集团18日宣布,计划投资约10亿欧元在中国安徽省合肥市建立...
4月17日,安乡县应急管理局联合消防、医疗、环保、市场监管等部门开...
随着内地与香港实现全面通关,以及出境游的恢复,游客出行需求不断...
以下是华灿光电在北京时间4月19日09:42分盘口异动快照:4月19日,华...
光大证券研报指出,当前锂矿估值已回调至具有性价比的区间,一方面...
谢治宇、赵诣、施成…顶流基金经理最新调仓动作曝光
4月17日,是2023年河南省事业单位公开招聘联考报名的最后一天,截至...
今年以来,消费回升态势明显。3月份,社会消费品零售总额同比增长10...
广告
X 关闭
广告
X 关闭