MyBatis 插件使用-自定义简单的分页插件

目录

  • 1 分页参数的传递
  • 2 实现 Interceptor 接口
    • 2.1 Interceptor 接口说明
    • 2.1 注解说明
    • 2.3 实现分页接口 PageInterceptor
  • 3. 更改配置
  • 4 测试

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

原文地址:https://www.cnblogs.com/homejim/p/11600720.html

时间: 2024-07-31 18:41:29

MyBatis 插件使用-自定义简单的分页插件的相关文章

angularjs自定义指令实现分页插件

由于最近的一个项目使用的是angularjs1.0的版本,涉及到分页查询数据的功能,后来自己就用自定义指令实现了该功能.现在单独做了个简易的小demo,主要是为了分享自己写的分页功能.注:本实例调用的是真实接口数据. 首先.小demo的目录结构如下: 一.代码部分 下面直接把每一个文件的代码贴出来,重点是ListCtrl.js和pageDirective.js: 1.index.html <!DOCTYPE html> <html lang="en" ng-app=&

分页插件PageHelper配置步骤(mybatis)

原理: mybatis执行sql步骤: 通过sqlsessionFactory  sqlsession Exector  (执行器对象)mappedstatement(sql语句封装) 在执行mappedstatement前在sql语句上加上limit即可实现分页 步骤: 一.引入pageHelper的jar包 二.在mybatis的xml文件中配置分页插件 <!-- plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下: properties?, settings?, typeA

品优购商城项目(二)mybatis分页插件

品优购商城项目第二天,使用mybatis分页插件实现分页. 一.引用mybatis分页插件 SqlMapConfig.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config

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

MyBatis 今天大年初一,你在学习!不学习做什么,斗地主...人都凑不齐.学习吧,学习使我快乐!除了诗和远方还有责任,我也想担当,我也想负责,可臣妾做不到啊,怎么办?你说怎么办,为啥人家能做到你做不到,因为人家比你多做了那么一点点.哪一点点?就那么一点点,只要你也多做那么一点点,不就做到了!...就那么一点点呀,我回顾SE去了.万丈高楼平地起,基础打的牢,怕什么狂风暴雨 MyBatis 动态SQL MyBatis为了解决通过一些不确定性的条件进行SQL语句的拼接操作的问题, 提供了动态SQL

Mybatis分页插件2.0版本发布

项目地址:http://git.oschina.net/free/Mybatis_PageHelper 分页插件示例: http://blog.csdn.net/isea533/article/details/24700339 v2.0更新内容: 支持Mybatis缓存,count和分页同时支持(二者同步) 修改拦截器签名,拦截Executor,签名如下: @Intercepts(@Signature(type = Executor.class, method = "query", a

简单分页插件,kkpager

后台用习惯了框架分页,到做网页的时候就不自己去写分页了,并且需要的分页也是比较简单的,然后就在网上找了个简单的分页插件:kkpager 需要导入的js 和cs: kkpager.js kkpager.min.js kkpager.css 效果图如下: 以下为实例: ----------BaseDao.java //分页查询 public List<T> listByPage(String hql,int start,int maxNum,Object ...params){  Query q

Mybatis的分页插件PageHelper分页失效的原因

引用博客:个人博客地址:https://alexaccele.github.io/ PageHelper是Mybatis的一个很好的分页插件,但要使用它的分页功能需要注意一下几点 1.导入相关包,例如maven导入依赖 1 <dependency> 2 <groupId>com.github.pagehelper</groupId> 3 <artifactId>pagehelper</artifactId> 4 <version>5.

浅谈jQuery Pagination Ajax 分页插件的使用

插件介绍 此插件是jQuery的ajax分页插件.分页切换时无刷新也无延迟,因为是一次性加载的.如果你用到此插件作分页的时候,涉及到的数据量大,建议不要使用此插件,因为加载慢会导致用户体验不好! 插件使用 此插件使用比较简单.主要引入以下文件就可以用了 1.jquery.js依赖库 2.pagination.css 这个主要是样式,我们在元素使用的时候将分页列表放在class类为pagination的标签内即可 <div class='pagination'></div> 3.jq

jqPaginator-master | kkpager-master 这两个分页插件的使用方法

首先:百度"分页插件" 就会 找到这条链接: jQuery分页插件 - 开源软件库 - 开源中国社区 打开,在里面下载我们要的jqPaginator-master 分页插件包.和kkpager-master分页插件包 下载完后,将这两个分页插件包解压到当前文件夹,然后将这两个插件文件包放到我们项目的根文件夹下 以下我们就来试试这连个分页插件的使用方法 第一步:创建一个Home控制器 using MvcApp.Models; using System; using System.Coll