1. 程式人生 > >支援連表等複雜查詢的PageHelper修改 —— 支援PageHelper5.1.4 Mybatis3.4.6

支援連表等複雜查詢的PageHelper修改 —— 支援PageHelper5.1.4 Mybatis3.4.6

一、PageHelper中分頁存在的問題:

- 主表結構如下:

provider_order表,訂單主表。我們這裡簡化處理,它只有一個欄位,即id。

VARCHAR  |  po_id   |    訂單表id

- 從表結構如下:

order_info表,訂單從表。它負責記錄訂單的一些資訊,與主表是一對多的關係。

VARCHAR  |  oi_id  |  訂單資訊id
VARCHAR  |  po_id   |    訂單表id
TEXT    |  info   |   資訊詳情

  • 現在我們需要一個簡單的連表查詢操作,sql語句如下:
    <select
id="getProviderOrderWithOrderInfoTest" resultMap="ResultMapWithOrderInfo" parameterType="java.lang.String">
SELECT * FROM (SELECT * FROM provider_order WHERE po_id LIKE #{po_id}) AS limitable LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id </select
>
  • 進行PageHelper的分頁操作,查詢前十條記錄:
        Page page = PageHelper.startPage(0, 10);
        List<ProviderOrder> providerOrderList = testService.getProviderOrderWithOrderInfoTest("%-%");
        PageInfo<ProviderOrder> pageInfo = new PageInfo<>(providerOrderList);

        System.out.println(pageInfo);
  • 目前我們的主表有兩條記錄,從表有二十條記錄。

這裡寫圖片描述

我們會發現,查詢出來的總數是20,然後查詢結果是兩個list(各5條從表資訊),不僅如此,分頁資料也很奇怪,我們只有兩條主表記錄,但卻被分成了兩頁(pages = 2),並且hasNextPage = true。這並不是我們要的結果。我們想要查詢的效果是:

前十條 訂單 記錄,然後連表查詢出該十條訂單記錄下的所有 訂單資訊

也就是說,結果集中,首先我們應該得到兩個list,list中各10條從表資訊,而且也不該有下一頁,總頁數應為1,總資料數應為2。

檢視PageHelper的原始碼我們來看看,它的count,以及limit,到底是怎麼回事。

二、 PageHelper的count實現

    private Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException,
        SQLException {
        Map<String, Object> additionalParameters = (Map) this.additionalParametersField.get(boundSql);
        CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
        String countSql = this.dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
        BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
        Iterator var11 = additionalParameters.keySet()
                                             .iterator();

        while (var11.hasNext()) {
            String key = (String) var11.next();
            countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
        }

        Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
        Long count = (Long) ((List) countResultList).get(0);

        // 獲取count
        return count;
    }

我們很容易看出來,count的sql語句,在這裡經過了處理,這裡呼叫了dialect的getCountSql方法。
它的實現如下:

    public String getSmartCountSql(String sql, String name) {
        Statement stmt = null;
        if (sql.indexOf("/*keep orderby*/") >= 0) {
            return this.getSimpleCountSql(sql);
        } else {
            try {
                stmt = CCJSqlParserUtil.parse(sql);
            } catch (Throwable var8) {
                return this.getSimpleCountSql(sql);
            }

            Select select = (Select)stmt;
            SelectBody selectBody = select.getSelectBody();

            try {
                this.processSelectBody(selectBody);
            } catch (Exception var7) {
                return this.getSimpleCountSql(sql);
            }

            this.processWithItemsList(select.getWithItemsList());
            this.sqlToCount(select, name);
            String result = select.toString();
            return result;
        }
    }

    public String getSimpleCountSql(String sql) {
        return this.getSimpleCountSql(sql, "0");
    }

    public String getSimpleCountSql(String sql, String name) {
        StringBuilder stringBuilder = new StringBuilder(sql.length() + 40);
        stringBuilder.append("select count(");
        stringBuilder.append(name);
        stringBuilder.append(") from (");
        stringBuilder.append(sql);
        stringBuilder.append(") tmp_count");
        return stringBuilder.toString();
    }

簡單來說,就是在sql外面套一層select count語句,我們的sql語句變成了 SELECT count(0) FROM (我們的sql語句)(真實情況沒有這麼簡單,這只是其中一種情況。)這樣就很容易得知,為什麼最後的total是20(所有資料數),而不是2(主表資料數)了。

這裡寫圖片描述

三、 PageHelper的分頁實現

與上面相同,這裡以dialect(為了適配各種資料庫)為mysql為例,我們可以看到,如果你沒有在sql語句裡面寫limit,就會在sql語句的最後面,新增limit語句。

    public String getPageSql(String sql, RowBounds rowBounds, CacheKey pageKey) {
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (rowBounds.getOffset() == 0) {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(rowBounds.getLimit());
        } else {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(rowBounds.getOffset());
            sqlBuilder.append(",");
            sqlBuilder.append(rowBounds.getLimit());
            pageKey.update(rowBounds.getOffset());
        }

        pageKey.update(rowBounds.getLimit());
        return sqlBuilder.toString();
    }

程式執行得到sql語句如下,PageHelper對查詢的總結果集(包括主表資料與從表資料),進行了分頁

這裡寫圖片描述

在得知PageHelper在這兩個步驟的原理後,我們開始對PageHelper進行改造。

四、 count結果修正

在上面的count原始碼中,我們可以看到這麼一段程式碼:

    public String getSmartCountSql(String sql, String name) {
        Statement stmt = null;
        if (sql.indexOf("/*keep orderby*/") >= 0) {
            return this.getSimpleCountSql(sql);
        } else {
            try {
                stmt = CCJSqlParserUtil.parse(sql);
            } catch (Throwable var8) {
                return this.getSimpleCountSql(sql);
            }

            Select select = (Select)stmt;
            SelectBody selectBody = select.getSelectBody();

            try {
                this.processSelectBody(selectBody);
            } catch (Exception var7) {
                return this.getSimpleCountSql(sql);
            }

            this.processWithItemsList(select.getWithItemsList());
            this.sqlToCount(select, name);
            String result = select.toString();
            return result;
        }
    }


    public void sqlToCount(Select select, String name) {
        SelectBody selectBody = select.getSelectBody();
        List<SelectItem> COUNT_ITEM = new ArrayList();
        COUNT_ITEM.add(new SelectExpressionItem(new Column("count(" + name + ")")));
        if (selectBody instanceof PlainSelect && this.isSimpleCount((PlainSelect)selectBody)) {
            ((PlainSelect)selectBody).setSelectItems(COUNT_ITEM);
        } else {
            PlainSelect plainSelect = new PlainSelect();
            SubSelect subSelect = new SubSelect();
            subSelect.setSelectBody(selectBody);
            subSelect.setAlias(TABLE_ALIAS);
            plainSelect.setFromItem(subSelect);
            plainSelect.setSelectItems(COUNT_ITEM);
            select.setSelectBody(plainSelect);
        }

    }

實際上PageHelper是有提供給我們一套指定colum查詢的入口的。我們將查詢程式碼做如下改造:

        Page page = PageHelper.startPage(0, 10);
        page.setCountColumn("DISTINCT po_id");
        List<ProviderOrder> providerOrderList = testService.getProviderOrderWithOrderInfoTest("%-%");

        PageInfo<ProviderOrder> pageInfo = new PageInfo<>(providerOrderList);

        System.out.println(pageInfo);

執行查詢,得到結果如下:

這裡寫圖片描述

我們會發現,我們需要的分頁資料正常了,總頁碼(pages:1),total:2。

五、 查詢結果集修正

我們在三中,發現了PageHelper limit新增的位置不正確,我們需要將其放在正確的位置下。所以新建一個類如下:

/**
 * Created by Anur IjuoKaruKas on 2018/6/8
 */
public class PageSqlResolver {

    public static final String SQL_SIGN = "AS limitable";

    public static final String LIMIT_SIGN = "LIMIT ?";

    public static final String LIMIT_SIGN_EX = "LIMIT ?, ?";

    public static String resolveLimit(String pageSql) {
        if (pageSql == null) {
            return null;
        }

        if (pageSql.contains(SQL_SIGN)) {// 如果需要特殊分頁
            String changer = "";

            if (pageSql.contains(LIMIT_SIGN_EX)) {
                changer = LIMIT_SIGN_EX;
            } else if (pageSql.contains(LIMIT_SIGN)) {
                changer = LIMIT_SIGN;
            }

            pageSql = pageSql.replace(changer, "");
            StringBuilder sqlBuilder = new StringBuilder(pageSql);

            StringBuilder mae = new StringBuilder(sqlBuilder.substring(0, sqlBuilder.indexOf(SQL_SIGN)));// mae 截止sql語句到 limitable
            StringBuilder uShiRo = new StringBuilder(sqlBuilder.substring(sqlBuilder.indexOf(SQL_SIGN), sqlBuilder.length()));// 剩餘的

            mae.insert(mae.lastIndexOf(")"), String.format(" %s", changer));

            return mae.append(uShiRo)
                      .toString();
        } else {
            return pageSql;
        }
    }
}

這段程式碼的含義是:將AS limitable,也就是主表,標記為真正分頁的地方,並向其添如limit ? ,?

SELECT *
        FROM (SELECT * FROM provider_order WHERE po_id LIKE ?) AS limitable
            LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id LIMIT ? 

sql語句從上面變成了:

SELECT *
        FROM (SELECT * FROM provider_order WHERE po_id LIKE ? LIMIT ?) AS limitable
            LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id  

執行程式,得到結果:
這裡寫圖片描述

我們發現這就是我們想要的結果,它的分頁是針對主表進行的,而不是針對結果集。

解析到這裡就結束了

六、 改造步驟詳解

1、引入依賴

這裡使用了mybatis 3.4.6(tk.mybatis 4.0.2),pagehelper5.1.4(pagehelper-spring-boot-starter1.2.5),你可以引入原依賴,也可以用這裡使用的tkMybatis,以及pagehelper-spring-boot-starter。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.anur</groupId>
    <artifactId>page-helper</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>page-helper</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- page - helper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.5</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- codeGen -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.6</version>
        </dependency>

        <!-- tk - mybatis -->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper</artifactId>
            <version>4.0.2</version>
        </dependency>

        <!-- mysql -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.47</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <proc>none</proc>
                </configuration>
            </plugin>
        </plugins>
    </build>


</project>

2、新增兩個類,用於修正查詢時sql語句

package com.anur.pagehelper.page;

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.github.pagehelper.Dialect;
import com.github.pagehelper.PageException;
import com.github.pagehelper.cache.Cache;
import com.github.pagehelper.cache.CacheFactory;
import com.github.pagehelper.util.MSUtils;
import com.github.pagehelper.util.StringUtil;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
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.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.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * Created by Anur IjuoKaruKas on 2018/6/8
 */

@Intercepts({
    @Signature(
        type = Executor.class,
        method = "query",
        args = {
            MappedStatement.class,
            Object.class,
            RowBounds.class,
            ResultHandler.class
        }
    ),
    @Signature(
        type = Executor.class,
        method = "query",
        args = {
            MappedStatement.class,
            Object.class,
            RowBounds.class,
            ResultHandler.class,
            CacheKey.class,
            BoundSql.class
        }
    )
})
public class CustomPageInterceptor implements Interceptor {

    protected Cache<String, MappedStatement> msCountMap = null;

    private Dialect dialect;

    private String default_dialect_class = "com.github.pagehelper.PageHelper";

    private Field additionalParametersField;

    private String countSuffix = "_COUNT";

    public CustomPageInterceptor() {
    }

    /**
     * 攔截
     */
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            Object[] args = invocation.getArgs();

            // 對應一個Mapper節點,描述一條sql語句
            MappedStatement ms = (MappedStatement) args[0];

            // 引數
            Object parameter = args[1];

            // mybatis自帶分頁
            RowBounds rowBounds = (RowBounds) args[2];

            // 結果集處理器
            ResultHandler resultHandler = (ResultHandler) args[3];

            // 執行器
            Executor executor = (Executor) invocation.getTarget();
            CacheKey cacheKey;
            BoundSql boundSql;

            if (args.length == 4) {
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                cacheKey = (CacheKey) args[4];
                boundSql = (BoundSql) args[5];
            }

            List resultList;
            if (this.dialect.skip(ms, parameter, rowBounds)) {
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            } else {
                String msId = ms.getId();
                Configuration configuration = ms.getConfiguration();
                Map<String, Object> additionalParameters = (Map) this.additionalParametersField.get(boundSql);
                if (this.dialect.beforeCount(ms, parameter, rowBounds)) {
                    String countMsId = msId + this.countSuffix;
                    MappedStatement countMs = this.getExistedMappedStatement(configuration, countMsId);
                    Long count;
                    if (countMs != null) {
                        count = this.executeManualCount(executor, countMs, parameter, boundSql, resultHandler);
                    } else {
                        countMs = (MappedStatement) this.msCountMap.get(countMsId);
                        if (countMs == null) {
                            countMs = MSUtils.newCountMappedStatement(ms, countMsId);
                            this.msCountMap.put(countMsId, countMs);
                        }

                        // 這裡是count,可能要改
//                        boundSql = PageSqlResolver.resolveCount(configuration, boundSql, parameter);
                        count = this.executeAutoCount(executor, countMs, parameter, boundSql, rowBounds, resultHandler);
                    }

                    if (!this.dialect.afterCount(count, parameter, rowBounds)) {
                        Object var24 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds);
                        return var24;
                    }
                }

                if (!this.dialect.beforePage(ms, parameter, rowBounds)) {
                    resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
                } else {
                    parameter = this.dialect.processParameterObject(ms, parameter, boundSql, cacheKey);
                    String pageSql = this.dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey);

                    // 對sql進行改造
                    pageSql = PageSqlResolver.resolveLimit(pageSql);

                    BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
                    Iterator var17 = additionalParameters.keySet()
                                                         .iterator();

                    while (true) {
                        if (!var17.hasNext()) {
                            resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, pageBoundSql);
                            break;
                        }

                        String key = (String) var17.next();
                        pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
                    }
                }
            }

            Object var22 = this.dialect.afterPage(resultList, parameter, rowBounds);
            return var22;
        } finally {
            this.dialect.afterAll();
        }
    }

    private Long executeManualCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, ResultHandler resultHandler) throws IllegalAccessException, SQLException {
        CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
        BoundSql countBoundSql = countMs.getBoundSql(parameter);
        Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
        Long count = ((Number) ((List) countResultList).get(0)).longValue();
        return count;
    }

    private Long executeAutoCount(Executor executor, MappedStatement countMs, Object parameter, BoundSql boundSql, RowBounds rowBounds, ResultHandler resultHandler) throws IllegalAccessException,
        SQLException {
        Map<String, Object> additionalParameters = (Map) this.additionalParametersField.get(boundSql);
        CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);
        String countSql = this.dialect.getCountSql(countMs, boundSql, parameter, rowBounds, countKey);
        BoundSql countBoundSql = new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);
        Iterator var11 = additionalParameters.keySet()
                                             .iterator();

        while (var11.hasNext()) {
            String key = (String) var11.next();
            countBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
        }

        Object countResultList = executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);
        Long count = (Long) ((List) countResultList).get(0);

        // 獲取count
        return count;
    }

    private MappedStatement getExistedMappedStatement(Configuration configuration, String msId) {
        MappedStatement mappedStatement = null;

        try {
            mappedStatement = configuration.getMappedStatement(msId, false);
        } catch (Throwable var5) {
            ;
        }

        return mappedStatement;
    }

    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    public void setProperties(Properties properties) {
        this.msCountMap = CacheFactory.createCache(properties.getProperty("msCountCache"), "ms", properties);
        String dialectClass = properties.getProperty("dialect");
        if (StringUtil.isEmpty(dialectClass)) {
            dialectClass = this.default_dialect_class;
        }

        try {
            Class<?> aClass = Class.forName(dialectClass);
            this.dialect = (Dialect) aClass.newInstance();
        } catch (Exception var6) {
            throw new PageException(var6);
        }

        this.dialect.setProperties(properties);
        String countSuffix = properties.getProperty("countSuffix");
        if (StringUtil.isNotEmpty(countSuffix)) {
            this.countSuffix = countSuffix;
        }

        try {
            this.additionalParametersField = BoundSql.class.getDeclaredField("additionalParameters");
            this.additionalParametersField.setAccessible(true);
        } catch (NoSuchFieldException var5) {
            throw new PageException(var5);
        }
    }
}

