分頁實現的三種方式


 

 

分頁實現的三種方式

三種分頁的實現方式
(1)每次取查詢結果的所有數據,然后根據頁面顯示指定的記錄
(2)根據頁面只取一頁的數據,然后顯示這一頁,這里要構造sql語句
(3)取一定頁數的數據,就是前兩種的折中
實現分頁的步驟:
1.創建一個用於封裝分頁相關屬性及操作的類
2.從頁面增加分頁導航條的功能
3.實現分頁查詢功能,從頁面請求->Servlet->DAO的實現

這里還要注意的是這些數據是放在request還是session中,這里一一討論

1.一般不會放在session中,因為會占用大量內存,所以要放在request里面。
優點:實現比較簡單,查詢速度比較快。
缺點:占用內存多一些,網絡傳輸數據多。
對於數據量比較少的查詢這種方法比較合適。這里有人把數據放在session中,這樣換頁的時候就不用重新查詢,但是這樣是極其不好的,強烈建議不要這樣使用。

2.肯定不會放在session中,因為放在session中沒有意義。
優點:占用內存少。
缺點:比較麻煩,必須先獲得查詢結果的總數,因為要知道有多少紀錄才知道有多少頁。另外要構造分頁查詢語句,對於不同的數據庫是不一樣的。

一.借助數組進行分頁

  • 原理:進行數據庫查詢操作時,獲取到數據庫中所有滿足條件的記錄,保存在應用的臨時數組中,再通過List的subList方法,獲取到滿足條件的所有記錄。

  • 實現:

首先在dao層,創建StudentMapper接口,用於對數據庫的操作。在接口中定義通過數組分頁的查詢方法,如下所示:

List

創建StudentMapper.xml文件,編寫查詢的sql語句:

可以看出再編寫sql語句的時候,我們並沒有作任何分頁的相關操作。這里是查詢到所有的學生信息。

接下來在service層獲取數據並且進行分頁實現:

定義IStuService接口,並且定義分頁方法:

通過接收currPage參數表示顯示第幾頁的數據,pageSize表示每頁顯示的數據條數。

創建IStuService接口實現類StuServiceIml對方法進行實現,對獲取到的數組通過currPage和pageSize進行分頁:

通過subList方法,獲取到兩個索引間的所有數據。

最后在controller中創建測試方法:

通過用戶傳入的currPage和pageSize獲取指定數據。

二.借助Sql語句進行分頁

在了解到通過數組分頁的缺陷后,我們發現不能每次都對數據庫中的所有數據都檢索。然后在程序中對獲取到的大量數據進行二次操作,這樣對空間和性能都是極大的損耗。所以我們希望能直接在數據庫語言中只檢索符合條件的記錄,不需要在通過程序對其作處理。這時,Sql語句分頁技術橫空出世。

實現:通過sql語句實現分頁也是非常簡單的,只是需要改變我們查詢的語句就能實現了,即在sql語句后面添加limit分頁語句。

  • 首先還是在StudentMapper接口中添加sql語句查詢的方法,如下:

List

接下來還是在IStuService接口中定義方法,並且在StuServiceIml中對sql分頁實現。

ql分頁語句如下:select * from table limit index, pageSize;

所以在service中計算出currIndex:要開始查詢的第一條記錄的索引。


三.攔截器分頁

上面提到的數組分頁和sql語句分頁都不是我們今天講解的重點,今天需要實現的是利用攔截器達到分頁的效果。自定義攔截器實現了攔截所有以ByPage結尾的查詢語句,並且利用獲取到的分頁相關參數統一在sql語句后面加上limit分頁的相關語句,一勞永逸。不再需要在每個語句中單獨去配置分頁相關的參數了。。

首先我們看一下攔截器的具體實現,在這里我們需要攔截所有以ByPage結尾的所有查詢語句,因此要使用該攔截器實現分頁功能,那么再定義名稱的時候需要滿足它攔截的規則(以ByPage結尾),

 

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;
import java.util.Map;
import java.util.Properties;

