|
@@ -0,0 +1,189 @@
|
|
|
+package com.baomidou.mybatisplus.extension.plugins.chain;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.annotation.DbType;
|
|
|
+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.SqlInfo;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.ParameterUtils;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.DialectFactory;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.DialectModel;
|
|
|
+import com.baomidou.mybatisplus.extension.plugins.pagination.dialects.IDialect;
|
|
|
+import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
|
|
|
+import com.baomidou.mybatisplus.extension.toolkit.SqlParserUtils;
|
|
|
+import lombok.Data;
|
|
|
+import net.sf.jsqlparser.JSQLParserException;
|
|
|
+import net.sf.jsqlparser.parser.CCJSqlParserUtil;
|
|
|
+import net.sf.jsqlparser.statement.select.*;
|
|
|
+import org.apache.ibatis.cache.CacheKey;
|
|
|
+import org.apache.ibatis.executor.Executor;
|
|
|
+import org.apache.ibatis.mapping.BoundSql;
|
|
|
+import org.apache.ibatis.mapping.MappedStatement;
|
|
|
+import org.apache.ibatis.mapping.ParameterMapping;
|
|
|
+import org.apache.ibatis.session.Configuration;
|
|
|
+import org.apache.ibatis.session.ResultHandler;
|
|
|
+import org.apache.ibatis.session.RowBounds;
|
|
|
+
|
|
|
+import java.sql.SQLException;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.Optional;
|
|
|
+
|
|
|
+/**
|
|
|
+ * @author miemie
|
|
|
+ * @since 2020-06-16
|
|
|
+ */
|
|
|
+@Data
|
|
|
+public class PageBeforeQuery implements BeforeQuery {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * COUNT SQL 解析
|
|
|
+ */
|
|
|
+ protected ISqlParser countSqlParser;
|
|
|
+ /**
|
|
|
+ * 溢出总页数后是否进行处理
|
|
|
+ */
|
|
|
+ protected boolean overflow = false;
|
|
|
+ /**
|
|
|
+ * 单页限制 500 条,小于 0 如 -1 不受限制
|
|
|
+ */
|
|
|
+ protected long limit = 500L;
|
|
|
+ /**
|
|
|
+ * 数据库类型
|
|
|
+ *
|
|
|
+ * @since 3.3.1
|
|
|
+ */
|
|
|
+ private DbType dbType;
|
|
|
+ /**
|
|
|
+ * 方言实现类
|
|
|
+ *
|
|
|
+ * @since 3.3.1
|
|
|
+ */
|
|
|
+ private IDialect dialect;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public BoundSql change(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
|
|
|
+ // 判断参数里是否有page对象
|
|
|
+ IPage<?> page = ParameterUtils.findPage(parameter).orElse(null);
|
|
|
+ /*
|
|
|
+ * 不需要分页的场合,如果 size 小于 0 返回结果集
|
|
|
+ */
|
|
|
+ if (null == page || page.getSize() < 0) {
|
|
|
+ return boundSql;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.limit > 0 && this.limit <= page.getSize()) {
|
|
|
+ //处理单页条数限制
|
|
|
+ handlerLimit(page);
|
|
|
+ }
|
|
|
+ String originalSql = boundSql.getSql();
|
|
|
+ if (page.isSearchCount() && !page.isHitCount()) {
|
|
|
+ SqlInfo sqlInfo = SqlParserUtils.getOptimizeCountSql(page.optimizeCountSql(), countSqlParser, originalSql, null);
|
|
|
+ ms = buildCountMappedStatement(ms);
|
|
|
+ CacheKey cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
|
|
|
+ long count = (long) executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql).get(0);
|
|
|
+ page.setTotal(count);
|
|
|
+ if (!this.continueLimit(page)) {
|
|
|
+ return boundSql;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ DbType dbType = this.dbType == null ? JdbcUtils.getDbType(JdbcUtils.getJdbcUrl(ms)) : this.dbType;
|
|
|
+ IDialect dialect = Optional.ofNullable(this.dialect).orElseGet(() -> DialectFactory.getDialect(dbType));
|
|
|
+ String buildSql = concatOrderBy(originalSql, page);
|
|
|
+ DialectModel model = dialect.buildPaginationSql(buildSql, page.offset(), page.getSize());
|
|
|
+ final Configuration configuration = ms.getConfiguration();
|
|
|
+ List<ParameterMapping> mappings = new ArrayList<>(boundSql.getParameterMappings());
|
|
|
+ Map<String, Object> additionalParameter = JdbcUtils.getAdditionalParameter(boundSql);
|
|
|
+ model.consumers(mappings, configuration, additionalParameter);
|
|
|
+ boundSql = new BoundSql(configuration, model.getDialectSql(), mappings, parameter);
|
|
|
+ for (Map.Entry<String, Object> entry : additionalParameter.entrySet()) {
|
|
|
+ boundSql.setAdditionalParameter(entry.getKey(), entry.getValue());
|
|
|
+ }
|
|
|
+ return boundSql;
|
|
|
+ }
|
|
|
+
|
|
|
+ private MappedStatement buildCountMappedStatement(MappedStatement ms) {
|
|
|
+ //todo
|
|
|
+ return new MappedStatement.Builder(ms.getConfiguration(), ).build();
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 查询SQL拼接Order By
|
|
|
+ *
|
|
|
+ * @param originalSql 需要拼接的SQL
|
|
|
+ * @param page page对象
|
|
|
+ * @return ignore
|
|
|
+ */
|
|
|
+ public String concatOrderBy(String originalSql, IPage<?> page) {
|
|
|
+ if (CollectionUtils.isNotEmpty(page.orders())) {
|
|
|
+ try {
|
|
|
+ List<OrderItem> orderList = page.orders();
|
|
|
+ Select selectStatement = (Select) CCJSqlParserUtil.parse(originalSql);
|
|
|
+ if (selectStatement.getSelectBody() instanceof PlainSelect) {
|
|
|
+ PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody();
|
|
|
+ List<OrderByElement> orderByElements = plainSelect.getOrderByElements();
|
|
|
+ List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);
|
|
|
+ plainSelect.setOrderByElements(orderByElementsReturn);
|
|
|
+ return plainSelect.toString();
|
|
|
+ } else if (selectStatement.getSelectBody() instanceof SetOperationList) {
|
|
|
+ SetOperationList setOperationList = (SetOperationList) selectStatement.getSelectBody();
|
|
|
+ List<OrderByElement> orderByElements = setOperationList.getOrderByElements();
|
|
|
+ List<OrderByElement> orderByElementsReturn = addOrderByElements(orderList, orderByElements);
|
|
|
+ setOperationList.setOrderByElements(orderByElementsReturn);
|
|
|
+ return setOperationList.toString();
|
|
|
+ } else if (selectStatement.getSelectBody() instanceof WithItem) {
|
|
|
+ // todo: don't known how to resole
|
|
|
+ return originalSql;
|
|
|
+ } else {
|
|
|
+ return originalSql;
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (JSQLParserException e) {
|
|
|
+ logger.warn("failed to concat orderBy from IPage, exception=" + e.getMessage());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return originalSql;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 判断是否继续执行 Limit 逻辑
|
|
|
+ *
|
|
|
+ * @param page 分页对象
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ protected boolean continueLimit(IPage<?> page) {
|
|
|
+ if (page.getTotal() <= 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (page.getCurrent() > page.getPages()) {
|
|
|
+ if (this.overflow) {
|
|
|
+ //溢出总页数处理
|
|
|
+ handlerOverflow(page);
|
|
|
+ } else {
|
|
|
+ // 超过最大范围,未设置溢出逻辑中断 list 执行
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理超出分页条数限制,默认归为限制数
|
|
|
+ *
|
|
|
+ * @param page IPage
|
|
|
+ */
|
|
|
+ protected void handlerLimit(IPage<?> page) {
|
|
|
+ page.setSize(this.limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 处理页数溢出,默认设置为第一页
|
|
|
+ *
|
|
|
+ * @param page IPage
|
|
|
+ */
|
|
|
+ protected void handlerOverflow(IPage<?> page) {
|
|
|
+ page.setCurrent(1);
|
|
|
+ }
|
|
|
+}
|