package com.anur.pagehelper.page;

/**
 * Created by Anur IjuoKaruKas on 2018/6/8
 */
public class PageSqlResolver {

    public static final String SQL_SIGN = "AS limitable";

    public static final String LIMIT_SIGN = "LIMIT ?";

    public static final String LIMIT_SIGN_EX = "LIMIT ?, ?";

    public static String resolveLimit(String pageSql) {
        if (pageSql == null) {
            return null;
        }

        if (pageSql.contains(SQL_SIGN)) {// 如果需要特殊分頁
            String changer = "";

            if (pageSql.contains(LIMIT_SIGN_EX)) {
                changer = LIMIT_SIGN_EX;
            } else if (pageSql.contains(LIMIT_SIGN)) {
                changer = LIMIT_SIGN;
            }

            pageSql = pageSql.replace(changer, "");
            StringBuilder sqlBuilder = new StringBuilder(pageSql);

            StringBuilder mae = new StringBuilder(sqlBuilder.substring(0, sqlBuilder.indexOf(SQL_SIGN)));// mae 截止sql語句到 limitable
            StringBuilder uShiRo = new StringBuilder(sqlBuilder.substring(sqlBuilder.indexOf(SQL_SIGN), sqlBuilder.length()));// 剩餘的

            mae.insert(mae.lastIndexOf(")"), String.format(" %s", changer));

            return mae.append(uShiRo)
                      .toString();
        } else {
            return pageSql;
        }
    }
}

3、新增配置檔案如下

注意,這裡配置了我們的CustomPageInterceptor(2步驟裡的那個類)作為外掛,如果不進行此處配置,我們的修改是不起作用的。

/**
 * Created by Anur IjuoKaruKas on 2017/12/13.
 */
@Configuration
public class MybatisConfiguration {

    @Bean
    public SqlSessionFactory sqlSessionFactoryBean(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        factory.setTypeAliasesPackage(Constant.MODEL_PACKAGE);

        // 配置分頁外掛,詳情請查閱官方文件
        PageHelperProperties properties = new PageHelperProperties();
        properties.setPageSizeZero("true");// 分頁尺寸為0時查詢所有紀錄不再執行分頁
        properties.setReasonable("true");// 頁碼<=0 查詢第一頁,頁碼>=總頁數查詢最後一頁
        properties.setSupportMethodsArguments("true");// 支援通過 Mapper 介面引數來傳遞分頁引數

        CustomPageInterceptor pageInterceptor = new CustomPageInterceptor();
        pageInterceptor.setProperties(properties.getProperties());
        //  新增外掛
        factory.setPlugins(new Interceptor[]{pageInterceptor});

        // 新增XML目錄
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        factory.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
        return factory.getObject();
    }
}

4、sql語句注意事項

