@

作为一个优秀的框架, 其除了要解决大部分的流程之外, 还需要提供给使用者能够自定义的能力。 MyBatis 有缓存, 有插件接口等。我们可以通过自定义插件的方式来对 MyBatis 进行使用上的扩展。

以一个简单的 mysql 分页插件为例, 插件的使用包含以下步骤:

1 分页参数的传递

分页参数就是 offset 和 limit。 可以使用 RowBounds 来进行传递, 但是这样需要对原有的方法进行修改。 因此, 本例子通过 ThreadLocal 进行无痛觉的传递。

  1. /**
  2. * @author homejim
  3. */
  4. public class PageUtil {
  5. private static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
  6. public static void setPagingParam(int offset, int limit) {
  7. Page page = new Page(offset, limit);
  8. LOCAL_PAGE.set(page);
  9. }
  10. public static void removePagingParam() {
  11. LOCAL_PAGE.remove();
  12. }
  13. public static Page getPaingParam() {
  14. return LOCAL_PAGE.get();
  15. }
  16. }

在实际的使用过程中, 用户只需要再调用之前使用 PageUtil#setPagingParam 方法来进行分页参数的传递即可。 后续无需进行处理。

2 实现 Interceptor 接口

2.1 Interceptor 接口说明

先看看拦截器的接口。


  1. /**
  2. * 拦截器接口
  3. *
  4. * @author Clinton Begin
  5. */
  6. public interface Interceptor {
  7. /**
  8. * 执行拦截逻辑的方法
  9. *
  10. * @param invocation 调用信息
  11. * @return 调用结果
  12. * @throws Throwable 异常
  13. */
  14. Object intercept(Invocation invocation) throws Throwable;
  15. /**
  16. * 代理类
  17. *
  18. * @param target
  19. * @return
  20. */
  21. Object plugin(Object target);
  22. /**
  23. * 根据配置来初始化 Interceptor 方法
  24. * @param properties
  25. */
  26. void setProperties(Properties properties);
  27. }

因此, 在实际的使用中。我们要覆盖这几个方法。

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 来指定拦截的方法。

  1. @Documented
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Target({ElementType.TYPE})
  4. public @interface Intercepts {
  5. Signature[] value();
  6. }

Intercepts 注解中是 Signature 注解的数组。

  1. /**
  2. * 方法签名信息
  3. *
  4. * @author Clinton Begin
  5. */
  6. @Documented
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Target({})
  9. public @interface Signature {
  10. /**
  11. * 需要拦截的类型
  12. *
  13. * @return
  14. */
  15. Class<?> type();
  16. /**
  17. * 需要拦截的方法
  18. * @return
  19. */
  20. String method();
  21. /**
  22. * 被拦截方法的参数列表
  23. *
  24. * @return
  25. */
  26. Class<?>[] args();
  27. }

2.3 实现分页接口 PageInterceptor


  1. /**
  2. * 分页插件
  3. *
  4. * @author homejim
  5. */
  6. @Intercepts({
  7. @Signature(
  8. type = Executor.class,
  9. method = "query",
  10. args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
  11. )
  12. })
  13. @Slf4j
  14. public class PageInterceptor implements Interceptor {
  15. private static int MAPPEDSTATEMENT_INDEX = 0;
  16. private static int PARAMETER_INDEX = 1;
  17. private static int ROWBOUNDS_INDEX = 2;
  18. @Override
  19. public Object intercept(Invocation invocation) throws Throwable {
  20. // 从 Invocation 中获取参数
  21. final Object[] queryArgs = invocation.getArgs();
  22. final MappedStatement ms = (MappedStatement) queryArgs[MAPPEDSTATEMENT_INDEX];
  23. final Object parameter = queryArgs[PARAMETER_INDEX];
  24. // 获取分页参数
  25. Page paingParam = PageUtil.getPaingParam();
  26. if (paingParam != null) {
  27. // 构造新的 sql, select xxx from xxx where yyy limit offset,limit
  28. final BoundSql boundSql = ms.getBoundSql(parameter);
  29. String pagingSql = getPagingSql(boundSql.getSql(), paingParam.getOffset(), paingParam.getLimit());
  30. // 设置新的 MappedStatement
  31. BoundSql newBoundSql = new BoundSql(ms.getConfiguration(), pagingSql,
  32. boundSql.getParameterMappings(), boundSql.getParameterObject());
  33. MappedStatement mappedStatement = newMappedStatement(ms, newBoundSql);
  34. queryArgs[MAPPEDSTATEMENT_INDEX] = mappedStatement;
  35. // 重置 RowBound
  36. queryArgs[ROWBOUNDS_INDEX] = new RowBounds(RowBounds.NO_ROW_OFFSET, RowBounds.NO_ROW_LIMIT);
  37. }
  38. Object result = invocation.proceed();
  39. PageUtil.removePagingParam();
  40. return result;
  41. }
  42. @Override
  43. public Object plugin(Object o) {
  44. return Plugin.wrap(o, this);
  45. }
  46. @Override
  47. public void setProperties(Properties properties) {
  48. }
  49. /**
  50. * 创建 MappedStatement
  51. * @param ms
  52. * @param newBoundSql
  53. * @return
  54. */
  55. private MappedStatement newMappedStatement(MappedStatement ms, BoundSql newBoundSql) {
  56. MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(),
  57. new BoundSqlSqlSource(newBoundSql), ms.getSqlCommandType());
  58. builder.keyColumn(delimitedArrayToString(ms.getKeyColumns()));
  59. builder.keyGenerator(ms.getKeyGenerator());
  60. builder.keyProperty(delimitedArrayToString(ms.getKeyProperties()));
  61. builder.lang(ms.getLang());
  62. builder.resource(ms.getResource());
  63. builder.parameterMap(ms.getParameterMap());
  64. builder.resultMaps(ms.getResultMaps());
  65. builder.resultOrdered(ms.isResultOrdered());
  66. builder.resultSets(delimitedArrayToString(ms.getResultSets()));
  67. builder.resultSetType(ms.getResultSetType());
  68. builder.timeout(ms.getTimeout());
  69. builder.statementType(ms.getStatementType());
  70. builder.useCache(ms.isUseCache());
  71. builder.cache(ms.getCache());
  72. builder.databaseId(ms.getDatabaseId());
  73. builder.fetchSize(ms.getFetchSize());
  74. builder.flushCacheRequired(ms.isFlushCacheRequired());
  75. return builder.build();
  76. }
  77. public String getPagingSql(String sql, int offset, int limit) {
  78. StringBuilder result = new StringBuilder(sql.length() + 100);
  79. result.append(sql).append(" limit ");
  80. if (offset > 0) {
  81. result.append(offset).append(",").append(limit);
  82. }else{
  83. result.append(limit);
  84. }
  85. return result.toString();
  86. }
  87. public String delimitedArrayToString(String[] array) {
  88. if (array == null || array.length == 0) {
  89. return "";
  90. }
  91. Joiner joiner = Joiner.on(",");
  92. return joiner.join(array);
  93. }
  94. }

