@

作为一个优秀的框架, 其除了要解决大部分的流程之外, 还需要提供给使用者能够自定义的能力。 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 中, 可以拦截的方法包括

  1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  2. ParameterHandler (getParameterObject, setParameters)
  3. ResultSetHandler (handleResultSets, handleOutputParameters)
  4. 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;

拦截后

  1. 获取分页参数
  2. 根据参数改写 sql
  3. 生成新的 MappedStatement 对象给代理方法
  4. 执行完成后移除分页参数

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 插件使用-简单的分页插件的更多相关文章

  1. 【UI插件】简单的日历插件(下)—— 学习MVC思想

    前言 我们上次写了一个简单的日历插件,但是只是一个半成品,而且做完后发现一些问题,于是我们今天尝试来解决这些问题 PS:距离上次貌似很久了 上次,我们大概遇到哪些问题呢: ① 既然想做一套UI库,那么 ...

  2. Mybatis拦截器介绍及分页插件

    1.1    目录 1.1 目录 1.2 前言 1.3 Interceptor接口 1.4 注册拦截器 1.5 Mybatis可拦截的方法 1.6 利用拦截器进行分页 1.2     前言 拦截器的一 ...

  3. 【SSM 5】Mybatis分页插件的使用

    一.添加maven依赖项 <span style="font-family:KaiTi_GB2312;font-size:18px;"><dependency&g ...

  4. Mybatis分页插件PageHelper的实现

    Mybatis分页插件PageHelper的实现 前言 分页这个概念在做web网站的时候很多都会碰到 说它简单吧 其实也简单 小型的网站,完全可以自己写一个,首先查出数据库总条数,然后按照分页大小分为 ...

  5. Java框架之MyBatis 07-动态SQL-缓存机制-逆向工程-分页插件

    MyBatis 今天大年初一,你在学习!不学习做什么,斗地主...人都凑不齐.学习吧,学习使我快乐!除了诗和远方还有责任,我也想担当,我也想负责,可臣妾做不到啊,怎么办?你说怎么办,为啥人家能做到你做 ...

  6. Mybatis分页插件的使用流程

    如果你也在用Mybatis,建议尝试该分页插件,这一定是最方便使用的分页插件.该插件支持任何复杂的单表.多表分页. 1.引入PageHelper的jar包 在pom.xml中添加如下依赖: 12345 ...

  7. mybatis的分页插件使用方法

    1.下载所需要的jar包,如果使用maven可以在maven中添加依赖: 插件的实现原理: 如果你想使用本项目的jar包而不是直接引入类,你可以在这里下载各个版本的jar包(点击Download下的j ...

  8. MyBatis学习总结_17_Mybatis分页插件PageHelper

    如果你也在用Mybatis,建议尝试该分页插件,这一定是最方便使用的分页插件. 分页插件支持任何复杂的单表.多表分页,部分特殊情况请看重要提示. 想要使用分页插件?请看如何使用分页插件. 物理分页 该 ...

  9. Mybatis分页插件更新

    分页插件演示:http://blog.csdn.net/isea533/article/details/23831273 分页插件演示样例:http://blog.csdn.net/isea533/a ...

随机推荐

  1. python 15 带参装饰器

    目录 2. 带参数的装饰器 3. 多个装饰器装饰一个函数 2. 带参数的装饰器 #在装饰器的基础上再套一层 def auth(argv): def wrapper(func): def inner(* ...

  2. jvm系列(五):Java GC 分析

    Java GC就是JVM记录仪,书画了JVM各个分区的表演. 什么是 Java GC Java GC(Garbage Collection,垃圾收集,垃圾回收)机制,是Java与C++/C的主要区别之 ...

  3. egret之弹幕

    要实现弹幕功能,首先需要将弹幕配置成配置表.然后代码随机生成. /**生成单个弹幕 */ private showCaptionAnim(captionText: string) { egret.lo ...

  4. 听说你不会调参?TextCNN的优化经验Tricks汇总

    前言:本篇是TextCNN系列的第三篇,分享TextCNN的优化经验 前两篇可见: 文本分类算法TextCNN原理详解(一) TextCNN代码详解(附测试数据集以及GitHub 地址)(二) 调优模 ...

  5. HDU-4857-逃生-反向拓扑排序+优先队列

    HDU-4857 题意就是做一个符合条件的排序,用到拓扑序列. 我一开始wa了多发,才发现有几个样例过不了,发现1->2->3...的顺序无法保证. 后来就想用并查集强连,还是wa: 后来 ...

  6. CodeForces - 938D-Buy a Ticket+最短路

    Buy a Ticket 题意:有n个点和m条路(都收费),n个点在开演唱会,门票不同,对于生活在n个点的小伙伴,要求计算出每个小伙伴为了看一场演唱会要花费的最小价格: 思路: 这道题我一开始觉得要对 ...

  7. Codeforces 832 D Misha, Grisha and Underground

    Misha, Grisha and Underground 题意:Misha 和 Grisha 是2个很喜欢恶作剧的孩子, 每天早上 Misha 会从地铁站 s 通过最短的路到达地铁站 f, 并且在每 ...

  8. hdu 5974 A Simple Math Problem(数学题)

    Problem Description Given two positive integers a and b,find suitable X and Y to meet the conditions ...

  9. CodeForces 980 C Posterized

    Posterized 题意:将[0,255] 分成 若干段, 每一段的长度最多为k, 每一个数只能被放进一个段里, 然后每一段的数组都可以被这一段最小的数字表示, 求最小的字典序. 题解:每次一个访问 ...

  10. uva 796 C - Critical Links(tarjan求桥)

    题目链接:https://vjudge.net/contest/67418#problem/C 题意:求出桥的个数并且按顺序输出 题解:所谓桥就是去掉这条边后连通块增加,套用一下模版就行. #incl ...