在需要被查詢的主表上,你需要手動包裝一層select,並將其別名設定為limitable。AS必須大寫。

SELECT *
FROM (SELECT *
      FROM provider_order) AS limitable
  LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id

這是原語句:

SELECT *
      FROM provider_order limitable
  LEFT JOIN order_info ON limitable.po_id = order_info.provider_order_id

5、查詢時注意事項

在查詢之前,我們需要將主表的主鍵(必須唯一)設定到countColumn中。

        Page page = PageHelper.startPage(0, 10);
        page.setCountColumn("DISTINCT po_id");
        List<ProviderOrder> providerOrderList = testService.getProviderOrderWithOrderInfoTest("%-%");

如果已經確保完成了上面的五個步驟,執行查詢即可,我們將得到正確的連表查詢結果。

這個改造並不能完全適應所有的複雜查詢,不過它提供了一種思路,你可以按照自己的實際情況做更好的調整!

相關推薦

支援複雜查詢PageHelper修改

新版本的PageHelper與mybatis請移步這裡——支援PageHelper5.1.4 Mybatis3.4.6 先說說使用PageHelper踩過的坑: - 在mapper對映檔案中,如果使用了limit關鍵字,而又使用了PageHelper的

支援複雜查詢PageHelper修改 —— 支援PageHelper5.1.4 Mybatis3.4.6

