最近做的項目前端是外包出去的,所以在做查詢分頁的時候比較麻煩
我們需要先吧結果集的條數返回給前端,然后由前端根據頁面情況(當前頁碼,每頁顯示條數)將所需參數傳到后端。
由於在項目搭建的時候,是沒有考慮數據量比較大(結果集數量大於1W條,甚至達到30W條)的情況
(使用的VPN網絡比較慢,使用單元測試,1w條數據,需要30s左右才能返回到系統上,sql本身執行在秒內可以出結果,
所以不能先把結果集拿到系統中,再返回結果集的條數,然后分頁。所以需要另一個查詢,返回結果集的條數)
現在項目已經存在很多查詢語句,項目使用的是mybatis,所有mybatis中就配了很多select(幾百個),每個都去加一個對應的查詢結果集條數的SQL,
不知道得吐多少老血(而且會讓項目的mapper配置,膨脹很多,這是不必要的損耗)
這種場景下,使用攔截器,在查詢中動態獲取SQL,添加查詢結果集的語句(select count(*) from (原來的sql)),就是合適的解決方法
這樣,只需要在請求參數中添加一個參數(根據項目情況,我是這樣設計的,慕課網上的案例是在select的id中添加指定字符串,如:“bypage”)
在攔截器中取出滿足條件的SQL,動態的添加上(select count(*) from (原來的sql)) 就可以返回結果集的條數。
同時引用了開源的mybatis插件Mybatis_PageHelper ,所有在使用攔截器的時候,加了一個判斷,是不是需要分頁的查詢
mybatis_config.xml
<plugins> <plugin interceptor="com.utstar.bi.interceptors.SqlInterceptorCount"> <!-- 自己寫的那個攔截器 --> <property name="dialect" value="mysql"/> <!-- mysql的方言 --> </plugin> </plugins>
java
package com.utstar.bi.interceptors; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import java.sql.Connection; import java.util.HashMap; import java.util.Properties; /** * Created by Lenovo on 2017/6/17. */ @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) }) public class SqlInterceptorCount implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaStatementHandler = MetaObject.forObject(statementHandler); HashMap mapParam = (HashMap) metaStatementHandler.getValue("delegate.boundSql.parameterObject"); /* * add by venn * if the request parameter with key count && value is sum * then this sql just return the sql count from result * 如果請求參數中有個值是count並且value是sum,就是我們需要攔截的查詢 * mapParam.get("startItem") ==null :這句是因為同時引用了Mybatis_PageHelper * 插件,防止SQL交叉,做的安全過濾 * */ if (mapParam.get("startItem") ==null && mapParam.get("count") != null && mapParam.get("count").toString().equalsIgnoreCase("sum")) { // 從StatementHandler中取出查詢的SQL String sql = (String) metaStatementHandler.getValue("delegate.boundSql.sql"); //System.out.println("before sql = " +sql); // 獲取第一個from的坐標 int index = sql.indexOf("from"); // 將sql from坐標前的字符截斷,加上 select count(1) coun 查詢結果集條數的SQL sql = "select count(1) coun " + sql.substring(index); //System.out.println("after sql = " +sql); // 將修改的SQL放回StatementHandler中 metaStatementHandler.setValue("delegate.boundSql.sql", sql); } // 繼續執行攔截之前的操作 return invocation.proceed(); } @Override public Object plugin(Object target) { /* 根據Intercepts注解,攔截 StatementHandler 的prepare 方法 */ return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { } }
