【MyBatis】使用MyBatis的分頁組件PageHelper時,多表關聯下使用別名查詢時,前台傳參過來,根據參數排序的解決方案
場景:
使用SQLServer數據庫
有2個表Customer、CustomerType,都有字段TypeId
后台SQL:select A.Name ,A.TypeId AS CTypeId, B.TypeName FROM Customer A LEFT JOIN CustomerType B ON A.TypeId = B.TypeId
傳給前台的字段是:Name,CTypeId,TypeName
前台要求查詢條件:要求按CTypeId字段排序,並且分頁查詢1~3條記錄
有2個解決方式:
a、【此方法效率可能較低】使用子查詢,先把整個表查出來,然后按照TypeName排序並分頁(PageHelper的5.1.3版本是這樣實現的)
生成sql如下,一個由3個查詢組成
SELECT TOP 3 Name,CTypeId,TypeName FROM ( SELECT ROW_NUMBER() OVER (ORDER BY CTypeId DESC) PAGE_ROW_NUMBER, Name, CTypeId, TypeName FROM ( SELECT A.Name, A.TypeId AS CTypeId ,B.TypeName FROM Customer A LEFT JOIN CustomerType B ON A.TypeId = B.TypeId ) AS PAGE_TABLE_ALIAS ) AS PAGE_TABLE_ALIAS WHERE PAGE_ROW_NUMBER > 0 ORDER BY PAGE_ROW_NUMBER
b、【我的解決方案】將前台傳過來的字段,解析成對應字段(上面例子中,將TypeName解析成A.name),插入到語句中,即可得到分頁結果
對應的sql,節省了一個查詢
SELECT TOP 3 Name,CTypeId,TypeName FROM ( SELECT ROW_NUMBER() OVER (ORDER BY A.TypeId DESC) PAGE_ROW_NUMBER, A.Name, A.TypeId AS CTypeId ,B.TypeName FROM Customer A LEFT JOIN CustomerType B ON A.TypeId = B.TypeId ) AS PAGE_TABLE_ALIAS WHERE PAGE_ROW_NUMBER > 0 ORDER BY PAGE_ROW_NUMBER
實現:
1、使用5.1.3版本的源碼
2、修改SqlServer對應的解析器
這個解析器我是從舊的版本(不記得是不是4.1.7)基礎上修改的,那個版本還未支持上面第一種方式的排序分頁,所以當時是不能排序並分頁的
主要修改了addRowNumber方法,整個文件代碼如下