一、PageHelper中分頁存在的問題: - 主表結構如下: provider_order表,訂單主表。我們這裡簡化處理,它只有一個欄位,即id。 VARCHAR  |  po_id   |    訂單表id - 從表結

(ORACLE)PL/SQL 複雜查詢

表的複雜查詢 在實際應用中,常常需要執行復雜的資料統計,經常需要顯示多張表的資料現在我們來學習比較複雜的select的語句。我們將繼續使用scott使用者下emp表作為示例。 聚合函式 MAX函式: 對一列取最大值 MIN函式: 對一列取最小值 AVG函式: 對一列取平均值 SU

MySQL:記錄的增刪改查、單查詢、約束條件、多查詢、子查詢、pymysql模組、MySQL內建功能

資料操作 插入資料(記錄): 用insert; 補充:插入查詢結果: insert into 表名(欄位1,欄位2,...欄位n) select (欄位1,欄位2,...欄位n) where ...; 更新資料update 語法: update 表名 set 欄位1=

複雜查詢----多查詢、兩的條件連線、內連線(自連線)、左外連線、右外連線、子查詢、分頁查詢

基於兩個或兩個以上的表或檢視的查詢。例如:emp和dept是兩張表。(這兩張表的deptno是共同欄位)    ①通過什麼條件把兩張表關聯(如果不使用條件關聯將產生行數乘積的合集---笛卡爾集)例子:emp表字段包含:每行有自己的名字和id以及自己的上級的id,同時自己的上級

