Browse Source

fix: #1160(github) 分页组件orderBy:
同时存在group by 和order by,且IPage 参数中存在排序属性时,拼接出来的sql会出现两个order by

yuxiaobin 6 years ago
parent
commit
6a7ad176e3

+ 62 - 25
mybatis-plus-extension/src/main/java/com/baomidou/mybatisplus/extension/plugins/PaginationInterceptor.java

@@ -15,32 +15,57 @@
  */
  */
 package com.baomidou.mybatisplus.extension.plugins;
 package com.baomidou.mybatisplus.extension.plugins;
 
 
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.ibatis.executor.statement.StatementHandler;
+import org.apache.ibatis.logging.Log;
+import org.apache.ibatis.logging.LogFactory;
+import org.apache.ibatis.mapping.BoundSql;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.mapping.SqlCommandType;
+import org.apache.ibatis.mapping.StatementType;
+import org.apache.ibatis.plugin.Interceptor;
+import org.apache.ibatis.plugin.Intercepts;
+import org.apache.ibatis.plugin.Invocation;
+import org.apache.ibatis.plugin.Plugin;
+import org.apache.ibatis.plugin.Signature;
+import org.apache.ibatis.reflection.MetaObject;
+import org.apache.ibatis.reflection.SystemMetaObject;
+import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.RowBounds;
+
 import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.annotation.DbType;
 import com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler;
 import com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
 import com.baomidou.mybatisplus.core.parser.ISqlParser;
 import com.baomidou.mybatisplus.core.parser.ISqlParser;
 import com.baomidou.mybatisplus.core.parser.SqlInfo;
 import com.baomidou.mybatisplus.core.parser.SqlInfo;
-import com.baomidou.mybatisplus.core.toolkit.*;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
+import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
 import com.baomidou.mybatisplus.extension.handlers.AbstractSqlParserHandler;
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
 import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
 import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
 import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
 import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
 import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
+
 import lombok.Setter;
 import lombok.Setter;
 import lombok.experimental.Accessors;
 import lombok.experimental.Accessors;
-import org.apache.ibatis.executor.statement.StatementHandler;
-import org.apache.ibatis.mapping.*;
-import org.apache.ibatis.plugin.*;
-import org.apache.ibatis.reflection.MetaObject;
-import org.apache.ibatis.reflection.SystemMetaObject;
-import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
-import org.apache.ibatis.session.Configuration;
-import org.apache.ibatis.session.RowBounds;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.util.*;
+import net.sf.jsqlparser.JSQLParserException;
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
+import net.sf.jsqlparser.schema.Column;
+import net.sf.jsqlparser.statement.select.OrderByElement;
+import net.sf.jsqlparser.statement.select.PlainSelect;
+import net.sf.jsqlparser.statement.select.Select;
 
 
 /**
 /**
  * 分页拦截器
  * 分页拦截器
@@ -53,6 +78,7 @@ import java.util.*;
 @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
 @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
 public class PaginationInterceptor extends AbstractSqlParserHandler implements Interceptor {
 public class PaginationInterceptor extends AbstractSqlParserHandler implements Interceptor {
 
 
+    protected static final Log logger = LogFactory.getLog(PaginationInterceptor.class);
     /**
     /**
      * COUNT SQL 解析
      * COUNT SQL 解析
      */
      */
@@ -79,16 +105,29 @@ public class PaginationInterceptor extends AbstractSqlParserHandler implements I
      *
      *
      * @param originalSql 需要拼接的SQL
      * @param originalSql 需要拼接的SQL
      * @param page        page对象
      * @param page        page对象
-     * @param orderBy     是否需要拼接Order By
      * @return ignore
      * @return ignore
      */
      */
-    public static String concatOrderBy(String originalSql, IPage<?> page, boolean orderBy) {
-        if (orderBy && CollectionUtils.isNotEmpty(page.orders())) {
-
-            StringJoiner joiner = new StringJoiner(StringPool.COMMA);
-            page.orders().forEach(order -> joiner.add(order.getColumn() + (order.isAsc() ? " ASC" : " DESC")));
-            return originalSql + " ORDER BY " + joiner;
-
+    public static String concatOrderBy(String originalSql, IPage<?> page) {
+        if (CollectionUtils.isNotEmpty(page.orders())) {
+            try {
+                List<OrderItem> orderList = page.orders();
+                Select selectStatement = (Select) CCJSqlParserUtil.parse(originalSql);
+                PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();
+                List<OrderByElement> orderByElements = plainSelect.getOrderByElements();
+                if (orderByElements == null || orderByElements.isEmpty()) {
+                    orderByElements = new ArrayList<>(orderList.size());
+                }
+                for (OrderItem item : orderList) {
+                    OrderByElement element = new OrderByElement();
+                    element.setExpression(new Column(item.getColumn()));
+                    element.setAsc(item.isAsc());
+                    orderByElements.add(element);
+                }
+                plainSelect.setOrderByElements(orderByElements);
+                return plainSelect.toString();
+            } catch (JSQLParserException e) {
+                logger.warn("failed to concat orderBy from IPage, exception=" + e.getMessage());
+            }
         }
         }
         return originalSql;
         return originalSql;
     }
     }
@@ -148,17 +187,15 @@ public class PaginationInterceptor extends AbstractSqlParserHandler implements I
         DbType dbType = StringUtils.isNotEmpty(dialectType) ? DbType.getDbType(dialectType)
         DbType dbType = StringUtils.isNotEmpty(dialectType) ? DbType.getDbType(dialectType)
             : JdbcUtils.getDbType(connection.getMetaData().getURL());
             : JdbcUtils.getDbType(connection.getMetaData().getURL());
 
 
