@

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

    CMake与NDK搭配使用时,可以配置的部分变量: 1. `ANDROID_PLATFORM`:指定Android的目标版本,对应`$NDK/platforms/`目录下的版本.通常情况下是`defa ...

  2. RANSAC简史(一)——RANSAC之初

    在开始正式的介绍之前,先做一个简单的定义,以免产生歧义: 1.本文中的“数据点”是指: 1)对于直线拟合.平面拟合等问题,即为相应的二维/三维坐标点: 2)对于从匹配点中估计基本矩阵.单应矩阵等问题, ...

  3. Liunx软件安装之Redis

    Redis是一个开源(BSD许可),内存数据结构存储,用作数据库,缓存和消息代理.它支持数据结构,如字符串,散列,列表,集合,带有范围查询的排序集,位图,超级日志和带有半径查询的地理空间索引.Redi ...

  4. 前端开发-CSS语法标准

    一.命名规则说明: 1.命名规则说明: 所有的命名最好都小写 属性的值一定要用双引号("")括起来,且一定要有值如class="nav",id="na ...

  5. Delphi - cxGrid内容xlsx、xls、csv格式导出

    .xls格式导出,uses中添加cxGridExportLink 代码如下: function SaveToExcel(gridMain: TcxGrid; FileName: string): st ...

  6. 迁移桌面程序到MS Store(10)——在Windows S Mode运行

    首先简单介绍Windows 10 S Mode,Windows在该模式下,只能跑MS Store里的软件,不能通过其他方式安装.好处是安全有保障,杜绝一切国产流氓软件.就像iOS一样,APP进商店都需 ...

  7. c++ uconcontext.h实现协程

    目录 c++ uconcontext.h实现协程 什么是协程? ucontext.h库 库的使用示例 代码地址 c++ uconcontext.h实现协程 什么是协程? 协程是一种程序组件,是由子例程 ...

  8. Atcoder D - XOR Replace(思维)

    题目链接:http://agc016.contest.atcoder.jp/tasks/agc016_d 题解:稍微想一下就知道除了第一次的x是所有的异或值,之后的x都是原先被替换掉的a[i]所以要想 ...

  9. Atcoder D - Black and White Tree(树dp+博弈)

    题目链接:http://agc014.contest.atcoder.jp/tasks/agc014_d 题意:有一棵树先手涂白色,后手涂黑色,直到不能再涂为止.涂完后再把所有黑色直接相邻的白色都变成 ...

  10. Go组件学习——手写连接池并没有那么简单

    1.背景 前段时间在看gorm,发现gorm是复用database/sql的连接池. 于是翻了下database/sql的数据库连接池的代码实现,看完代码,好像也不是很复杂,但是总觉得理解不够深刻,于 ...