標籤:mybatis sql-parser sql解析 sql結構
由於想要解決Mybatis分頁外掛程式中count查詢效率問題,因為order by很影響效率,所以需要一種方式處理sql,將order by 語句去掉。
試了好幾個sql解析工具,最後選擇了fdb-sql-parser。
Maven依賴:
<dependency> <groupId>com.foundationdb</groupId> <artifactId>fdb-sql-parser</artifactId> <version>1.3.0</version></dependency>
項目地址:https://github.com/FoundationDB/sql-parser
解析方法:
package com.isea533.sql;import com.foundationdb.sql.parser.SQLParser;import com.foundationdb.sql.parser.StatementNode;public class Parser { public static void main(String[] args) throws Exception { SQLParser parser = new SQLParser(); StatementNode stmt = parser.parseStatement( "select userid,username,password " + "from sys_user where username = 'isea533'"); stmt.treePrint(); }}
調用treePrint()可以列印出sql的結構:
[email protected]name: nullupdateMode: UNSPECIFIEDstatementType: SELECTresultSet: [email protected]isDistinct: falseresultColumns: [email protected][0]:[email protected]exposedName: useridname: useridtableName: nullisDefaultColumn: falsetype: nullexpression: [email protected]columnName: useridtableName: nulltype: null[1]:[email protected]exposedName: usernamename: usernametableName: nullisDefaultColumn: falsetype: nullexpression: [email protected]columnName: usernametableName: nulltype: null[2]:[email protected]exposedName: passwordname: passwordtableName: nullisDefaultColumn: falsetype: nullexpression: [email protected]columnName: passwordtableName: nulltype: nullfromList: [email protected][0]:[email protected]tableName: sys_userupdateOrDelete: nullnullcorrelation Name: nullnullwhereClause: [email protected]operator: =methodName: equalstype: nullleftOperand: [email protected]columnName: usernametableName: nulltype: nullrightOperand: [email protected]value: isea533type: CHAR(7) NOT NULL
解析後的結果stmt結構挺複雜的,像上面這張結構是由很多不同類型的Node嵌套而成的,想要在這種結構上做什麼改動比較麻煩,並且許多屬性沒有提供完整的setter和getter方法,使用反射處理起來比較麻煩。對於不同類型的Node判斷起來相當的麻煩。至於有多麻煩,去看NodeToString這個類就明白了。
除瞭解析SQL之外,還能將上面這種結構形式的資料轉換為sql。使用如下方法:
NodeToString unparser = new NodeToString();String sql = unparser.toString(stmt);
我曾經花了不少時間在處理Node結構上,如果想通過修改結構來獲得最終沒有order by的sql,因為SQL可以有很多種變化和形式,所以需要處理的相當複雜。最後不得不放棄這種方式。
後來在看到NodeToString時,想到既然這個類將結構輸出成sql了,那麼他一定也處理了這種複雜的結構。進入NodeToString後直接尋找order by,發現:
protected String orderByList(OrderByList node) throws StandardException { return "ORDER BY " + nodeList(node);}
這裡應該就是處理sql中order by的地方,很快就複製源碼,只把上面這個方法修改為return "";,然後測試發現沒問題。
在之後看到類中好多的protected方法時,才察覺到應該採用繼承的方式來擴充,同時把解析sql的方法寫到這個方法中:
package com.isea533.sql;import com.foundationdb.sql.StandardException;import com.foundationdb.sql.parser.OrderByList;import com.foundationdb.sql.parser.SQLParser;import com.foundationdb.sql.parser.StatementNode;import com.foundationdb.sql.unparser.NodeToString;public class RemoveOrderByUnParser extends NodeToString { private static final SQLParser PARSER = new SQLParser(); public String removeOrderBy(String sql) throws StandardException { StatementNode stmt = PARSER.parseStatement(sql); return toString(stmt); } @Override protected String orderByList(OrderByList node) throws StandardException { return ""; }}
想要去掉Order by 的時候調用removeOrderBy方法就可以。
從一開始浪費那麼多時間,到最後這麼簡單的方法就能實現,這種情況雖然很常見,但是感覺很複雜。
就好比遇到了一個問題,有些人費盡各種方法最後解決了,有些人直接看到了答案。兩種情況得到的東西是不一樣的,解決問題的過程有時候比解決方案更重要。
這篇檔案從頭到尾主要說的可能就是去掉了order by,肯定不是所有人解析sql就是為了這個目的,但是這是一種思路,使用這個sqlparser,你可以從NodeToString入手,通過重寫這裡面的方法,應該能夠滿足大多數的需求了。
最後想說,Mybatis分頁外掛程式又更新了,增加了count查詢時對sql的最佳化,最佳化方法就是本文的內容。
Mybatis分頁外掛程式是一個努力讓Mybatis物理分頁更簡單的開源項目,如果你在使用Mybatis,不妨看看這個分頁外掛程式。
Mybatis項目地址:
https://github.com/pagehelper/Mybatis-PageHelper
http://git.oschina.net/free/Mybatis_PageHelper