/**

/**

  • @Intercepts 說明是一個攔截器
  • @Signature 攔截器的簽名
  • type 攔截的類型 四大對象之一( Executor,ResultSetHandler,ParameterHandler,StatementHandler)
  • method 攔截的方法
  • args 參數
    */
    @Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
    public class MyPageInterceptor implements Interceptor {

//每頁顯示的條目數
private int pageSize;
//當前現實的頁數
private int currPage;

//如果項目中分頁的pageSize是統一的,也可以在這里統一配置和獲取,這樣就不用每次請求都傳遞pageSize參數了。參數是在配置攔截器時配置的。
String limit1 = properties.getProperty("limit", "10");
this.pageSize = Integer.valueOf(limit1);
this.dbType = properties.getProperty("dbType", "mysql");
}
}
上面即是攔截器功能的實現,在intercept方法中獲取到select標簽和sql語句的相關信息,攔截所有以ByPage結尾的select查詢,並且統一在查詢語句后面添加limit分頁的相關語句,統一實現分頁功能。

重點詳解:

StatementHandler是一個接口,而我們在代碼中通過StatementHandler statementHandler = (StatementHandler) invocation.getTarget();獲取到的是StatementHandler默認的實現類RoutingStatementHandler。而RoutingStatementHandler只是一個中間代理,他不會提供具體的方法。那你可能會納悶了,攔截器中基本上是依賴statementHandler獲取各種對象和屬性的,沒有具體屬性和方法怎么行??接着看下面代碼:

private final StatementHandler delegate;

原來它是通過不同的MappedStatement創建不同的StatementHandler實現類對象處理不同的情況。這里的到的StatementHandler實現類才是真正服務的。看到這里,你可能就會明白MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");中delegate的來源了吧。至於為什么要這么去獲取,后面我們會說道。

拿到statementHandler后,我們會通過MetaObject MetaObjectHandler = SystemMetaObject.forObject(statementHandler);去獲取它的包裝對象,通過包裝對象去獲取各種服務。

接下來說說:MappedStatement mappedStatement = (MappedStatement) MetaObjectHandler.getValue("delegate.mappedStatement");

上面提到為什么要這么去獲取MappedStatement對象??在RoutingStatementHandler中delegate是私有的(private final StatementHandler delegate;),有沒有共有的方法去獲取。所以這里只有通過反射來獲取啦。

MappedStatement是保存了xxMapper.xml中一個sql語句節點的所有信息的包裝類,可以通過它獲取到節點中的所有信息。在示例中我們拿到了id值,也就是方法的名稱,通過名稱區攔截所有需要分頁的請求。

通過StatementHandler的包裝類,不光能拿到MappedStatement,還可以拿到下面的數據:

上面的所有數據都可以通過反射拿到。

幾個重要的參數:
Configuration:所有配置的相關信息。
ResultSetHandler:用於攔截執行結果的組裝。
ParameterHandler:攔截執行Sql的參數的組裝。
Executor:執行Sql的全過程,包括組裝參數、組裝結果和執行Sql的過程。
BoundSql:執行的Sql的相關信息。

接下來我們通過如下代碼拿到請求時的map對象(反射)。

如上所示,還能在里面配置一些屬性,在攔截器的setProperties方法中可以獲取配置好的屬性值。如項目分頁的pageSize參數的值固定,我們就可以配置在這里了,以后就不需要每次傳入pageSize了,讀取方式如下:

到這里,有關攔截器的相關知識就講解的差不多了,接下來就需要測試,是否我們這樣寫真的有效??

首先還是添加dao層的方法和xml文件的sql語句配置,注意項目中攔截的是以ByPage結尾的請求,所以在這里,我們的方法名稱也以此結尾:

方法

實現:

這里我們雖然傳入了currPage和pageSize兩個參數,但是在sql的xml文件中並沒有使用,直接在攔截器中獲取到統一使用。

最后編寫controller的測試代碼:

 


免責聲明!

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



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