/* * The MIT License (MIT) * * Copyright (c) 2014 abel533@gmail.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package com.github.pagehelper.parser; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import com.github.pagehelper.PageException; import net.sf.jsqlparser.expression.Alias; import net.sf.jsqlparser.expression.LongValue; import net.sf.jsqlparser.expression.operators.relational.GreaterThan; import net.sf.jsqlparser.parser.CCJSqlParserUtil; import net.sf.jsqlparser.schema.Column; import net.sf.jsqlparser.statement.Statement; import net.sf.jsqlparser.statement.select.AllColumns; import net.sf.jsqlparser.statement.select.AllTableColumns; import net.sf.jsqlparser.statement.select.FromItem; import net.sf.jsqlparser.statement.select.Join; import net.sf.jsqlparser.statement.select.LateralSubSelect; import net.sf.jsqlparser.statement.select.OrderByElement; import net.sf.jsqlparser.statement.select.PlainSelect; import net.sf.jsqlparser.statement.select.Select; import net.sf.jsqlparser.statement.select.SelectBody; import net.sf.jsqlparser.statement.select.SelectExpressionItem; import net.sf.jsqlparser.statement.select.SelectItem; import net.sf.jsqlparser.statement.select.SetOperationList; import net.sf.jsqlparser.statement.select.SubJoin; import net.sf.jsqlparser.statement.select.SubSelect; import net.sf.jsqlparser.statement.select.Top; import net.sf.jsqlparser.statement.select.ValuesList; import net.sf.jsqlparser.statement.select.WithItem; /** * 將sqlserver查詢語句轉換為分頁語句<br> * 注意事項:<br> * <ol> * <li>請先保證你的SQL可以執行</li> * <li>sql中最好直接包含order by,可以自動從sql提取</li> * <li>如果沒有order by,可以通過入參提供,但是需要自己保證正確</li> * <li>如果sql有order by,可以通過orderby參數覆蓋sql中的order by</li> * <li>order by的列名不能使用別名</li> * <li>表和列使用別名的時候不要使用單引號(')</li> * </ol> * 該類設計為一個獨立的工具類,依賴jsqlparser,可以獨立使用 * * @author liuzh */ public class SqlServerParser { // 緩存結果 protected static final Map<String, String> CACHE = new ConcurrentHashMap<String, String>(); // 開始行號 protected static final String START_ROW = String.valueOf(Long.MIN_VALUE); // 結束行號 protected static final String PAGE_SIZE = String.valueOf(Long.MAX_VALUE); // 外層包裝表 protected static final String WRAP_TABLE = "WRAP_OUTER_TABLE"; // 表別名名字 protected static final String PAGE_TABLE_NAME = "PAGE_TABLE_ALIAS"; // protected public static final Alias PAGE_TABLE_ALIAS = new Alias(PAGE_TABLE_NAME); // 行號 protected static final String PAGE_ROW_NUMBER = "PAGE_ROW_NUMBER"; // 行號列 protected static final Column PAGE_ROW_NUMBER_COLUMN = new Column(PAGE_ROW_NUMBER); // TOP 100 PERCENT protected static final Top TOP100_PERCENT; // 靜態方法處理 static { TOP100_PERCENT = new Top(); TOP100_PERCENT.setExpression(new LongValue(100)); TOP100_PERCENT.setPercentage(true); } /** * 轉換為分頁語句 * * @param sql * @param offset * @param limit * @return */ public String convertToPageSql(String sql, int offset, int limit) { String pageSql = CACHE.get(sql); if (pageSql == null) { // 解析SQL Statement stmt; try { stmt = CCJSqlParserUtil.parse(sql); } catch (Throwable e) { throw new RuntimeException("不支持該SQL轉換為分頁查詢!"); } if (!(stmt instanceof Select)) { throw new RuntimeException("分頁語句必須是Select查詢!"); } // 獲取分頁查詢的select Select pageSelect = getPageSelect((Select) stmt); pageSql = pageSelect.toString(); CACHE.put(sql, pageSql); } pageSql = pageSql.replace(START_ROW, String.valueOf(offset)); pageSql = pageSql.replace(PAGE_SIZE, String.valueOf(limit)); return pageSql; } /** * 獲取一個外層包裝的TOP查詢 * * @param select * @return */ protected Select getPageSelect(Select select) { SelectBody selectBody = select.getSelectBody(); if (selectBody instanceof SetOperationList) { selectBody = wrapSetOperationList((SetOperationList) selectBody); } // 這里的selectBody一定是PlainSelect if (((PlainSelect) selectBody).getTop() != null) { throw new RuntimeException("被分頁的語句已經包含了Top,不能再通過分頁插件進行分頁查詢!"); } // 獲取查詢列 List<SelectItem> selectItems = getSelectItems((PlainSelect) selectBody); // 對一層的SQL增加ROW_NUMBER() addRowNumber((PlainSelect) selectBody); // 處理子語句中的order by processSelectBody(selectBody, 0); // 新建一個select Select newSelect = new Select(); PlainSelect newSelectBody = new PlainSelect(); // 設置top Top top = new Top(); top.setExpression(new LongValue(Long.MAX_VALUE)); newSelectBody.setTop(top); // 設置order by List<OrderByElement> orderByElements = new ArrayList<OrderByElement>(); OrderByElement orderByElement = new OrderByElement(); orderByElement.setExpression(PAGE_ROW_NUMBER_COLUMN); orderByElements.add(orderByElement); newSelectBody.setOrderByElements(orderByElements); // 設置where GreaterThan greaterThan = new GreaterThan(); greaterThan.setLeftExpression(PAGE_ROW_NUMBER_COLUMN); greaterThan.setRightExpression(new LongValue(Long.MIN_VALUE)); newSelectBody.setWhere(greaterThan); // 設置selectItems newSelectBody.setSelectItems(selectItems); // 設置fromIterm SubSelect fromItem = new SubSelect(); fromItem.setSelectBody(selectBody); fromItem.setAlias(PAGE_TABLE_ALIAS); newSelectBody.setFromItem(fromItem); newSelect.setSelectBody(newSelectBody); if (isNotEmptyList(select.getWithItemsList())) { newSelect.setWithItemsList(select.getWithItemsList()); } return newSelect; } /** * 包裝SetOperationList * * @param setOperationList * @return */ protected SelectBody wrapSetOperationList(SetOperationList setOperationList) { // 獲取最后一個plainSelect SelectBody setSelectBody = setOperationList.getSelects().get(setOperationList.getSelects().size() - 1); if (!(setSelectBody instanceof PlainSelect)) { throw new RuntimeException("目前無法處理該SQL,您可以將該SQL發送給abel533@gmail.com協助作者解決!"); } PlainSelect plainSelect = (PlainSelect) setSelectBody; PlainSelect selectBody = new PlainSelect(); List<SelectItem> selectItems = getSelectItems(plainSelect); selectBody.setSelectItems(selectItems); // 設置fromIterm SubSelect fromItem = new SubSelect(); fromItem.setSelectBody(setOperationList); fromItem.setAlias(new Alias(WRAP_TABLE)); selectBody.setFromItem(fromItem); // order by if (isNotEmptyList(plainSelect.getOrderByElements())) { selectBody.setOrderByElements(plainSelect.getOrderByElements()); plainSelect.setOrderByElements(null); } return selectBody; } /** * 獲取查詢列 * * @param plainSelect * @return */ protected List<SelectItem> getSelectItems(PlainSelect plainSelect) { // 設置selectItems List<SelectItem> selectItems = new ArrayList<SelectItem>(); for (SelectItem selectItem : plainSelect.getSelectItems()) { // 別名需要特殊處理 if (selectItem instanceof SelectExpressionItem) { SelectExpressionItem selectExpressionItem = (SelectExpressionItem) selectItem; if (selectExpressionItem.getAlias() != null) { // 直接使用別名 Column column = new Column(selectExpressionItem.getAlias().getName()); SelectExpressionItem expressionItem = new SelectExpressionItem(column); selectItems.add(expressionItem); } else if (selectExpressionItem.getExpression() instanceof Column) { Column column = (Column) selectExpressionItem.getExpression(); SelectExpressionItem item = null; if (column.getTable() != null) { Column newColumn = new Column(column.getColumnName()); item = new SelectExpressionItem(newColumn); selectItems.add(item); } else { selectItems.add(selectItem); } } else { selectItems.add(selectItem); } } else if (selectItem instanceof AllTableColumns) { selectItems.add(new AllColumns()); } else { selectItems.add(selectItem); } } return selectItems; } /** * 最外層的SQL查詢需要增加ROW_NUMBER() * * @param plainSelect */ protected void addRowNumber(PlainSelect plainSelect) { // 增加ROW_NUMBER() StringBuilder orderByBuilder = new StringBuilder(); orderByBuilder.append("ROW_NUMBER() OVER ("); if (isNotEmptyList(plainSelect.getOrderByElements())) { // ByLouis 使用別名,自動找出哪一列,然后進行排序 for (OrderByElement orderByElement : plainSelect.getOrderByElements()) { String orderName = orderByElement.getExpression().toString(); // 如果排序列已經帶.,如A.TypeId,則不用處理 int indexOfPoint = orderName.indexOf("."); if (indexOfPoint >= 0) break; // 找出排序列名 String realFieldName = ""; for (SelectItem selectItem : plainSelect.getSelectItems()) { // 首先找到前台傳過來的字段所在的列 // selectItem.toString()可以有4種格式 // 直接select字段:HelpCode // 表加字段:A.HelpCode // 直接select字段加別名:HelpCode as NewCode // 表加字段加別名:A.HelpCode as NewCode // 前台傳過來的字段:有別名則是別名,列名則是列名 // 查找規則:最后一個空格,或最后一個.后面的數據 String selectName = selectItem.toString(); int lastIndexOfSpace = selectName.lastIndexOf(" "); int lastIndexOfPoint = selectName.lastIndexOf("."); int startGetIndex = 0; if (lastIndexOfSpace > startGetIndex) startGetIndex = lastIndexOfSpace; if (lastIndexOfPoint > startGetIndex) startGetIndex = lastIndexOfPoint; if (startGetIndex == 0) startGetIndex = 1; else startGetIndex++; String fieldName = selectName.substring(startGetIndex); System.out.println(fieldName); if (fieldName.toUpperCase().equals(orderName.toUpperCase())) { realFieldName = selectName; // 找到對應select的字段 // 查找規則 第一個空格前面 int firstIndexOfSpace = selectName.indexOf(" "); if (firstIndexOfSpace >= 0) realFieldName = realFieldName.substring(0, firstIndexOfSpace); break; } } orderByElement.setExpression(new Column(realFieldName)); } orderByBuilder.append(PlainSelect.orderByToString(false, plainSelect.getOrderByElements())); } else { throw new RuntimeException("請您在sql中包含order by語句!"); } // 需要把改orderby清空 if (isNotEmptyList(plainSelect.getOrderByElements())) { plainSelect.setOrderByElements(null); } orderByBuilder.append(") "); orderByBuilder.append(PAGE_ROW_NUMBER); Column orderByColumn = new Column(orderByBuilder.toString()); plainSelect.getSelectItems().add(0, new SelectExpressionItem(orderByColumn)); } /** * 處理selectBody去除Order by * * @param selectBody */ protected void processSelectBody(SelectBody selectBody, int level) { if (selectBody instanceof PlainSelect) { processPlainSelect((PlainSelect) selectBody, level + 1); } else if (selectBody instanceof WithItem) { WithItem withItem = (WithItem) selectBody; if (withItem.getSelectBody() != null) { processSelectBody(withItem.getSelectBody(), level + 1); } } else { SetOperationList operationList = (SetOperationList) selectBody; if (operationList.getSelects() != null && operationList.getSelects().size() > 0) { List<SelectBody> plainSelects = operationList.getSelects(); for (SelectBody plainSelect : plainSelects) { processSelectBody(plainSelect, level + 1); } } } } /** * 處理PlainSelect類型的selectBody * * @param plainSelect */ protected void processPlainSelect(PlainSelect plainSelect, int level) { if (level > 1) { if (isNotEmptyList(plainSelect.getOrderByElements())) { if (plainSelect.getTop() == null) { plainSelect.setTop(TOP100_PERCENT); } } } if (plainSelect.getFromItem() != null) { processFromItem(plainSelect.getFromItem(), level + 1); } if (plainSelect.getJoins() != null && plainSelect.getJoins().size() > 0) { List<Join> joins = plainSelect.getJoins(); for (Join join : joins) { if (join.getRightItem() != null) { processFromItem(join.getRightItem(), level + 1); } } } } /** * 處理子查詢 * * @param fromItem */ protected void processFromItem(FromItem fromItem, int level) { if (fromItem instanceof SubJoin) { SubJoin subJoin = (SubJoin) fromItem; if (subJoin.getJoin() != null) { if (subJoin.getJoin().getRightItem() != null) { processFromItem(subJoin.getJoin().getRightItem(), level + 1); } } if (subJoin.getLeft() != null) { processFromItem(subJoin.getLeft(), level + 1); } } else if (fromItem instanceof SubSelect) { SubSelect subSelect = (SubSelect) fromItem; if (subSelect.getSelectBody() != null) { processSelectBody(subSelect.getSelectBody(), level + 1); } } else if (fromItem instanceof ValuesList) { } else if (fromItem instanceof LateralSubSelect) { LateralSubSelect lateralSubSelect = (LateralSubSelect) fromItem; if (lateralSubSelect.getSubSelect() != null) { SubSelect subSelect = lateralSubSelect.getSubSelect(); if (subSelect.getSelectBody() != null) { processSelectBody(subSelect.getSelectBody(), level + 1); } } } // Table時不用處理 } /** * List不空 * * @param list * @return */ public boolean isNotEmptyList(List<?> list) { if (list == null || list.size() == 0) { return false; } return true; } /** * 轉換為分頁語句 * * @param sql * @return */ public String convertToPageSql(String sql) { return convertToPageSql(sql, null, null); } /** * 轉換為分頁語句 * * @param sql * @param offset * @param limit * @return */ public String convertToPageSql(String sql, Integer offset, Integer limit) { // 解析SQL Statement stmt; try { stmt = CCJSqlParserUtil.parse(sql); } catch (Throwable e) { throw new PageException("不支持該SQL轉換為分頁查詢!"); } if (!(stmt instanceof Select)) { throw new PageException("分頁語句必須是Select查詢!"); } // 獲取分頁查詢的select Select pageSelect = getPageSelect((Select) stmt); String pageSql = pageSelect.toString(); // 緩存移到外面了,所以不替換參數 if (offset != null) { pageSql = pageSql.replace(START_ROW, String.valueOf(offset)); } if (limit != null) { pageSql = pageSql.replace(PAGE_SIZE, String.valueOf(limit)); } return pageSql; } }