这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
上文Mybatis之动态SQL介绍了Mybatis中很常用的动态标签,本来继续来介绍一下在Mybatis中十分常用的分页查询。废话不多说,开始今天的内容。
准备
- 查询参数的POJO中,在
offset
参数的get方法中,对offset
的值进行了相应的计算。 - 查询结果的POJO中,在构造方法中对总页数
pages
进行了相应的计算。
// 封装查询参数的POJO
public class QueryCondition {
/**
* ID
*/
private Integer id;
/**
* 名称
*/
private String name;
/**
* 价格
*/
private Integer price;
/**
* 分类
*/
private Integer category;
private List<Integer> categoryList;
/**
* 分页参数
*/
//偏移量
private Integer offset;
//每页条数
private Integer pageSize;
//页数
private Integer pageNum;
// 计算分页的起始位置
public Integer getOffset() {
return ((pageNum == null || pageNum < 1 ? 1 : pageNum) - 1) * (pageSize == null ? 3 : pageSize);
}
// 省略其余get/set方法
}
// 封装查询结果的POJO
public class PageVO<T> {
/**
* 每页条数
*/
private int pageSize;
/**
* 页码
*/
private int pageNum;
/**
* 总页数
*/
private int pages;
/**
* 总条数
*/
private int total;
/**
* 当前页的数据
*/
private List<T> data;
public PageVO(int pageSize, int pageNum, int total, List<T> data) {
this.pageSize = pageSize;
this.pageNum = pageNum;
this.total = total;
this.data = data;
this.pages = total / pageSize + (total % pageSize == 0 ? 0 : 1);
}
// 省略get/set方法
}
复制代码
- 数据库数据
手写limit分页
- 顾名思义,这种方法就是我们自己在SQL中添加
limit
关键字来实现分页;
<select id="findPageByHand" parameterType="org.apache.ibatis.z_run.pojo.QueryCondition" resultType="org.apache.ibatis.z_run.pojo.Purchase">
select
<include refid="Base_Column_List" />
from purchase
<where>
<if test="id != null">
And id = #{id,jdbcType=INTEGER}
</if>
<if test="category != null">
And category = #{category,jdbcType=INTEGER}
</if>
</where>
<if test="pageSize != null and pageNum != null">
limit #{offset}, #{pageSize}
</if>
</select>
复制代码
- 另外,为了返回一个总条数,我们还得单独再写一条SQL,用于统计分页查询的总条数,在计算总页数的时候使用。
<select id="count" parameterType="org.apache.ibatis.z_run.pojo.QueryCondition" resultType="java.lang.Integer">
select
count(1)
from
(
select
<include refid="Base_Column_List"/>
from purchase
<where>
<if test="id != null">
And id = #{id,jdbcType=INTEGER}
</if>
<if test="category != null">
And category = #{category,jdbcType=INTEGER}
</if>
</where>
) t1
</select>
复制代码
- 测试代码及返回结果
int pageSize = 2;
int pageNum = 1;
@Test
public void pageQueryByHand() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
QueryCondition condition = new QueryCondition();
condition.setPageNum(pageNum);
condition.setPageSize(pageSize);
System.out.println(new PageVO<>(pageSize, pageNum, mapper.count(condition), mapper.findPageByHand(condition)));
}
复制代码
DEBUG [main] - ==> Preparing: select count(1) from ( select id, `name`, price, category from purchase ) t1
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 1
DEBUG [main] - Cache Hit Ratio [org.apache.ibatis.z_run.mapper.PurchaseMapper]: 0.0
DEBUG [main] - ==> Preparing: select id, `name`, price, category from purchase limit ?, ?
DEBUG [main] - ==> Parameters: 0(Integer), 2(Integer)
DEBUG [main] - <== Total: 2
PageVO{pageSize=2, pageNum=1, pages=4, total=7, data=[Purchase{id=1, name='可乐', price=6, category=1}, Purchase{id=2, name='爆米花', price=18, category=2}]}
复制代码
RowBounds分页
RowBounds
对象是Mybatis提供的一个分页类,只需要在查询的方法参数中加上这个对象即可使用。
<select id="findPageByRowBounds" parameterType="org.apache.ibatis.z_run.pojo.QueryCondition" resultType="org.apache.ibatis.z_run.pojo.Purchase">
select
<include refid="Base_Column_List" />
from purchase
<where>
<if test="id != null">
And id = #{id,jdbcType=INTEGER}
</if>
<if test="category != null">
And category = #{category,jdbcType=INTEGER}
</if>
</where>
</select>
复制代码
- 使用
RowBounds
对象后,就不需要在SQL语句中写limit
语句了,但是仍然要对满足条件的数据条数进行单独查询,其结果将用于计算总页数,语句同上。 - 测试代码及返回结果
int pageSize = 2;
int pageNum = 2;
@Test
public void pageQueryByRowBounds() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
QueryCondition condition = new QueryCondition();
System.out.println(new PageVO<>(pageSize, pageNum, mapper.count(condition),
mapper.findPageByRowBounds(condition, new RowBounds((pageNum - 1) * pageSize, pageSize))));
}
复制代码
DEBUG [main] - ==> Preparing: select count(1) from ( select id, `name`, price, category from purchase ) t1
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 1
DEBUG [main] - Cache Hit Ratio [org.apache.ibatis.z_run.mapper.PurchaseMapper]: 0.0
DEBUG [main] - ==> Preparing: select id, `name`, price, category from purchase
DEBUG [main] - ==> Parameters:
PageVO{pageSize=2, pageNum=2, pages=4, total=7, data=[Purchase{id=8, name='火腿', price=3, category=1}, Purchase{id=9, name='火腿', price=3, category=1}]}
复制代码
- 可以看到,使用
RowBounds
进行分页时,SQL语句中并没有添加limit
关键字进行分页,这是因为RowBounds
分页是将所有的数据查询到内存中以后,再使用RowBounds
参数进行分页的,对内存的压力很大,性能很低,因此这种方式不建议使用。
分页插件
-
这里主要介绍目前使用最广泛的Pagehelper插件。Pagehelper插件的原理是使用拦截器拦截SQL语句的执行,并为SQL语句添加
limit
关键字进行分页查询,以及count语句来查询总数(就不需要我们自己手写count方法来计算数据总条数了)。 -
导入依赖
<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
复制代码
- 配置文件
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
复制代码
- XML语句同
RowBounds
相同,不需要在SQL语句中写limit
关键字。
<select id="findByCondition" parameterType="org.apache.ibatis.z_run.pojo.QueryCondition" resultType="org.apache.ibatis.z_run.pojo.Purchase">
select
<include refid="Base_Column_List" />
from purchase
<where>
<if test="id != null">
And id = #{id,jdbcType=INTEGER}
</if>
<if test="category != null">
And category = #{category,jdbcType=INTEGER}
</if>
</where>
</select>
复制代码
- 测试代码及查询结果。查询结果的总条数以及总页数都可以在
Page
对象中直接获取。
int pageSize = 2;
int pageNum = 2;
@Test
public void pageQueryByPageHelper() {
PurchaseMapper mapper = sqlSession.getMapper(PurchaseMapper.class);
QueryCondition condition = new QueryCondition();
// 这一行代码之后需要立即接上需要分页的查询方法,否则可能导致分页失效
Page page = PageHelper.startPage(pageNum, pageSize);
List<Purchase> purchaseList = mapper.findByCondition(condition);
System.out.println(new PageVO<>(pageSize, pageNum, (int) page.getTotal(), purchaseList));
}
复制代码
DEBUG [main] - ==> Preparing: SELECT count(0) FROM purchase
DEBUG [main] - ==> Parameters:
DEBUG [main] - <== Total: 1
DEBUG [main] - Cache Hit Ratio [org.apache.ibatis.z_run.mapper.PurchaseMapper]: 0.0
DEBUG [main] - ==> Preparing: select id, `name`, price, category from purchase LIMIT ?, ?
DEBUG [main] - ==> Parameters: 2(Long), 2(Integer)
DEBUG [main] - <== Total: 2
PageVO{pageSize=2, pageNum=2, pages=4, total=7, data=Page{count=true, pageNum=2, pageSize=2, startRow=2, endRow=4, total=7, pages=4, reasonable=false, pageSizeZero=false}[Purchase{id=8, name='火腿', price=3, category=1}, Purchase{id=9, name='火腿', price=3, category=1}]}
复制代码
Tips
- Mybatis配置文件的标签顺序是有讲究的,如果顺序出问题,是会报错的。例如:
<!--指定Mapper.xml所在位置-->
<mappers>
<mapper resource="resources/xml/PurchaseMapper.xml"/>
</mappers>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
复制代码
- 此时,配置文件报错,内容如下:
- 运行项目报错,内容大体如下:
org.apache.ibatis.exceptions.PersistenceException:
### Error building SqlSession.
### Cause: org.apache.ibatis.builder.BuilderException: Error creating document instance.
.
.
.
Caused by: org.xml.sax.SAXParseException; lineNumber: 101; columnNumber: 17; 元素类型为 "configuration" 的内容必须匹配 "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)"。
复制代码
以上便是对于Mybatis中分页方式的介绍,一般来说,使用Pagehelper更为方便,但是引入第三方插件之后可能会有一些bug,需要在遇到之后进行仔细的排查。而自己手写分页不容易出现问题,但是比较麻烦。具体情况具体分析,根据自身情况挑选适合自己的方式:)
近期评论