【Mybatis】Mybatis之分页查询 准备 手写lim

这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战

上文Mybatis之动态SQL介绍了Mybatis中很常用的动态标签,本来继续来介绍一下在Mybatis中十分常用的分页查询。废话不多说,开始今天的内容。

准备

  • 查询参数的POJO中,在offset参数的get方法中,对offset的值进行了相应的计算。
  • 查询结果的POJO中,在构造方法中对总页数pages进行了相应的计算。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
java复制代码// 封装查询参数的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方法
}
  • 数据库数据

image.png

手写limit分页

  • 顾名思义,这种方法就是我们自己在SQL中添加limit关键字来实现分页;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
xml复制代码<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,用于统计分页查询的总条数,在计算总页数的时候使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
xml复制代码<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>
  • 测试代码及返回结果
1
2
3
4
5
6
7
8
9
10
11
java复制代码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)));
}
1
2
3
4
5
6
7
8
txt复制代码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提供的一个分页类,只需要在查询的方法参数中加上这个对象即可使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
xml复制代码<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语句了,但是仍然要对满足条件的数据条数进行单独查询,其结果将用于计算总页数,语句同上。
  • 测试代码及返回结果
1
2
3
4
5
6
7
8
9
10
java复制代码    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))));
}
1
2
3
4
5
6
7
txt复制代码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方法来计算数据总条数了)。
  • 导入依赖
1
2
3
4
5
6
xml复制代码<!-- https://mvnrepository.com/artifact/com.github.pagehelper/pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
  • 配置文件
1
2
3
xml复制代码<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
  • XML语句同RowBounds相同,不需要在SQL语句中写limit关键字。
1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码<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对象中直接获取。
1
2
3
4
5
6
7
8
9
10
11
12
java复制代码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));
}
1
2
3
4
5
6
7
8
txt复制代码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配置文件的标签顺序是有讲究的,如果顺序出问题,是会报错的。例如:
1
2
3
4
5
6
7
xml复制代码<!--指定Mapper.xml所在位置-->
<mappers>
<mapper resource="resources/xml/PurchaseMapper.xml"/>
</mappers>
<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
  • 此时,配置文件报错,内容如下:

image.png

  • 运行项目报错,内容大体如下:
1
2
3
4
5
6
7
txt复制代码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,需要在遇到之后进行仔细的排查。而自己手写分页不容易出现问题,但是比较麻烦。具体情况具体分析,根据自身情况挑选适合自己的方式:)

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%