@

作为一个优秀的框架, 其除了要解决大部分的流程之外, 还需要提供给使用者能够自定义的能力。 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学习——面向对象编程

    关于python面向对象编程,请参考: https://blog.csdn.net/zhoudaxia/article/details/23341261

  2. python学习——高阶函数

    递归函数 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数.使用递归函数的优点是逻辑简单清晰,缺点就是过深的调用会导致栈溢出.但是针对尾递归优化的语言可以通过尾递归防 ...

  3. 约瑟夫环问题:有n个人围成一圈,顺序排号。从第一个人开始报数(从1到3报数),凡报到3的人退出圈子,问最后留下的是原来第几号的那位。

    首先,我最大的学习来源不是百度而是我群友~~在这里表白一波我热爱学习的群友们!然后今天群里突然有人提出了题目的这个问题:有n个人围成一圈,顺序排号.从第一个人开始报数(从1到3报数),凡报到3的人退出 ...

  4. Windows Server 2008 R2

    Windows Server 2008 R2 Windows Server Core 微软因为向往 Linux 的纯命令行, 提出了 Windows Server Core 只能使用命令, 但是只要配 ...

  5. 分布式唯一ID生成算法-雪花算法

    在我们的工作中,数据库某些表的字段会用到唯一的,趋势递增的订单编号,我们将介绍两种方法,一种是传统的采用随机数生成的方式,另外一种是采用当前比较流行的“分布式唯一ID生成算法-雪花算法”来实现. 一. ...

  6. CF-920C-Swap Adjacent Elements 贪心

    题意 给你一个1-n的排列. 并给你一个字符串——其中用0和1表示对应数列中的位置上的值可不可以和后面相邻的数交换. 判断该数列能否在限制中交换为不降序数列. 思路 由于刚学了树状数组,一开始以为是用 ...

  7. BZOJ 1935: [Shoi2007]Tree 园丁的烦恼 +CDQ分治

    1935: [Shoi2007]Tree 园丁的烦恼 参考与学习:https://www.cnblogs.com/mlystdcall/p/6219421.html 题意 在一个二维平面中有n颗树,有 ...

  8. 福建工程学院16级第一周寒假作业E题----第七集,奇思妙想

    第七集,奇思妙想                                                                                            ...

  9. win10 设定计划任务时提示所指定的账户名称无效,如何解决?

    我想把我的 python 爬虫脚本设定为自动定时执行,我的设备是win10 操作系统,这将用到系统自带的计划任务功能.且我希望不管用户是否登录都要运行该定时任务,但在设置计划任务的属性时,遇到一个报错 ...

  10. 全栈开发博客系统(nodejs+vuejs+mongodb)

    本篇文章将会介绍如何使用nodejs+vuejs构建个人博客. 主要分三部分内容: 环境准备 博客后端管理系统(admin) 后端服务(主要提供admin及web端接口) 博客前端展示(web) 环境 ...