总 Mybatis-PageHelper介绍
1. 引入分页插件
使用Maven
在pom.xml中添加如下依赖
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>最新版本</version>
</dependency>
最新版本号可以从首页查看
2. 配置拦截器插件
特别注意,新版拦截器是com.github.pagehelper.PageInterceptor
。com.github.pagehelper.PageHelper
现在是一个特殊的dialect
实现类,是分页插件的默认实现类,提供了和以前相同的用法。
1.在MyBatis配置xml中配置拦截器插件
<!--
plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
properties?, settings?,
typeAliases?, typeHandlers?,
objectFactory?,objectWrapperFactory?,
plugins?,
environments?, databaseIdProvider?, mappers?
-->
<plugins>
<!-- com.github.pagehelper为PageHelper类所在包名 -->
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<!-- 使用下面的方式配置参数,后面会有所有的参数介绍 -->
<property name="param1" value="value1"/>
</plugin>
</plugins>
2.在Spring配置文件中配置拦截器插件
使用spring的属性配置方式,可以使用plugins
属性像下面这样配置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注意其他配置 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageInterceptor">
<property name="properties">
<!--使用下面的方式配置参数,一行配置一个 -->
<value>
params=value1
</value>
</property>
</bean>
</array>
</property>
</bean>
3. 分页插件参数介绍
分页插件提供了多个可选参数,这些参数使用时,按照上面两种配置方式的示例配置即可。
dialect
: 默认情况下会使用PageHelper方式进行分页,如果想要实现自己的分页逻辑,可以实现Dialect(com.github.pagehelper.Dialect
)接口,然后配置该属性为实现类的全限定名称
下面几个参数都是针对默认dialect情况下的参数。使用自定义dialect实现时,下面的参数没有任何作用。
helperDialect
:分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。你可以配置helperDialect
属性来指定分页插件使用哪种方式。配置时,可以使用下面的缩写值:oracle
,mysql
,mariadb
,sqlite
,hsqldb
,postgresql
,db2
,sqlserver
,nformix
,h2
,sqlserver2012
,derby
特别注意:使用SqlServer2012数据库时,需要手动指定为sqlserver2012
,否则会使用SqlServer2015的方式进行分页。 你也可以实现AbstractHelperDialect
, 然后配置该属性为实现类的全限定名称即可使用自定义的实现方法。
offsetAsPageNum
:默认值为false
,该参数对使用RowBounds
作为分页参数时有效。当该参数设置true
时,会将RowBounds
中的offset
参数当成pageNum
使用,可以用页码和页面大小两个参数进行分页。rowBoundsWithCount
:默认值为false
,该参数对使用RowBounds
作为分页参数时有效。当该参数设置为true
时,使用RowBounds
分页会进行count查询。pageSizeZero
:默认值为false
,当该参数设置为true
时,如果pageSize=0
或者RowBounds.limit = 0
就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是Page
类型)。reasonable
:分布合理化参数,默认值为false
。当该参数设置为true
时,如果pageSize=0
或者RowBounds.limit=0
就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是Page
类型)。params
:为了支持startPage(Object params)方法
,增加了该参数来配置参数映射,用于从对象中根据属性名取值,可以配置pageNum, pageSize, count, pageSizeZero, reasonable
,不配置映射的用默认值,默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
。supportMethodsArguments
:支持通过Mapper接口参数来传递分页参数,默认值false
,分页插件会从查询方法的参数中,自动根据上面params配置的字段中取值,查找到合适的值时就会自动分页。使用方法可以参考测试代码中的con.github.pagehelper.test.basic
包下的ArgumentsMapTest
和ArgumentsObjectTest
。
autoRuntimeDailect
:默认值为false
。设置为true
时,允许在运行时根据多数据源自动识别对应方言的分页(不支持自动选择sqlserver2012
,只能使用sqlserver
),用法和注意事项参考下面的场景五closeConn
:默认值为true
。当使用运行时动态数据源或没有设置helperDialect
属性自动获取数据库类型时,会自动获取一个数据库链接,通过该属性来设置是否关闭获取的这个链接,默认true
关闭,设置为false
后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定。
重要提示
当offsetAsPageNum=false
的时候,由于pageNum
问题,RowBounds
查询的时候reasonable
会强制为false。使用PageHelper.startPage
方法不受影响。
4.如何选择配置这些参数
单独看每个参数的说明可能是一件让人不爽的事情,这里列举一些可能会用到某些参数的情况。
场景一
如果你仍然在用类似ibatis式的命名空间调用方式,你也许会用到rowBoundsWithCount
,分页插件对RowBounds
支持和MyBatis
默认的方式是一致,默认情况下不会进行count查询,如果你想在分页查询时进行count查询,以及使用更强大的PageInfo
类,你需要设置该参数为true
。
场景二
如果你仍然在用类似ibatis式的命名空间调用方式,你觉得RowBounds
中的两个参数offset, limit不如pageNum, pageSize
容易理解,你可以使用offsetAsPageNum
参数,将该参数设置为true
后,offset
会当成pageNum
使用,limit
和pageSize
含义相同。
场景三
如果觉得某个地方使用分页后,你仍然想通过控制参数查询全部的结果,你可以配置pageSizeZero
为true
,配置后,当pageSize=0
或者RowBounds.limit=0
就会查询出全部的结果。
场景四
如果你分布插件使用于类似分页查看列表的数据,如新闻列表、软件列表,你希望用户输入的页数不在合法范围(每一页最后一页之外)时能够正确的响应到正确的结果页面,那么你可以配置reasonable
为true
,这时如果pageNum <= 0
会查询第一页,如果pageNum > 总页数
会查询最后一页。
场景五
如果你在Spring中配置了动态数据源,并且连接不同类型的数据库,这时你可以配置autoRuntimeDialect
为true
,这样在使用不同数据源时,会使用匹配的分页进行查询。这种情况下,你还需要特别注意closeConn
参数,由于获取数据源类型会获取一个数据库连接,所以需要通过这个参数来控制获取连接后,是否关闭该链接。默认为true
,有些数据库连接关闭后就没法进行后续的数据库操作。而不些数据库链接不关闭就会很快由于链接数用完而导致数据库无响应。所以在使用该功能时,特别需要注意你使用的数据源是否需要关闭数据库的链接。
当不使用动态数据源而只是自动获取helperDialect
时,数据库连接只会获取一次,所以不需要担心占用的这一个链接是否会导致数据库出错,但是最好也根据数据源的特性选择是否关闭链接。
如何在代码中使用
分页插件支持以下几种调用方式:
//第一种,RowBounds方式的调用。
List<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(0, 10))
//第二种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.startPage(1, 10);
List<Country> list = countryMapper.selectIf(1);
//第三种,Mapper接口方式的调用,推荐这种使用方式。
PageHelper.offsetPage(1, 10);
List<Country> list = countryMapper.selectIf(1);
//第四种,参数方法调用
//存在以下的Mapper接口方法,你不需要在xml处理后两个参数
public interface CountryMapper {
List<Country> selectByPageNumSize(
@Param("user") User user,
@Param("pageNum") int pageNum,
@Param("pageSize") int pageSize);
}
//配置supportMethodsArguments=true
//在代码中直接调用
List<Country> list = countryMapper.selectByPageNumSize(user, 1, 10);
//第五种,参数对象
//如果 pageNum和pageSize存在于User对象中,只要参数有值,也会被分页
//有如下User对象
public class User{
// 其他fields
// 下面两个参数名和params配置的名字一致
private Integer pageNum;
private Integer pageSize;
}
//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数
public interface CountryMapper {
List<Country> selectByPageNumSize(User user);
}
//当 user 中的 pageNum!= null && pageSize!= null 时,会自动分页
List<Country> list = countryMapper.selectByPageNumSize(user);
//第六种,ISelect 接口方式
//jdk6,7用法,创建接口
Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() {
@Override
public void doSelect() {
countryMapper.selectGroupBy();
}
});
//jdk8 lambda用法
Page<Country> page = PageHelper.startPage(1, 10).doSelectPage(()-> countryMapper.selectGroupBy());
//也可以直接返回PageInfo,注意doSelectPageInfo方法和doSelectPage
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() {
@Override
public void doSelect() {
countryMapper.selectGroupBy();
}
});
//对应的lambda用法
pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> countryMapper.selectGroupBy());
//count查询,返回一个查询语句的count数
long total = PageHelper.count(new ISelect() {
@Override
public void doSelect() {
countryMapper.selectLike(country);
}
});
//lambda
total = PageHelper.count(()->countryMapper.selectLike(country));
下面对最常用的方式进行详细介绍
1). RowBounds方式的调用
List<Country> list = sqlSession.selectList("x.y.selectIf", null, new RowBounds(1, 10));
使用这种调用方式时,你可以使用RowBounds参数进行分页,这种方式侵入性最小,我们可以看到,通过RowBounds方式调用只是使用了这个参数,并没有增加其他任何内容。
分页插件检测到使用了RowBounds参数时,就会对该查询进行物理分页。
关于这种方式的调用,有两个特殊的参数是针对RowBounds
的,你可以参看上面的场景一和场景二
注:不只有命名空间方式可以用RowBounds,使用接口的时候也可以增加RowBounds参数,例如:
List<Country> selectAll(RowBounds rowBounds);
注意: 由于默认情况下RowBounds
无法获取查询总数,分页插件提供了一个继承自RowBounds
的PageRowBounds
,这个对象中增加了total
属性,执行分页查询后,可以从该属性得到查询总数。
2). PageHelper.startPage 表态方法调用
除了PageHelper.startPage
方法外,还提供了类似用法的PageHelper.offsetPage
方法。
在你需要进行分页的MyBatis查询方法前调用PageHelper.startPage
静态方法即可,紧跟在这个方法后的第一个MyBatis查询方法会被进行分页