MySQL複雜查詢 —— 跨/多查詢,如何解決MySQL不支援全連線的問題 —— 使用結果集的合併

  SELECT  sex,  MAX(salary),  MIN(salary),  AVG(salary)   FROM  emp   GROUP  BY  sex; 5.MySQL複雜查詢 —— 跨表/多表查詢 —— 瞭解 查詢結果集中的資料來自於多個不同的

不同服務器數據庫查詢修改

server dlink 數據庫 exe bsp 連接查詢 sql 對數 als 不同服務器數據庫表連接查詢,修改 exec sp_addlinkedserver ‘ERP‘,‘‘,‘SQLOLEDB‘,‘10.0.10.0‘ exec sp_addlinkedsrvl

介紹 複雜查詢,包括多關聯,分頁,排序

本篇進行Spring-data-jpa的介紹,幾乎涵蓋該框架的所有方面,在日常的開發當中,基本上能滿足所有需求。這裡不講解JPA和Spring-data-jpa單獨使用,所有的內容都是在和Spring整合的環境中實現。如果需要了解該框架的入門,百度一下,很多入門的介紹。在這篇文章的接下來一篇,會有一個

hive信息查詢:查看結構、操作--轉

cal ica class depend yun sel getting 結構 dconf 原文地址:http://www.aboutyun.com/forum.PHP?mod=viewthread&tid=8590&highlight=Hive 問題導讀

