- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章详解Mybatis极其(最)简(好)单(用)的一个分页插件由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
注意:这篇博客已经和当前的分页插件完全不一样了,所以建议大家通过上面项目地址查看最新的源码和文档来了解.
以前为Mybatis分页查询发愁过,而且在网上搜过很多相关的文章,最后一个都没采用。在分页的地方完全都是手写分页SQL和count的sql,总之很麻烦.
后来有一段时间想从Mybatis内部写一个分页的实现,我对LanguageDriver写过一个实现,自动分页是没问题了,但是查询总数(count)仍然没法一次性解决,最后不了了之.
最近又要用到分页,为了方便必须地写个通用的分页类,因此又再次参考网上大多数的Mybatis分页代码.
实际上在很早之前,有人在github上开源过一个实现,支持MySQL,Oracle,sqlserver的,和上面这个参考的比较类似,考虑的更全面。但是我觉得太多类太麻烦了,所以自己实现了一个只有一个拦截器的类,实际上可以分为两个类,其中一个类被我写成静态类放在了拦截器中,你也可以将Page类提取出来,方便使用Page.
先说实现方法,该插件只有一个类:PageHelper.Java 。
拦截器签名为:
1
2
|
@Intercepts
({
@Signature
(type = StatementHandler.
class
, method =
"prepare"
, args = {Connection.
class
}),
@Signature
(type = ResultSetHandler.
class
, method =
"handleResultSets"
, args = {Statement.
class
})})
|
这里的签名对整个实现和思想至关重要,首先我拦截prepare方法来改分页SQL,来做count查询。然后我拦截handleResultSets方法来获取最后的处理结果,将结果放到Page对象中.
下面是修改分页的代码,是针对Oracle数据进行的修改,如果有用其他数据库的,自己修改这里的代码就可以.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/**
* 修改原SQL为分页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方法中一个selelct count语句,这里也需要根据数据库类型进行修改:
1
2
|
// 记录总记录数
String countSql =
"select count(0) from ("
+ sql +
")"
;
|
为什么我不提供对各种数据库的支持呢,我觉得没必要,还有些数据库不支持分页,而且这个插件越简单对使用的开发人员来说越容易理解,越容易修改。修改成自己需要的分页查询肯定不是问题.
最后上完整代码(继续看下去,下面还有使用方法):(点击下载) 。
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 - 通用分页拦截器
* @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>();
/**
* 开始分页
* @param pageNum
* @param pageSize
*/
public
static
void
startPage(
int
pageNum,
int
pageSize) {
localPage.set(
new
Page(pageNum, pageSize));
}
/**
* 结束分页并返回结果,该方法必须被调用,否则localPage会一直保存下去,直到下一次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);
// 分离代理对象链(由于目标类可能被多个拦截器拦截,从而形成多次代理,通过下面的两次循环
// 可以分离出最原始的的目标类)
while
(metaStatementHandler.hasGetter(
"h"
)) {
Object object = metaStatementHandler.getValue(
"h"
);
metaStatementHandler = SystemMetaObject.forObject(object);
}
// 分离最后一个代理对象的目标类
while
(metaStatementHandler.hasGetter(
"target"
)) {
Object object = metaStatementHandler.getValue(
"target"
);
metaStatementHandler = SystemMetaObject.forObject(object);
}
MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue(
"delegate.mappedStatement"
);
//分页信息if (localPage.get() != null) {
Page page = localPage.get();
BoundSql boundSql = (BoundSql) metaStatementHandler.getValue(
"delegate.boundSql"
);
// 分页参数作为参数对象parameterObject的一个属性
String sql = boundSql.getSql();
// 重写sql
String pageSql = buildPageSql(sql, page);
//重写分页sql
metaStatementHandler.setValue(
"delegate.boundSql.sql"
, pageSql);
Connection connection = (Connection) invocation.getArgs()[
0
];
// 重设分页参数里的总页数等
setPageParameter(sql, connection, mappedStatement, boundSql, page);
// 将执行权交给下一个拦截器
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
;
}
/**
* 只拦截这两种类型的
* 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为分页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();
}
/**
* 获取总记录数
* @param sql
* @param connection
* @param mappedStatement
* @param boundSql
* @param page
*/
private
void
setPageParameter(String sql, Connection connection, MappedStatement mappedStatement,
BoundSql boundSql, Page page) {
// 记录总记录数
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);
}
}
}
/**
* 代入参数值
* @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: 分页
* 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
>
|
配置拦截器的时候需要注意plugins的位置,plugins位置顺序如下:
1
|
properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers?
|
最后是调用该方法的例子代码(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();
}
|
从上面可以看到使用该插件使用起来是很简单的,只需要在查询前后使用PageHelper的startPage和endPage方法即可,中间代码的调用结果已经存在于Page的result中,如果你在一个返回一个结果的地方调用PageHelper,返回的结果仍然是一个List,取第一个值即可(我想没人会在这种地方这么用,当然这样也不出错).
另外在startPage和endPage中间的所有mybatis代码都会被分页,而且PageHelper只会保留最后一次的结果,因而使用时需要保证每次只在其中执行一个mybatis查询,如果有多个分页,请多次使用startPage和endPage.
由于这里只提供了Oracle的实现,所以我希望参考该分页插件实现的其他数据库的读者也能将相应的代码开源.
项目地址:Mybatis_PageHelper.zip 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我.
原文链接:http://blog.csdn.net/isea533/article/details/23831273 。
最后此篇关于详解Mybatis极其(最)简(好)单(用)的一个分页插件的文章就讲到这里了,如果你想了解更多关于详解Mybatis极其(最)简(好)单(用)的一个分页插件的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
单向链表 单向链表比顺序结构的线性表最大的好处就是不用保证存放的位置,它只需要用指针去指向下一个元素就能搞定。 单链表图解 图画的比较粗糙,简单的讲解一下: 上面四个长方形,每个长方
使用TCP,我正在设计一些类似于next的程序。 客户端在许多线程中的接收正在等待一台服务器的发送消息。但是,这是有条件的。 recv正在等待特定的发送消息。 例如 客户 thread 1: recv
我正在编写正则表达式来验证电子邮件。唯一让我困惑的是: 顶级域名可以使用单个字符吗?(例如:lockevn.c) 背景:我知道顶级域名可以是 2 个字符到任意字符(.uk、.us 到 .canon、.
是否可以在单个定义中定义同一 Controller 的多个路由? 例如: 我想要一个单一的定义 /, /about, /privacy-policy 使用类似的东西 _home: pat
我正在使用 objective-c开发针对 11.4 iOS 的单 View 应用程序,以及 Xcode版本是 9.4.1。 创建后有Main.storyboard和LaunchScreen.stor
我一直在尝试在 shell 程序中实现管道结构,如果我执行简单的命令(例如“hello | rev”),它就可以工作 但是当我尝试执行“head -c 1000000/dev/urandom | wc
此表包含主机和接口(interface)列UNIQUE 组合* 编辑:这个表也有一个自动递增的唯一 ID,抱歉我应该在之前提到这个 ** | host.... | interface..... |
我想将具有固定补丁大小的“std filter”应用于单 channel 图像。 也就是说,我希望 out[i,j] 等于 img[i,j] 附近的像素值的标准值。 对于那些熟悉 Matlab 的人,
假设我想进行网络调用并使用 rx.Single,因为我希望只有一个值。 我如何应用replay().autoConnect() 这样的东西,这样当我从多个来源订阅时网络调用就不会发生多次?我应该使用
我将图像从 rgb 转换为 YUV。现在我想单独找到亮度 channel 的平均值。你能告诉我如何实现这一目标吗?此外,有没有办法确定图像由多少个 channel 组成? 最佳答案 你可以这样做: #
在比较Go和Scala的语句结束检测时,我发现Scala的规则更丰富,即: A line ending is treated as a semicolon unless one of the foll
在IEEE 1800-2005或更高版本中,&和&&二进制运算符有什么区别?它们相等吗? 我注意到,当a和b的类型为bit时,这些coverpoint定义的行为相同: cp: coverpoint a
我正在使用Flutter的provider软件包。我要实现的是为一个 View 或页面提供一个简单的提供程序。因此,我在小部件中尝试了以下操作: Widget build(BuildContext c
我正在尝试在 cython 中使用 openmp。我需要在 cython 中做两件事: i) 在我的 cython 代码中使用 #pragma omp single{} 作用域。 ii) 使用#pra
我正在尝试从转义字符字符串中删除单引号和双引号。它对单引号 ' 或双自动 " 不起作用。 请问有人可以帮忙吗? var mysting = escapedStr.replace(/^%22/g, '
我正在尝试在 cython 中使用 openmp。我需要在 cython 中做两件事: i) 在我的 cython 代码中使用 #pragma omp single{} 作用域。 ii) 使用#pra
我正在使用 ANT+ 协议(protocol),将智能手机与 ANT+ USB 加密狗连接,该加密狗通过 SimulANT+ 连接到 PC。 SimulANT+ 正在模拟一个心率传感器,它将数据发送到
有人可以解释/理解单/多线程模式下计算结果的不同吗? 这是一个大约的例子。圆周率的计算: #include #include #include const int itera(100000000
我编写了一个粗略的阴影映射实现,它使用 6 个不同的 View 矩阵渲染场景 6 次以创建立方体贴图。 作为优化,我正在尝试使用几何着色器升级到单 channel 方法,但很难从我的着色器获得任何输出
尝试使用 Single-Spa 构建一些东西并面临添加到应用程序 AngularJS 的问题。 Angular2 和 ReactJs 工作完美,但如果添加 AngularJS 并尝试为此应用程序使用
我是一名优秀的程序员,十分优秀!