前言:
首先說下實現原理。使用攔截器攔截原始的sql,然后加上分頁查詢的關鍵字和屬性,拼裝成新的sql語句再交給mybatis去執行。
除了業務代碼之外,需要寫的東西不多,提幾個關鍵的:
1、分頁對象Page類。給該對象設置一個當前頁數(前端給)、總記錄數(攔截器內賦值)2個參數,他就能幫你計算出分頁sql語句用的2個參數。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
/** * 分頁對應的實體類 */ public class Page { /** * 總條數 */ private int totalNumber; /** * 當前第幾頁 */ private int currentPage; /** * 總頁數 */ private int totalPage; /** * 每頁顯示條數 */ private int pageNumber = 5 ; /** * 數據庫中limit的參數,從第幾條開始取 */ private int dbIndex; /** * 數據庫中limit的參數,一共取多少條 */ private int dbNumber; /** * 根據當前對象中屬性值計算并設置相關屬性值 */ public void count() { // 計算總頁數 int totalPageTemp = this .totalNumber / this .pageNumber; int plus = ( this .totalNumber % this .pageNumber) == 0 ? 0 : 1 ; totalPageTemp = totalPageTemp + plus; if (totalPageTemp <= 0 ) { totalPageTemp = 1 ; } this .totalPage = totalPageTemp; // 設置當前頁數 // 總頁數小于當前頁數,應將當前頁數設置為總頁數 if ( this .totalPage < this .currentPage) { this .currentPage = this .totalPage; } // 當前頁數小于1設置為1 if ( this .currentPage < 1 ) { this .currentPage = 1 ; } // 設置limit的參數 this .dbIndex = ( this .currentPage - 1 ) * this .pageNumber; this .dbNumber = this .pageNumber; } public int getTotalNumber() { return totalNumber; } public void setTotalNumber( int totalNumber) { this .totalNumber = totalNumber; this .count(); } public int getCurrentPage() { return currentPage; } public void setCurrentPage( int currentPage) { this .currentPage = currentPage; } public int getTotalPage() { return totalPage; } public void setTotalPage( int totalPage) { this .totalPage = totalPage; } public int getPageNumber() { return pageNumber; } public void setPageNumber( int pageNumber) { this .pageNumber = pageNumber; this .count(); } public int getDbIndex() { return dbIndex; } public void setDbIndex( int dbIndex) { this .dbIndex = dbIndex; } public int getDbNumber() { return dbNumber; } public void setDbNumber( int dbNumber) { this .dbNumber = dbNumber; } } |
2、關鍵的攔截器實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
package com.imooc.interceptor; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Map; import java.util.Properties; import org.apache.ibatis.executor.parameter.ParameterHandler; import org.apache.ibatis.executor.statement.StatementHandler; 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.reflection.DefaultReflectorFactory; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import com.imooc.entity.Page; /** * 分頁攔截器 * * @author Skye * */ @Intercepts ({ @Signature (type = StatementHandler. class , method = "prepare" , args = { Connection. class , Integer. class }) }) public class PageInterceptor implements Interceptor { public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory()); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue( "delegate.mappedStatement" ); //通過MetaObject元數據取得方法名id:com.XXX.queryMessageListByPage String id = mappedStatement.getId(); //匹配在mybatis中定義的與分頁有關的查詢id if (id.matches( ".+ByPage$" )) { //BoundSql中有原始的sql語句和對應的查詢參數 BoundSql boundSql = statementHandler.getBoundSql(); Map<String, Object> params = (Map<String, Object>) boundSql.getParameterObject(); Page page = (Page) params.get( "page" ); String sql = boundSql.getSql(); String countSql = "select count(*)from (" + sql + ")a" ; Connection connection = (Connection) invocation.getArgs()[ 0 ]; PreparedStatement countStatement = connection.prepareStatement(countSql); ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue( "delegate.parameterHandler" ); parameterHandler.setParameters(countStatement); ResultSet rs = countStatement.executeQuery(); if (rs.next()) { //為什么是getInt(1)? 因為數據表的列是從1開始計數 page.setTotalNumber(rs.getInt( 1 )); System.out.println( "攔截器得知page的記錄總數為:" + page.getTotalNumber()); } String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber(); metaObject.setValue( "delegate.boundSql.sql" , pageSql); } return invocation.proceed(); } /** * @param target * 被攔截的對象 */ public Object plugin(Object target) { // 如果將攔截器類比喻為代購票的公司,那this就是代購業務員(進入方法前是無代理購票能力業務員,進入后成為有代理能力的業務員) // 通過注解獲取攔截目標的信息,如果不符合攔截要求就返回原目標,如果符合則使用動態代理生成代理對象 return Plugin.wrap(target, this ); } public void setProperties(Properties properties) { // TODO Auto-generated method stub } } |
3、mybatis-config.xml里面注冊自己寫的攔截器
1
2
3
4
5
|
<!-- 自定義的分頁攔截器 --> <plugins> <plugin interceptor= "你寫的攔截器全類名" > </plugin> </plugins> |
Dao層相關的mapper.xml里面的sql語句不用做改動。
4、前端需要給后端一個顯示哪一頁的參數,通過service層組裝查詢參數之后交給MyBatis去查分頁數據,我定義的分頁DAO接口返回的數據是一個list,包含了分頁查詢結果。前端可以用jquery_pagination插件去實現分頁的展示,具體去官方github看怎么設置吧。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<!--pagination需要的腳本--> <% // 獲取請求的上下文 String context = request.getContextPath(); %> <link href= "../css/pagination.css" rel= "external nofollow" rel= "stylesheet" type= "text/css" /> <script type= "text/javascript" src= "../js/jquery-1.11.3.js" ></script> <script type= "text/javascript" src= "../js/jquery.pagination.js" ></script> <script type= "text/javascript" > // 點擊分頁按鈕以后觸發的動作 function handlePaginationClick(new_page_index, pagination_container) { <!--從stuForm表單提交當前頁的參數.可以使用restful方式,讓springmvc使用 @PathVariable 關鍵字定義的形參去接。這 2 個參數是分頁控件自己提供的,不需要我們去自己找,但是計數從 0 開始,而我們后臺分頁計數從 1 開始,因此要手動加 1 。 --> $( "#stuForm" ).attr( "action" , "你定義的分頁查詢url/" +(new_page_index+ 1 )); $( "#stuForm" ).submit(); return false ; } $(function(){ $( "#News-Pagination" ).pagination(${result.totalRecord}, { items_per_page:${result.pageSize}, // 每頁顯示多少條記錄 current_page:${result.currentPage} - 1 , // 當前顯示第幾頁數據 num_display_entries: 8 , // 分頁顯示的條目數 next_text: "下一頁" , prev_text: "上一頁" , num_edge_entries: 2 , // 連接分頁主體,顯示的條目數 callback:handlePaginationClick(當前頁,分頁div的id), //執行的回調函數 load_first_page: false //防止頁面一直刷新( 這條非常重要!) }); }); </script> <!-- 這部分用c:forEach標簽打印查詢結果的表格--> <!--分頁控件名稱--> <div id= "News-Pagination" ></div> |
寫這篇總結的目的是希望形成一個分頁功能的整體解決方案(前端+后端都涵蓋到)。4月17、18日開始我會寫一個小系統將前段時間所學都用上,完了之后會回來更新這篇文章里面不正確的地方。
如有疑問請留言或者到本站社區交流討論,感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
原文鏈接:http://blog.csdn.net/kaka0509/article/details/70195804