Mybatis:拦截器实现分页
在前面往数据库中批量插入了大量数据,那么如何将这些数据进行分页展示呢?这里就需要介绍Mybatis中的拦截器了,通过拦截器可以实现数据的分页(在数据库中物理分页方式读取数据)。 本篇主要介绍以下几点内容:1、Mybatis的四大对象、插件原理及接口;2、Mybatis的插件开发过程;3、代理模式的介绍;4、如何使用PageHelper插件来实现分页功能。
Mybatis的四大对象
Mybatis对持久层的操作就是借助于四大核心对象,分别是:ParameterHandler
、ResultSetHandler
、StatementHandler
、Executor
。
其中ParameterHandler
用于处理SQL的参数对象;ResultSetHandler
用于处理SQL的返回结果集;StatementHandler
是数据库的处理对象,用于执行SQL语句;Executor
是Mybatis的执行器,用于执行增删改查操作。
MyBatis支持用插件对四大核心对象进行拦截,对MyBatis来说插件就是拦截器,用来增强核心对象的功能,增强功能本质上是借助于底层的动态代理实现的,换句话说MyBatis中的四大对象都是代理对象。可以把拦截器理解为Servlet,对传递过来的请求进行拦截过滤和封装。
Mybatis插件原理
Mybatis的插件借助于责任链模式进行拦截处理,同时使用动态代理对目标对象进行包装达到拦截的目的,然后作用于Mybatis的作用域对象之上。那么插件具体是如何拦截并附加额外的功能的呢?我们以ParameterHandler 为例进行说明:
1 | public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object object, BoundSql sql, InterceptorChain interceptorChain){ |
接着再来看看这个pluginAll方法的源码,可以发现其实就是对每个拦截器对象都调用plugin方法,然后将生成的target对象返回过去。
1 | public Object pluginAll(Object target) { |
也就是说interceptorChain
中保存了所有的拦截器(interceptors),Mybatis初始化时创建的。接着调用拦截器链中的拦截器依次的对目标进行拦截或增强,interceptor.plugin(target)
中的target可以理解为Mybatis中的四大对象,返回的target是被重重代理后的对象。
Mybatis插件接口—Interceptor
如何让一个类变成一个插件呢?很简单,只需让该类实现Interceptor
接口(org.apache.ibatis.plugin.Interceptor
包内的Interceptor
接口),并重写其中的Intercept
、plugin
和setProperties
方法。
其中Intercept
方法是插件的核心方法,是拦截目标对象的目标方法;plugin
方法,用于生成target代理对象;setProperties
方法用于传递插件所需参数。
注意使用之前的mybatis_insertdata
项目进行代码的演示。
在mybatis-config.xml
文件中新增plugins配置:
1 | <plugins> |
在com.envy.interceptor
包中新建一个FirstInterceptor.java文件,里面的代码为:
1 | package com.envy.interceptor; |
新建一个查询方法,因为只有查询方法才会返回ResultSet。在PersonMapper.java
文件中新增addPersons
方法:
1 | //测试Interceptor接口 |
PersonMapper.xml
文件中新增代码如下:
1 | <!--测试Interceptor接口--> |
在ParameterTest.java
文件中新增selectPersonById
方法:
1 | //测试Interceptor接口 |
运行结果如下:
1 | 插件配置的初始化参数为:{hello=world} |
可以发现这个将要包装的目标对象为
一共输出了4次,因为Mybatis拦截器会默认去拦截前面所说的四大对象。
插件开发的过程是:确定拦截的签名–>实现拦截方法–>配置和运行。
多插件开发过程
多插件开发过程中需要注意两点:1、创建代理对象时,按照插件配置的顺序进行包装;2、执行目标方法后,是按照代理的逆向进行执行:
1 | <plugins> |
也就是说执行到拦截目标对象的目标方法intercept
处就不是FirstInterceptor在前面了,而是先执行SecondtInterceptor(注意这里的前提是各个拦截器的签名是一致的)。
针对上述情况,接下来简单总结一下:1、遵循插件尽量不使用的原则,因为会修改底层设计;2、插件是生成层层代理对象的责任链模式,使用反射机制实现;3、插件的编写要考虑全面,特别是多个插件层层代理的时候。
分页
分页原理
分页通常分为两种:内存分页和物理分页。
内存分页:从数据库中一次性将数据读取到内存,根据页面显示的条数去内存中查询。虽然占用了较大的内存空间,但是减少了与数据库的交互,数据发生改变,数据库的最新状态不能实时反映到操作中,实时性差。
物理分页:将整个大的ResultSet转换成一个个小的ResultSet,降低了内存的开销,增加了与数据库的交互,能够获取数据库的最新状态,实时性强。(实际开发使用)
MySQL中实现分页的方式为:
1 | select * from person limit 0,10; /*第一页*/ |
从上面可以分析出规律:开始记录索引的规律(当前页-1)*每页的条数;一共有多少页:总记录%条数==0?(总记录%条数):(总记录%条数+1)。同时分页主要注意三个参数:当前页、每页的条数、总记录数。
使用pageHelper插件进行分页
点击这里了解更多关于pageHelper插件的信息。