MyBatis——RowBounds

如何分頁查詢

Mybatis如何分頁查詢?Mysql中可以使用limit語句,但limit並不是標準SQL中的,如果是其它的數據庫,則需要使用其它語句。MyBatis提供了RowBounds類,用於實現分頁查詢。RowBounds中有兩個數字,offset和limit。

MyBatis如何利用RowBounds實現通用分頁

在查詢數據庫時,如果沒有limit語句,則ResultSet中會包含所有滿足條件的數據,

RowBounds在處理分頁時,只是簡單的把offset之前的數據都skip掉,超過limit之後的數據不取出,上圖中的代碼取自MyBatis中的DefaultResultSetHandler類。跳過offset之前的數據是由方法skipRows處理,判斷數據是否超過了limit則是由shouldProcessMoreRows方法進行判斷。簡單點說道,就是先把數據全部查詢到ResultSet,然後從ResultSet中取出offset和limit之間的數據,這就實現了分頁查詢。

從兩種場景下說明RowBounds的使用。

    SqlSession,使用SqlSession時,selectList有一個重載的方法,帶有RowBounds參數,這種情況下,DAO層的實現了,可以對外界隱藏RowBounds類。如下圖,Page是自定義的一個接口,用於表示分頁信息,不直接使用RowBounds源於自己的一個習慣,不喜歡在Service層中侵入持久層所使用的持久化技術的類或接口。如果在Service中使用了RowBounds,Service與MyBatis就耦合了,當然,如果不要求這一點,直接要Service裏寫RowBounds對象也是可以的。

    1.映射接口,MyBatis提供了映射接口的形式,這種情況下,可以不寫DAO接口的實現即可完成DAO層,這種情況下,DAO接口寫成List<User> vip(RowBounds page),這種情況下,mybatis會完成分頁查詢。

回到Page接口,如果類與接口之間的關係如右類圖,DAO中的方法還是vip(Page page),而調用的時候,工廠返回了一個MyBatisPage類的對象給DAO,因爲MyBatisPage類繼承自RowBounds類,所以此時傳給DAO的Page對象也是一個RowBounds類的對象,但這種情況下卻不會有分頁查詢的效果。

MyBatisPage類相當於一個適配器,用於適配Page接口與RowBounds,但爲何給DAO一個RowBounds對象時,卻沒有實現分頁效果,原因是MyBatis判斷方法的參數中有沒有RowBounds參數是在產生接口的代理時,而不是在方法調用的時候。根據DefaultSqlSession類的getMapper方法可以很快找到MapperProxyFactory類,此類的作用就是創建接口的動態代理,所以方法的調用邏輯應該要看代理的InvocationHandler對象,它是MapperProxy類,這裏的處理調用比較深,在MapperProxy類中可以發現,每一個DAO接口上的方法都會對應一個MapperMethod類的對象,MapperMethod類中有一個內部類MethodSignature,這是關鍵的地方,每一個MapperMethod對象都依賴於一個MethodSignature對象,看看此類的構造器:

rowBoundsIndex,這個屬性是用於記錄MapperMthod對應的方法的參數中,RowBounds是第幾個參數,getUniqueParamIndex方法的實現中,如果發現沒有Rowbounds參數,則返回null,由此可見,如果DAO的方法簽名爲vip(Page page),則rowBoundsIndex爲null,所以在調用的時候,即使Page接口的實例也是一個RowBounds的實例,也不會有分頁效果。

    如果想要解決這個限制,付出的代價有點大。與Spring框架不同的是,Spring中,類與類之間依賴的是接口而不是具體類。而MyBatis從DefaultSqlSesison到MethodProx,全部依賴的是具體類,這些類沒有接口,如果要想解決這個限制,需要子類化DefaultSqlSession,Configuration,MapperRegistry,MapperProxyFactory,MapperProxy,MapperMethod和MethodSignature,這些類都沒有實現某個可擴展的接口,甚至沒有實現接口,全部是依賴具體的類,無法從其中某個點進行擴展。至於MapperProxyFactory,它無法替換成其它工廠類,它僅僅只是隱藏了接口的代理的創建方式。

RowBounds沒有覆蓋equals和hashCode方法

    如果RowBounds在Service中直接new了,則在測試Service時,在mock時使用了when這樣的方法,如:when(userDao.vip(rowBounds)).xxx,那麼就會發生錯誤,因爲在Service中也會new一個RowBounds,即使Service中new的那個RowBounds和單元測試中的RowBoumds的offset和limit兩個數都一樣,rowBounds.equals(row)也不會返回true,當然,可以使用when(userDao.vip(anyObject()))。

如何解決這個問題?有兩種方式,一種是RowBounds作爲參數傳入Service,另一種是使用適配器,這個適配器很簡單,寫個RowBounds的子類,在子類中覆蓋hashCode&equals方法,在Service中使用新的類

僅用於記錄,以備查看!轉自:MyBatis中的RowBounds

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章