MyBatis 插件使用-简单的分页插件
@
作为一个优秀的框架, 其除了要解决大部分的流程之外, 还需要提供给使用者能够自定义的能力。 MyBatis
有缓存, 有插件接口等。我们可以通过自定义插件的方式来对 MyBatis
进行使用上的扩展。
以一个简单的 mysql 分页插件为例, 插件的使用包含以下步骤:
1 分页参数的传递
分页参数就是 offset 和 limit。 可以使用 RowBounds
来进行传递, 但是这样需要对原有的方法进行修改。 因此, 本例子通过 ThreadLocal
进行无痛觉的传递。
/**
* @author homejim
*/
public class PageUtil {
private static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
public static void setPagingParam(int offset, int limit) {
Page page = new Page(offset, limit);
LOCAL_PAGE.set(page);
}
public static void removePagingParam() {
LOCAL_PAGE.remove();
}
public static Page getPaingParam() {
return LOCAL_PAGE.get();
}
}
在实际的使用过程中, 用户只需要再调用之前使用 PageUtil#setPagingParam
方法来进行分页参数的传递即可。 后续无需进行处理。
2 实现 Interceptor 接口
2.1 Interceptor 接口说明
先看看拦截器的接口。
/**
* 拦截器接口
*
* @author Clinton Begin
*/
public interface Interceptor {
/**
* 执行拦截逻辑的方法
*
* @param invocation 调用信息
* @return 调用结果
* @throws Throwable 异常
*/
Object intercept(Invocation invocation) throws Throwable;
/**
* 代理类
*
* @param target
* @return
*/
Object plugin(Object target);
/**
* 根据配置来初始化 Interceptor 方法
* @param properties
*/
void setProperties(Properties properties);
}
因此, 在实际的使用中。我们要覆盖这几个方法。
2.1 注解说明
在 mybatis
中, 可以拦截的方法包括
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
但是接口只有一个 Interceptor
, 因此, 需要使用注解 @Intercepts
和 @Signature
来指定拦截的方法。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface Intercepts {
Signature[] value();
}
Intercepts
注解中是 Signature
注解的数组。
/**
* 方法签名信息
*
* @author Clinton Begin
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
/**
* 需要拦截的类型
*
* @return
*/
Class<?> type();
/**
* 需要拦截的方法
* @return
*/
String method();
/**
* 被拦截方法的参数列表
*
* @return
*/
Class<?>[] args();
}
2.3 实现分页接口 PageInterceptor
/**
* 分页插件
*
* @author homejim
*/
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)
})
@Slf4j
public class PageInterceptor implements Interceptor {
private static int MAPPEDSTATEMENT_INDEX = 0;
private static int PARAMETER_INDEX = 1;
private static int ROWBOUNDS_INDEX = 2;
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 从 Invocation 中获取参数
final Object[] queryArgs = invocation.getArgs();
final MappedStatement ms = (MappedStatement) queryArgs[MAPPEDSTATEMENT_INDEX];
final Object parameter = queryArgs[PARAMETER_INDEX];
// 获取分页参数
Page paingParam = PageUtil.getPaingParam();
if (paingParam != null) {
// 构造新的 sql, select xxx from xxx where yyy limit offset,limit
final BoundSql boundSql = ms.getBoundSql(parameter);
String pagingSql = getPagingSql(boundSql.getSql(), paingParam.getOffset(), paingParam.getLimit());
// 设置新的 MappedStatement
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), pagingSql,
boundSql.getParameterMappings(), boundSql.getParameterObject());
MappedStatement mappedStatement = newMappedStatement(ms, newBoundSql);
queryArgs[MAPPEDSTATEMENT_INDEX] = mappedStatement;
// 重置 RowBound
queryArgs[ROWBOUNDS_INDEX] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
}
Object result = invocation.proceed();
PageUtil.removePagingParam();
return result;
}
@Override
public Object plugin(Object o) {
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 创建 MappedStatement
* @param ms
* @param newBoundSql
* @return
*/
private MappedStatement newMappedStatement(MappedStatement ms, BoundSql newBoundSql) {
MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(),
new BoundSqlSqlSource(newBoundSql), ms.getSqlCommandType());
builder.keyColumn(delimitedArrayToString(ms.getKeyColumns()));
builder.keyGenerator(ms.getKeyGenerator());
builder.keyProperty(delimitedArrayToString(ms.getKeyProperties()));
builder.lang(ms.getLang());
builder.resource(ms.getResource());
builder.parameterMap(ms.getParameterMap());
builder.resultMaps(ms.getResultMaps());
builder.resultOrdered(ms.isResultOrdered());
builder.resultSets(delimitedArrayToString(ms.getResultSets()));
builder.resultSetType(ms.getResultSetType());
builder.timeout(ms.getTimeout());
builder.statementType(ms.getStatementType());
builder.useCache(ms.isUseCache());
builder.cache(ms.getCache());
builder.databaseId(ms.getDatabaseId());
builder.fetchSize(ms.getFetchSize());
builder.flushCacheRequired(ms.isFlushCacheRequired());
return builder.build();
}
public String getPagingSql(String sql, int offset, int limit) {
StringBuilder result = new StringBuilder(sql.length() + 100);
result.append(sql).append(" limit ");
if (offset > 0) {
result.append(offset).append(",").append(limit);
}else{
result.append(limit);
}
return result.toString();
}
public String delimitedArrayToString(String[] array) {
if (array == null || array.length == 0) {
return "";
}
Joiner joiner = Joiner.on(",");
return joiner.join(array);
}
}
根据前面注解的讲解, 我们要拦截的是 Executor
类中以下方法
<E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
拦截后
- 获取分页参数
- 根据参数改写 sql
- 生成新的
MappedStatement
对象给代理方法 - 执行完成后移除分页参数
3. 更改配置
在以上的步骤之后, mybatis 还是不知道我们都有哪些接口, 以及哪些接口需要用。 因此, 需要再配置中进行说明。
在 mybatis-config.xml
文件中, 加入以下的配置
<plugins>
<plugin interceptor="com.homejim.mybatis.plugin.PageInterceptor">
</plugin>
</plugins>
4 测试
@Test
public void testSelectList() {
SqlSession sqlSession = null;
try {
sqlSession = sqlSessionFactory.openSession();
PageUtil.setPagingParam(1, 2);
List<Student> students = sqlSession.selectList("selectAll");
for (int i = 0; i < students.size(); i++) {
System.out.println(students.get(i));
}
List<Student> students2 = sqlSession.selectList("selectAll");
for (int i = 0; i < students2.size(); i++) {
System.out.println(students2.get(i));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (sqlSession != null) {
sqlSession.close();
}
}
}
其中, 第一个查询使用了分页, 第二个没有使用。 执行结果如下
第一个查询使用了分页, 因此有 limit , 第二个查询没有, 因此查询出了所有的结果。
更多使用, 访问我的GitHub项目
MyBatis 插件使用-简单的分页插件的更多相关文章
- 【UI插件】简单的日历插件(下)—— 学习MVC思想
前言 我们上次写了一个简单的日历插件,但是只是一个半成品,而且做完后发现一些问题,于是我们今天尝试来解决这些问题 PS:距离上次貌似很久了 上次,我们大概遇到哪些问题呢: ① 既然想做一套UI库,那么 ...
- Mybatis拦截器介绍及分页插件
1.1 目录 1.1 目录 1.2 前言 1.3 Interceptor接口 1.4 注册拦截器 1.5 Mybatis可拦截的方法 1.6 利用拦截器进行分页 1.2 前言 拦截器的一 ...
- 【SSM 5】Mybatis分页插件的使用
一.添加maven依赖项 <span style="font-family:KaiTi_GB2312;font-size:18px;"><dependency&g ...
- Mybatis分页插件PageHelper的实现
Mybatis分页插件PageHelper的实现 前言 分页这个概念在做web网站的时候很多都会碰到 说它简单吧 其实也简单 小型的网站,完全可以自己写一个,首先查出数据库总条数,然后按照分页大小分为 ...
- Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件
MyBatis 今天大年初一,你在学习!不学习做什么,斗地主...人都凑不齐.学习吧,学习使我快乐!除了诗和远方还有责任,我也想担当,我也想负责,可臣妾做不到啊,怎么办?你说怎么办,为啥人家能做到你做 ...
- Mybatis分页插件的使用流程
如果你也在用Mybatis,建议尝试该分页插件,这一定是最方便使用的分页插件.该插件支持任何复杂的单表.多表分页. 1.引入PageHelper的jar包 在pom.xml中添加如下依赖: 12345 ...
- mybatis的分页插件使用方法
1.下载所需要的jar包,如果使用maven可以在maven中添加依赖: 插件的实现原理: 如果你想使用本项目的jar包而不是直接引入类,你可以在这里下载各个版本的jar包(点击Download下的j ...
- MyBatis学习总结_17_Mybatis分页插件PageHelper
如果你也在用Mybatis,建议尝试该分页插件,这一定是最方便使用的分页插件. 分页插件支持任何复杂的单表.多表分页,部分特殊情况请看重要提示. 想要使用分页插件?请看如何使用分页插件. 物理分页 该 ...
- Mybatis分页插件更新
分页插件演示:http://blog.csdn.net/isea533/article/details/23831273 分页插件演示样例:http://blog.csdn.net/isea533/a ...
随机推荐
- JS如何重写一个函数
分享一些自己在开发上遇到的问题,比如我们页面上用了大量的打印语句,但是在某些时候,我们不想要了. 解决方案1 : 我们删除这里的代码,如果太多了,那工作量太大好累,想想都不想干 解决方案2 :我们将c ...
- C#数据结构_栈和队列
栈:先进后出,只能在栈顶进行操作. 栈的操作主要包括在栈顶插入元素和删除元素.取栈顶元素和判断栈是否为空等. 栈的接口定义: public interface IStack<T> { in ...
- Java集合框架之List接口浅析
Java集合框架之List接口浅析 一.List综述: 毫无疑问List接口位于java.util包下,继承自 Collection接口 存储元素的特点: 有序可重复(有序:即存进去是什么顺序,取出来 ...
- netty源码解解析(4.0)-20 ChannelHandler: 自己实现一个自定义协议的服务器和客户端
本章不会直接分析Netty源码,而是通过使用Netty的能力实现一个自定义协议的服务器和客户端.通过这样的实践,可以更深刻地理解Netty的相关代码,同时可以了解,在设计实现自定义协议的过程中需要解决 ...
- java学习之String类
标签(空格分隔): String类 String 的概述 class StringDemo{ public static void main(String[] args){ String s1=&qu ...
- Struts2:request & response
整理自网上: 1. 获取Request和Response的方法 1.1. ServletActionContext的静态方法 HttpServletRequest request = ...
- This probably means that Tcl wasn't installed properly.
报错 D:/Anaconda3/tcl/tcl8.6/init.tcl: version conflict for package "Tcl": have 8.6.8, need ...
- HDU-1532 网络流裸题
HDU-1532 题意简单的来说就是从1点到n点,最大的流量是多少. 代码: #include<bits/stdc++.h> using namespace std; #define Fo ...
- hdu 1503 Advanced Fruits 最长公共子序列 *
Advanced Fruits Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...
- bzoj3032 七夕祭题解
题面 TYVJ七夕祭和11区的夏祭的形式很像.矩形的祭典会场由N排M列共计N×M个摊点组成.虽然摊点种类繁多,不过cl只对其中的一部分摊点感兴趣,比如章鱼烧.苹果糖.棉花糖.射的屋--什么的.Vani ...