根据前面注解的讲解, 我们要拦截的是 Executor 类中以下方法

  1. <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 文件中, 加入以下的配置

  1. <plugins>
  2. <plugin interceptor="com.homejim.mybatis.plugin.PageInterceptor">
  3. </plugin>
  4. </plugins>

4 测试

  1. @Test
  2. public void testSelectList() {
  3. SqlSession sqlSession = null;
  4. try {
  5. sqlSession = sqlSessionFactory.openSession();
  6. PageUtil.setPagingParam(1, 2);
  7. List<Student> students = sqlSession.selectList("selectAll");
  8. for (int i = 0; i < students.size(); i++) {
  9. System.out.println(students.get(i));
  10. }
  11. List<Student> students2 = sqlSession.selectList("selectAll");
  12. for (int i = 0; i < students2.size(); i++) {
  13. System.out.println(students2.get(i));
  14. }
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. } finally {
  18. if (sqlSession != null) {
  19. sqlSession.close();
  20. }
  21. }
  22. }

其中, 第一个查询使用了分页, 第二个没有使用。 执行结果如下



第一个查询使用了分页, 因此有 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. 《Java 8 in Action》Chapter 7:并行数据处理与性能

    在Java 7之前,并行处理数据集合非常麻烦.第一,你得明确地把包含数据的数据结构分成若干子部分.第二,你要给每个子部分分配一个独立的线程.第三,你需要在恰当的时候对它们进行同步来避免不希望出现的竞争 ...

  2. Gym 101510C

    题意略. 思路: 由于xi的选取是任意的,所以我们不用去理会题目中的xi数列条件.主要是把关注点放在长度为L的线段覆盖至少k个整数这个条件上. 像这种取到最小的合法解的问题,我们应该要想到使用二分法来 ...

  3. Leetcode之二分法专题-875. 爱吃香蕉的珂珂(Koko Eating Bananas)

    Leetcode之二分法专题-875. 爱吃香蕉的珂珂(Koko Eating Bananas) 珂珂喜欢吃香蕉.这里有 N 堆香蕉,第 i 堆中有 piles[i] 根香蕉.警卫已经离开了,将在 H ...

  4. NLP(二十三)使用LSTM进行语言建模以预测最优词

    N元模型 预测要输入的连续词,比如 如果抽取两个连续的词汇,则称之为二元模型 准备工作 数据集使用 Alice in Wonderland 将初始数据提取N-grams import nltk imp ...

  5. CF 551 E GukiZ and GukiZiana

    https://codeforces.com/contest/551/problem/E 分块真强. 题意就是1.区间加,2.询问整个区间中,最远的两个x的距离. 分块,然后,每次找位子用二分找即可. ...

  6. CF Edu54 E. Vasya and a Tree DFS+树状数组

    Vasya and a Tree 题意: 给定一棵树,对树有3e5的操作,每次操作为,把树上某个节点的不超过d的子节点都加上值x; 思路: 多开一个vector记录每个点上的操作.dfs这颗树,同时以 ...

  7. CF - 1110F Nearest Leaf

    题目传送门 题解: 先用题目给定的dfs方式得到dfs序,记录下出入的dfs序. 很明显可以得知的是,以u为根的子树的dfs序在 in[u] - out[u] 的范围之内. 将每个询问先全部存到对应的 ...

  8. hdu 1176免费馅饼(记忆化搜索)

    题目链接 http://acm.hdu.edu.cn/showproblem.php?pid=1176 题意不解释了 简单的记忆化搜索可以拿来练练手,注意要从pos = 5 开始搜索 #include ...

  9. [USACO07OCT]障碍路线 & yzoj P1130 拐弯 题解

    题意 给出n* n 的图,A为起点,B为终点,* 为障碍,.可以行走,问最少需要拐90度的弯多少次,无法到达输出-1. 解析 思路:构造N * M * 4个点,即将原图的每个点分裂成4个点.其中点(i ...

  10. 分享个人学习js的笔记

    1.回到顶部效果. 2.滚动条向上滚动式,滑动滚轮.解决bug的方法. 3.有关Document. 4.getElementByClassName();获取元素类名的封装.单个类名的元素.任然不完美. ...