查詢(3)之根據相關條件,查詢另外一個或多個的某字段

兩個 mode object 添加 就會 resultmap control var 多個 1、送檢單位 代理商是從代理商表中取得數據 銷售人員從銷售表中取得數據 說一個簡單的思路: sql語句找到連接兩個表的條件,把另外一個表要用到的字段 寫一個 <re

Java鏈接HBASE數據庫,創建一個,刪除一張修改,輸出插入,修改,數據刪除,數據獲取,顯示信息,過濾查詢,分頁查詢,地理hash

can charat nfa true 目錄結構 dfa byte sin extra 準備工作 1、創建Java的Maven項目 創建好的目錄結構如下: 另外註意junit的版本,最好不要太高,最開始筆者使用的junit4.12的,發現運行的時候會報錯。最後把Junit

查詢的簡單方法!!

etl point return rim plist turn public 方法 nbsp 在主表(Appointment)裏面private+副表的對象 private Department(副表) depa; private User(副表) u; 然後source

安裝vsphere client時出現“錯誤2229 無法在SQL查詢中加載錯誤

錯誤2229解決辦法:請按照以下步驟繼續進行升級:找到日誌文件 %TEMP%vim-sso-msi.log搜索上次安裝期間作為緩存文件的 *.mst 文件。例如: c:\Windows \Installer\xxxxx.mst找到該 *.mst 文件並將其刪除。重新安裝vsphere client即可本文出自

Linq to SQL 的查詢(轉)

equal query 交集 數據庫 調用 數據 變量 bst log 關於數據庫的查詢中經常需要用到多表的連接查詢,這裏就簡單地展示關於linq的查詢功能。 1、單表的查詢 [csharp] view plain copy var query = from

接和分組查詢

兩張 -- 多表連接查詢 數據行 產生 記錄 group bsp log 分組查詢:分組查詢就是按某一列分組,相同的列合並,得到結果可能他少於總記錄 使用group by分組查詢:按什麽分(年級、姓氏、地址、年齡)年級 分組查詢語法:Select * from <表名

SQL總結 查詢

表示 自然連接 簡單 put delete title null ros insert 連接查詢包括合並、內連接、外連接和交叉連接,如果涉及多表查詢,了解這些連接的特點很重要。 只有真正了解它們之間的區別,才能正確使用。 1、Union UNION 操作符用於合並兩個或

Sql查詢

teacher rom SQ 學生 全外連接 連接 教師表 tom 重點 1.Union:使用union是組合兩張表,消去表中重復行,兩張表查詢的結果有相同數量的列、列類型相似;UNION ALL,不消除重復行教師表: ID Name 101 Mrs Lee

在Mybatis中使用查詢的一次實際應用

多表關聯 del 應用 行記錄 全部 業務 val 一定的 att 以前在工作中很少使用多表關聯查詢,對連表查詢的具體作用和使用場景也沒有很直觀的認識,通過這次在項目中的實際應用,對此有了一定的認識,特記錄如下。 關聯表介紹: 分別是屬性表attr_info、屬性值表a

查詢都用Left Join吧

簡單 不知道 效率 行記錄 我們 主體 left 註意 說明 最近看同事的代碼,SQL連表查詢的時候很多時候用的是Inner Join,而我覺得對我們的業務而言,99.9%都應該使用Left Join(還有0.1%我不知道在哪),我用最簡單的方式來描述這兩者的區別,直接看圖

查詢

regex 另一個 employee 數據 tab png ima false 通配符 1.內鏈接查詢 1.查詢department表和employee表,d_id字段相等的數據 2.外連接查詢 1.左連接查詢:可以查詢出表1的所指的所有記錄,表2只能查詢出匹配的記錄