-        boolean orderBy = true;
         if (page.isSearchCount()) {
         if (page.isSearchCount()) {
             SqlInfo sqlInfo = SqlParserUtils.getOptimizeCountSql(page.optimizeCountSql(), countSqlParser, originalSql);
             SqlInfo sqlInfo = SqlParserUtils.getOptimizeCountSql(page.optimizeCountSql(), countSqlParser, originalSql);
-            orderBy = sqlInfo.isOrderBy();
             this.queryTotal(overflow, sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
             this.queryTotal(overflow, sqlInfo.getSql(), mappedStatement, boundSql, page, connection);
             if (page.getTotal() <= 0) {
             if (page.getTotal() <= 0) {
                 return null;
                 return null;
             }
             }
         }
         }
 
 
-        String buildSql = concatOrderBy(originalSql, page, orderBy);
+        String buildSql = concatOrderBy(originalSql, page);
         DialectModel model = DialectFactory.buildPaginationSql(page, buildSql, dbType, dialectClazz);
         DialectModel model = DialectFactory.buildPaginationSql(page, buildSql, dbType, dialectClazz);
         Configuration configuration = mappedStatement.getConfiguration();
         Configuration configuration = mappedStatement.getConfiguration();
         List<ParameterMapping> mappings = new ArrayList<>(boundSql.getParameterMappings());
         List<ParameterMapping> mappings = new ArrayList<>(boundSql.getParameterMappings());

+ 24 - 13
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/H2UserTest.java

@@ -15,6 +15,23 @@
  */
  */
 package com.baomidou.mybatisplus.test.h2;
 package com.baomidou.mybatisplus.test.h2;
 
 
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
 import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
@@ -22,18 +39,6 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.test.h2.entity.H2User;
 import com.baomidou.mybatisplus.test.h2.entity.H2User;
 import com.baomidou.mybatisplus.test.h2.enums.AgeEnum;
 import com.baomidou.mybatisplus.test.h2.enums.AgeEnum;
 import com.baomidou.mybatisplus.test.h2.service.IH2UserService;
 import com.baomidou.mybatisplus.test.h2.service.IH2UserService;
-import org.junit.jupiter.api.*;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.test.context.ContextConfiguration;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-import java.math.BigDecimal;
-import java.math.RoundingMode;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 
 /**
 /**
  * Mybatis Plus H2 Junit Test
  * Mybatis Plus H2 Junit Test
@@ -126,7 +131,7 @@ class H2UserTest extends BaseTest {
 //            Assert.assertEquals(nameParam, u.getName());
 //            Assert.assertEquals(nameParam, u.getName());
 //            Assert.assertNotNull(u.getId());
 //            Assert.assertNotNull(u.getId());
 //        }
 //        }
-//        Assert.assertNotEquals(0, page.getTotal());
+//        Assert.assertNotEquals(0, pagemySelectMaps.getTotal());
 //    }
 //    }
 
 
     @Test
     @Test
@@ -338,4 +343,10 @@ class H2UserTest extends BaseTest {
         userService.query().nested(i -> i.eq("name", "Tomcat")).list();
         userService.query().nested(i -> i.eq("name", "Tomcat")).list();
         userService.lambdaUpdate().set(H2User::getName, "Tom").eq(H2User::getName, "Tomcat").update();
         userService.lambdaUpdate().set(H2User::getName, "Tom").eq(H2User::getName, "Tomcat").update();
     }
     }
+
+
+    @Test
+    public void myQueryWithGroupByOrderBy(){
+        userService.mySelectMaps().forEach(System.out::println);
+    }
 }
 }

+ 2 - 2
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/mapper/H2UserMapper.java

@@ -94,8 +94,8 @@ public interface H2UserMapper extends SuperMapper<H2User> {
         ") a")
         ") a")
     int selectCountWithParamInSelectItems(Map<String, Object> param);
     int selectCountWithParamInSelectItems(Map<String, Object> param);
 
 
-    @Select("select * from h2user")
-    List<Map<?,?>> mySelectMaps();
+    @Select("select age,name,count(age) from h2user group by age,name order by age")
+    List<Map<?,?>> mySelectMaps(IPage<H2User> page);
 
 
     @Select("call 1")
     @Select("call 1")
     @Options(statementType = StatementType.CALLABLE)
     @Options(statementType = StatementType.CALLABLE)

+ 13 - 8
mybatis-plus/src/test/java/com/baomidou/mybatisplus/test/h2/service/impl/H2UserServiceImpl.java

@@ -15,18 +15,21 @@
  */
  */
 package com.baomidou.mybatisplus.test.h2.service.impl;
 package com.baomidou.mybatisplus.test.h2.service.impl;
 
 
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
 import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
 import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
-import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;
 import com.baomidou.mybatisplus.test.h2.entity.H2User;
 import com.baomidou.mybatisplus.test.h2.entity.H2User;
+import com.baomidou.mybatisplus.test.h2.mapper.H2UserMapper;
 import com.baomidou.mybatisplus.test.h2.service.IH2UserService;
 import com.baomidou.mybatisplus.test.h2.service.IH2UserService;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
 
 
 /**
 /**
  * Service层测试
  * Service层测试
@@ -82,7 +85,9 @@ public class H2UserServiceImpl extends ServiceImpl<H2UserMapper, H2User> impleme
 
 
     @Override
     @Override
     public List<Map<?,?>> mySelectMaps() {
     public List<Map<?,?>> mySelectMaps() {
-        return baseMapper.mySelectMaps();
+        Page<H2User> page = new Page<>(1,3);
+        page.addOrder(OrderItem.asc("name"));
+        return baseMapper.mySelectMaps(page);
     }
     }
 
 
     @Override
     @Override