Mybatis中分頁存在的坑1


站在巨人的肩膀上

https://www.cnblogs.com/esileme/p/7565184.html

環境:Spring 4.2.1 Mybatis 3.2.8 pagehelper 5.1.2

Mybatis官方教程:https://github.com/pagehelper/Mybatis-PageHelper/blob/d5947437cc6272cb3f1b186cadee74f1f8072cbb/wikis/zh/HowToUse.md

  • 加入pageHelper依賴

     <dependency>
          <groupId>com.github.pagehelper</groupId>
          <artifactId>pagehelper</artifactId>
          <version>5.1.2</version>
      </dependency>

     

  • 在Mybatis-config.xml中配置pageHelper的攔截器

     <plugins>
          <!-- com.github.pagehelper為PageHelper類所在包名 -->
          <plugin interceptor="com.github.pagehelper.PageInterceptor">
              <!-- 使用下面的方式配置參數,后面會有所有的參數介紹 -->
              <property name="helperDialect" value="mysql" />
              <property name="reasonable" value="true" />
              <property name="supportMethodsArguments" value="true" />
              <property name="autoRuntimeDialect" value="true" />
    
          </plugin>
    
      </plugins>

     

  • 調用startPage()分頁方法,在獲取數據后將數據放到PageInfo對象中。

     PageHelper.startPage(page, pageSize);
     List<Map<String, Object>> list = itemMapper.getItems();
     PageInfo<Map<String, Object>> pageInfor = new PageInfo<Map<String, Object>>(list);

     

即可對數據進行分頁處理。

Mybatis使用pageHelper分頁出現的問題

在pageHelper的文檔中,我們可以看到這樣的提示分頁插件不支持嵌套結果映射https://github.com/pagehelper/Mybatis-PageHelper/blob/d5947437cc6272cb3f1b186cadee74f1f8072cbb/wikis/zh/Important.md),由於嵌套結果方式會導致結果集被折疊,因此分頁查詢的結果在折疊后總數會減少,所以無法保證分頁結果數量正確。這時候對於一些嵌套查詢來說使用通用pageHelper已經不能用了,因此需要我們通過手動分別查詢分頁數據以及分頁數據的映射數據。

通過使用一個例子對pageHelper進行測試不兼容嵌套查詢的結果映射:

 <resultMap type="java.util.Map" id="fatherMap">
        <result column="orderId" property="orderId" />
        <result column="payment" property="payment" />
        <result column="buyer_nick" property="buyerNick" />
        <result column="shipping_code" property="shippingCode" />
        <result column="state" property="state" />
    </resultMap>

    <resultMap type="java.util.Map" id="orderMap" extends="fatherMap">

        <collection property="items" javaType="ArrayList" resultMap="itemMap" />

    </resultMap>

    <select id="getOrdersByUserId" resultMap="orderMap">

        SELECT o.order_id AS orderId,o.payment ,o.buyer_nick,o.shipping_code
        ,o.status AS orderState,i.title ,i.price FROM tb_order AS o,tb_item AS
        i,tb_order_item AS oi WHERE o.user_id=#{userId} AND
        oi.order_id=o.order_id AND i.id=oi.item_id

    </select>
   /*Service層代碼*/
    public List<Map<String, Object>> getOrdersByUserId(String userId) {

        PageHelper.startPage(1, 2);
        List<Map<String, Object>> list = orderMapper.getOrdersByUserId(userId);
        return list;

    }

通過嵌套查詢返回一個Map對象並嵌套了另一個表中的多條數據(List集合),在取出數據后,可以看到這樣的結果:

可以發現,pageHelper對我們的子數據進行了分頁,我們要兩條數據且只是出現了一條數據,這個時候,就只有通過分別查詢來取出數據了。

首先,對訂單數據不進行關聯查詢,先把數據取出來,然后根據取出來的訂單對象,再把關聯的信息查出來,這樣就可以使用pageHelper來進行操作了。在Service層的代碼如下:

    public List<Map<String, Object>> getAnotherOrdersByUserId(String userId) { PageHelper.startPage(1, 2); List<Map<String, Object>> orderMaps = orderMapper.getAnotherOrdersByUserId(userId); for (Map<String, Object> map : orderMaps) { String orderId = (String) map.get("orderId"); List<Map<String, Object>> items = orderMapper.getItems(orderId); map.put("items", items); } PageInfo pageInfor = new PageInfo(orderMaps); System.out.println(pageInfor.getPageNum()); System.out.println(pageInfor.getTotal()); return orderMaps; }

mapper的代碼:

<select id="getAnotherOrdersByUserId" resultMap="fatherMap"> SELECT o.order_id AS orderId,o.payment ,o.buyer_nick,o.shipping_code ,o.status AS orderState FROM tb_order AS o WHERE o.user_id=#{userId} </select> <select id="getItems" resultMap="itemMap"> SELECT item.title ,item.price FROM tb_order_item AS oi ,tb_item AS item WHERE oi.order_id=#{orderId} AND oi.item_id=item.id </select>

這樣通過分別查詢就可以避免了pageHelper的聯合查詢的問題。

正確的數據顯示:

Demo:https://github.com/esileme/MybatisPager

PageHelper分頁原理

在Mybatis中,提供了一個Interceptor接口,Mybatis的PageInterceptor類實現了Interceptor接口,在這個接口中,MyBatis允許我們攔截四個方法(ParameterHandler、ResultSetHandler、StatementHandler、Executor),可以對這四個方法進行攔截並實現相應的操作。

我們可以自定義實現Interceptor方式對Mybatis返回為Map的對象進行數據庫數據與pojo對象映射的操作:

  • 在Mybatis-config.xml中配置

    <plugin interceptor="com.yl.page.intercpter.CameHumpIntercpter"></plugin>
    code
  • package com.yl.page.intercpter;
    
    import java.sql.Statement;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Map;
    import java.util.Properties;
    import java.util.Set;
    
    import org.apache.ibatis.executor.resultset.ResultSetHandler;
    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;
    
    @Intercepts(
    
    @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }))
    public class CameHumpIntercpter implements Interceptor {
    
        public Object intercept(Invocation invocation) throws Throwable {
            List<Object> values = (List<Object>) invocation.proceed();
            for (Object object : values) {
    
                if (object instanceof Map) {
                    processMap((Map) object);
    
                } else {
                    break;
                }
            }
            return values;
        }
    
        public Object plugin(Object target) {
    
            return Plugin.wrap(target, this);
        }
    
        // 獲取配置參數
        public void setProperties(Properties properties) {
    
        }
    
        private void processMap(Map<String, Object> map) {
            Set<String> keySet = new HashSet<String>(map.keySet());
            for (String key : keySet) {
    
                if ((key.charAt(0) >= 'A' && key.charAt(0) <= 'Z') || key.indexOf("_") >= 0) {
    
                    Object value = map.get(key);
                    map.remove(key);
                    map.put(undelineToNomall(key), value);
                }
            }
    
        }
    
        public String undelineToNomall(String inputString) {
            StringBuilder buder = new StringBuilder();
            boolean nextUpperCase = false;
            for (int i = 0; i < inputString.length(); i++) {
    
                char c = inputString.charAt(i);
                if (c == '_') {
                    if (buder.length() > 0) {
                        nextUpperCase = true;
                    }
    
                } else {
    
                    if (nextUpperCase) {
                        buder.append(Character.toUpperCase(c));
    
                        nextUpperCase = false;
                    } else {
                        buder.append(Character.toLowerCase(c));
                    }
    
                }
    
            }
    
            return buder.toString();
        }
    
    }

     


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM