注意:這篇博客已經(jīng)和當(dāng)前的分頁(yè)插件完全不一樣了,所以建議大家通過(guò)上面項(xiàng)目地址查看最新的源碼和文檔來(lái)了解。
以前為Mybatis分頁(yè)查詢(xún)發(fā)愁過(guò),而且在網(wǎng)上搜過(guò)很多相關(guān)的文章,最后一個(gè)都沒(méi)采用。在分頁(yè)的地方完全都是手寫(xiě)分頁(yè)SQL和count的sql,總之很麻煩。
后來(lái)有一段時(shí)間想從Mybatis內(nèi)部寫(xiě)一個(gè)分頁(yè)的實(shí)現(xiàn),我對(duì)LanguageDriver寫(xiě)過(guò)一個(gè)實(shí)現(xiàn),自動(dòng)分頁(yè)是沒(méi)問(wèn)題了,但是查詢(xún)總數(shù)(count)仍然沒(méi)法一次性解決,最后不了了之。
最近又要用到分頁(yè),為了方便必須地寫(xiě)個(gè)通用的分頁(yè)類(lèi),因此又再次參考網(wǎng)上大多數(shù)的Mybatis分頁(yè)代碼。
實(shí)際上在很早之前,有人在github上開(kāi)源過(guò)一個(gè)實(shí)現(xiàn),支持MySQL,Oracle,sqlserver的,和上面這個(gè)參考的比較類(lèi)似,考慮的更全面。但是我覺(jué)得太多類(lèi)太麻煩了,所以自己實(shí)現(xiàn)了一個(gè)只有一個(gè)攔截器的類(lèi),實(shí)際上可以分為兩個(gè)類(lèi),其中一個(gè)類(lèi)被我寫(xiě)成靜態(tài)類(lèi)放在了攔截器中,你也可以將Page類(lèi)提取出來(lái),方便使用Page。
先說(shuō)實(shí)現(xiàn)方法,該插件只有一個(gè)類(lèi):PageHelper.Java
攔截器簽名為:
1
2
|
@Intercepts ({ @Signature (type = StatementHandler. class , method = "prepare" , args = {Connection. class }), @Signature (type = ResultSetHandler. class , method = "handleResultSets" , args = {Statement. class })}) |
這里的簽名對(duì)整個(gè)實(shí)現(xiàn)和思想至關(guān)重要,首先我攔截prepare方法來(lái)改分頁(yè)SQL,來(lái)做count查詢(xún)。然后我攔截handleResultSets方法來(lái)獲取最后的處理結(jié)果,將結(jié)果放到Page對(duì)象中。
下面是修改分頁(yè)的代碼,是針對(duì)Oracle數(shù)據(jù)進(jìn)行的修改,如果有用其他數(shù)據(jù)庫(kù)的,自己修改這里的代碼就可以。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** * 修改原SQL為分頁(yè)SQL * @param sql * @param page * @return */ private String buildPageSql(String sql, Page page) { StringBuilder pageSql = new StringBuilder( 200 ); pageSql.append( "select * from ( select temp.*, rownum row_id from ( " ); pageSql.append(sql); pageSql.append( " ) temp where rownum <= " ).append(page.getEndRow()); pageSql.append( ") where row_id > " ).append(page.getStartRow()); return pageSql.toString(); } |
之后在下面的setPageParameter方法中一個(gè)selelct count語(yǔ)句,這里也需要根據(jù)數(shù)據(jù)庫(kù)類(lèi)型進(jìn)行修改:
1
2
|
// 記錄總記錄數(shù) String countSql = "select count(0) from (" + sql + ")" ; |
為什么我不提供對(duì)各種數(shù)據(jù)庫(kù)的支持呢,我覺(jué)得沒(méi)必要,還有些數(shù)據(jù)庫(kù)不支持分頁(yè),而且這個(gè)插件越簡(jiǎn)單對(duì)使用的開(kāi)發(fā)人員來(lái)說(shuō)越容易理解,越容易修改。修改成自己需要的分頁(yè)查詢(xún)肯定不是問(wèn)題。
最后上完整代碼(繼續(xù)看下去,下面還有使用方法):(點(diǎn)擊下載)
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
|
package com.mybatis.util; 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.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.plugin.*; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.SystemMetaObject; import org.apache.ibatis.scripting.defaults.DefaultParameterHandler; import org.apache.log4j.Logger; import java.sql.*; import java.util.List; import java.util.Properties; /** * Mybatis - 通用分頁(yè)攔截器 * @author liuzh/abel533/isea * Created by liuzh on 14-4-15. */ @Intercepts ({ @Signature (type = StatementHandler. class , method = "prepare" , args = {Connection. class }), @Signature (type = ResultSetHandler. class , method = "handleResultSets" , args = {Statement. class })}) public class PageHelper implements Interceptor { private static final Logger logger = Logger.getLogger(PageHelper. class ); public static final ThreadLocal<Page> localPage = new ThreadLocal<Page>(); /** * 開(kāi)始分頁(yè) * @param pageNum * @param pageSize */ public static void startPage( int pageNum, int pageSize) { localPage.set( new Page(pageNum, pageSize)); } /** * 結(jié)束分頁(yè)并返回結(jié)果,該方法必須被調(diào)用,否則localPage會(huì)一直保存下去,直到下一次startPage * @return */ public static Page endPage() { Page page = localPage.get(); localPage.remove(); return page; } @Override public Object intercept(Invocation invocation) throws Throwable { if (localPage.get() == null ) { return invocation.proceed(); } if (invocation.getTarget() instanceof StatementHandler) { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); MetaObject metaStatementHandler = SystemMetaObject.forObject(statementHandler); // 分離代理對(duì)象鏈(由于目標(biāo)類(lèi)可能被多個(gè)攔截器攔截,從而形成多次代理,通過(guò)下面的兩次循環(huán) // 可以分離出最原始的的目標(biāo)類(lèi)) while (metaStatementHandler.hasGetter( "h" )) { Object object = metaStatementHandler.getValue( "h" ); metaStatementHandler = SystemMetaObject.forObject(object); } // 分離最后一個(gè)代理對(duì)象的目標(biāo)類(lèi) while (metaStatementHandler.hasGetter( "target" )) { Object object = metaStatementHandler.getValue( "target" ); metaStatementHandler = SystemMetaObject.forObject(object); } MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue( "delegate.mappedStatement" ); //分頁(yè)信息if (localPage.get() != null) { Page page = localPage.get(); BoundSql boundSql = (BoundSql) metaStatementHandler.getValue( "delegate.boundSql" ); // 分頁(yè)參數(shù)作為參數(shù)對(duì)象parameterObject的一個(gè)屬性 String sql = boundSql.getSql(); // 重寫(xiě)sql String pageSql = buildPageSql(sql, page); //重寫(xiě)分頁(yè)sql metaStatementHandler.setValue( "delegate.boundSql.sql" , pageSql); Connection connection = (Connection) invocation.getArgs()[ 0 ]; // 重設(shè)分頁(yè)參數(shù)里的總頁(yè)數(shù)等 setPageParameter(sql, connection, mappedStatement, boundSql, page); // 將執(zhí)行權(quán)交給下一個(gè)攔截器 return invocation.proceed(); } else if (invocation.getTarget() instanceof ResultSetHandler) { Object result = invocation.proceed(); Page page = localPage.get(); page.setResult((List) result); return result; } return null ; } /** * 只攔截這兩種類(lèi)型的 * StatementHandler * ResultSetHandler * @param target * @return */ @Override public Object plugin(Object target) { if (target instanceof StatementHandler || target instanceof ResultSetHandler) { return Plugin.wrap(target, this ); } else { return target; } } @Override public void setProperties(Properties properties) { } /** * 修改原SQL為分頁(yè)SQL * @param sql * @param page * @return */ private String buildPageSql(String sql, Page page) { StringBuilder pageSql = new StringBuilder( 200 ); pageSql.append( "select * from ( select temp.*, rownum row_id from ( " ); pageSql.append(sql); pageSql.append( " ) temp where rownum <= " ).append(page.getEndRow()); pageSql.append( ") where row_id > " ).append(page.getStartRow()); return pageSql.toString(); } /** * 獲取總記錄數(shù) * @param sql * @param connection * @param mappedStatement * @param boundSql * @param page */ private void setPageParameter(String sql, Connection connection, MappedStatement mappedStatement, BoundSql boundSql, Page page) { // 記錄總記錄數(shù) String countSql = "select count(0) from (" + sql + ")" ; PreparedStatement countStmt = null ; ResultSet rs = null ; try { countStmt = connection.prepareStatement(countSql); BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(), countSql, boundSql.getParameterMappings(), boundSql.getParameterObject()); setParameters(countStmt, mappedStatement, countBS, boundSql.getParameterObject()); rs = countStmt.executeQuery(); int totalCount = 0 ; if (rs.next()) { totalCount = rs.getInt( 1 ); } page.setTotal(totalCount); int totalPage = totalCount / page.getPageSize() + ((totalCount % page.getPageSize() == 0 ) ? 0 : 1 ); page.setPages(totalPage); } catch (SQLException e) { logger.error( "Ignore this exception" , e); } finally { try { rs.close(); } catch (SQLException e) { logger.error( "Ignore this exception" , e); } try { countStmt.close(); } catch (SQLException e) { logger.error( "Ignore this exception" , e); } } } /** * 代入?yún)?shù)值 * @param ps * @param mappedStatement * @param boundSql * @param parameterObject * @throws SQLException */ private void setParameters(PreparedStatement ps, MappedStatement mappedStatement, BoundSql boundSql, Object parameterObject) throws SQLException { ParameterHandler parameterHandler = new DefaultParameterHandler(mappedStatement, parameterObject, boundSql); parameterHandler.setParameters(ps); } /** * Description: 分頁(yè) * Author: liuzh * Update: liuzh(2014-04-16 10:56) */ public static class Page<E> { private int pageNum; private int pageSize; private int startRow; private int endRow; private long total; private int pages; private List<E> result; public Page( int pageNum, int pageSize) { this .pageNum = pageNum; this .pageSize = pageSize; this .startRow = pageNum > 0 ? (pageNum - 1 ) * pageSize : 0 ; this .endRow = pageNum * pageSize; } public List<E> getResult() { return result; } public void setResult(List<E> result) { this .result = result; } public int getPages() { return pages; } public void setPages( int pages) { this .pages = pages; } public int getEndRow() { return endRow; } public void setEndRow( int endRow) { this .endRow = endRow; } public int getPageNum() { return pageNum; } public void setPageNum( int pageNum) { this .pageNum = pageNum; } public int getPageSize() { return pageSize; } public void setPageSize( int pageSize) { this .pageSize = pageSize; } public int getStartRow() { return startRow; } public void setStartRow( int startRow) { this .startRow = startRow; } public long getTotal() { return total; } public void setTotal( long total) { this .total = total; } @Override public String toString() { return "Page{" + "pageNum=" + pageNum + ", pageSize=" + pageSize + ", startRow=" + startRow + ", endRow=" + endRow + ", total=" + total + ", pages=" + pages + '}' ; } } } |
使用該攔截器首先需要在Mybatis配置中配置該攔截器:
1
2
3
|
< plugins > < plugin interceptor = "com.mybatis.util.PageHelper" ></ plugin > </ plugins > |
配置攔截器的時(shí)候需要注意plugins的位置,plugins位置順序如下:
1
|
properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers? |
最后是調(diào)用該方法的例子代碼(Service層):
1
2
3
4
5
6
7
8
9
10
11
12
|
@Override public PageHelper.Page<SysLoginLog> findSysLoginLog(String loginIp, String username, String loginDate, String exitDate, String logerr, int pageNumber, int pageSize) throws BusinessException { PageHelper.startPage(pageNumber,pageSize); sysLoginLogMapper.findSysLoginLog(loginIp, username, loginDate, exitDate, logerr); return PageHelper.endPage(); } |
從上面可以看到使用該插件使用起來(lái)是很簡(jiǎn)單的,只需要在查詢(xún)前后使用PageHelper的startPage和endPage方法即可,中間代碼的調(diào)用結(jié)果已經(jīng)存在于Page的result中,如果你在一個(gè)返回一個(gè)結(jié)果的地方調(diào)用PageHelper,返回的結(jié)果仍然是一個(gè)List,取第一個(gè)值即可(我想沒(méi)人會(huì)在這種地方這么用,當(dāng)然這樣也不出錯(cuò))。
另外在startPage和endPage中間的所有mybatis代碼都會(huì)被分頁(yè),而且PageHelper只會(huì)保留最后一次的結(jié)果,因而使用時(shí)需要保證每次只在其中執(zhí)行一個(gè)mybatis查詢(xún),如果有多個(gè)分頁(yè),請(qǐng)多次使用startPage和endPage。
由于這里只提供了Oracle的實(shí)現(xiàn),所以我希望參考該分頁(yè)插件實(shí)現(xiàn)的其他數(shù)據(jù)庫(kù)的讀者也能將相應(yīng)的代碼開(kāi)源。
項(xiàng)目地址:Mybatis_PageHelper.zip
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持服務(wù)器之家。
原文鏈接:http://blog.csdn.net/isea533/article/details/23831273