分頁實現的三種方式java
三種分頁的實現方式
(1)每次取查詢結果的全部數據,而後根據頁面顯示指定的記錄
(2)根據頁面只取一頁的數據,而後顯示這一頁,這裏要構造sql語句
(3)取必定頁數的數據,就是前兩種的折中
實現分頁的步驟:
1.建立一個用於封裝分頁相關屬性及操做的類
2.從頁面增長分頁導航條的功能
3.實現分頁查詢功能,從頁面請求->Servlet->DAO的實現mysql
這裏還要注意的是這些數據是放在request仍是session中,這裏一一討論
1.通常不會放在session中,由於會佔用大量內存,因此要放在request裏面。
優勢:實現比較簡單,查詢速度比較快。
缺點:佔用內存多一些,網絡傳輸數據多。
對於數據量比較少的查詢這種方法比較合適。這裏有人把數據放在session中,這樣換頁的時候就不用從新查詢,可是這樣是極其很差的,強烈建議不要這樣使用。
2.確定不會放在session中,由於放在session中沒有意義。
優勢:佔用內存少。
缺點:比較麻煩,必須先得到查詢結果的總數,由於要知道有多少紀錄才知道有多少頁。另外要構造分頁查詢語句,對於不一樣的數據庫是不同的。 sql
一.藉助數組進行分頁數據庫
原理:進行數據庫查詢操做時,獲取到數據庫中全部知足條件的記錄,保存在應用的臨時數組中,再經過List的subList方法,獲取到知足條件的全部記錄。apache
實現:數組
首先在dao層,建立StudentMapper接口,用於對數據庫的操做。在接口中定義經過數組分頁的查詢方法,以下所示:網絡
Listsession
建立StudentMapper.xml文件,編寫查詢的sql語句:app
能夠看出再編寫sql語句的時候,咱們並無做任何分頁的相關操做。這裏是查詢到全部的學生信息。性能
接下來在service層獲取數據而且進行分頁實現:
定義IStuService接口,而且定義分頁方法:
經過接收currPage參數表示顯示第幾頁的數據,pageSize表示每頁顯示的數據條數。
建立IStuService接口實現類StuServiceIml對方法進行實現,對獲取到的數組經過currPage和pageSize進行分頁:
經過subList方法,獲取到兩個索引間的全部數據。
最後在controller中建立測試方法:
經過用戶傳入的currPage和pageSize獲取指定數據。
二.藉助Sql語句進行分頁
在瞭解到經過數組分頁的缺陷後,咱們發現不能每次都對數據庫中的全部數據都檢索。而後在程序中對獲取到的大量數據進行二次操做,這樣對空間和性能都是極大的損耗。因此咱們但願能直接在數據庫語言中只檢索符合條件的記錄,不須要在經過程序對其做處理。這時,Sql語句分頁技術橫空出世。
實現:經過sql語句實現分頁也是很是簡單的,只是須要改變咱們查詢的語句就能實現了,即在sql語句後面添加limit分頁語句。
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;
/**
/**
//每頁顯示的條目數
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的測試代碼: