Java Web 开发:项目通用技能
分页插件PageHelper
我们在使用MyBatis开发过程中会遇到查询过程需要使用分页的场景
比如
/admin/user/list?page=1&limit=20&sort=add_time&order=desc
/admin/goods/list?page=3&limit=20&sort=add_time&order=desc
上面的两个请求中都提供了两个参数,page和limit,MySQL中可以使用limit语句来执行分页相关的查询,这时候需要我们来做一件事就是来计算offset
offset = (page-1)*limit
这时候我们查询goods中的记录的SQL就变为了
SELECT * FROM `market_goods` limit 40,20 -- 其中40为偏移量这时候其实我们可以使用分页插件来帮我们做分页
配置分页插件
首先引入依赖
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>5.3.2</version></dependency>然后在MyBatis的mybatis-config.xml配置文件中增加配置
<plugins> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!--方言:mysql--> <property name="helperDialect" value="mysql"/> <!--页码的合理性修正--> <property name="reasonable" value="true"/> </plugin></plugins>使用分页
分页插件的原理是在构建预编译的SQL语句过程中帮我们拼接分页相关的语句,并且给对应的占位符提供值,比如我们定义Mapper接口和映射文件中的标签如下
public interface MarketGoodsMapper { List<MarketGoods> selectAll();}<sql id="Base_Column_List"> <!--@mbg.generated--> id, goods_sn, `name`, category_id, brand_id, gallery, keywords, brief, is_on_sale, sort_order, pic_url, share_url, is_new, is_hot, unit, counter_price, retail_price, detail, add_time, update_time, deleted</sql>
<select id="selectAll" resultMap="BaseResultMap"> select <include refid="Base_Column_List" /> from market_goods</select>这时候我们去执行查询,做的是查询数据库market_goods表中的所有记录
@Testpublic void test1() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); List<MarketGoods> marketGoods = goodsMapper.selectAll();}对应的控制台的预编译SQL语句以及提供的参数如下
==> Preparing: select id, goods_sn, `name`, category_id, brand_id, gallery, keywords, brief, is_on_sale, sort_order, pic_url, share_url, is_new, is_hot, unit, counter_price, retail_price, detail, add_time, update_time, deleted from market_goods==> Parameters:这里呢并没有使用分页
接下来我们来使用一下分页,在执行查询之前开启分页,传入页码和对应的限制量
PageHelper.startPage(page,limit);
对应代码如下
@Testpublic void test2() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); PageHelper.startPage(3, 20); List<MarketGoods> marketGoods = goodsMapper.selectAll();}相较于前面的单元测试,增加了PageHelper.startPage(3, 20);
控制台日志输出的预编译的SQL和给预编译的SQL语句提供的参数如下
==> Preparing: select id, goods_sn, `name`, category_id, brand_id, gallery, keywords, brief, is_on_sale, sort_order, pic_url, share_url, is_new, is_hot, unit, counter_price, retail_price, detail, add_time, update_time, deleted from market_goods LIMIT ?, ?==> Parameters: 40(Long), 20(Integer)相较于使用之前增加了LIMIT ?, ?
分别提供的参数40(Long), 20(Integer)
也就是帮我们拼接了分页的语句,并且帮我们添加了offset和limit
进一步获得分页信息
我们前面在分页的时候还获得了这样的一些信息
@Datapublic class PageData { private Integer limit; // 数据量的限制 private List<?> list; // 数据列表 private Integer page; // 当前页的页码 private Integer pages; // 总的页码 private Integer total; // 总的数据量}通过分页的话也可以获得这样的一些信息
将查询的原始结果放入到PageInfo的构造方法中,可以获得一个PageInfo对象,里面封装对应的分页信息
PageInfo pageInfo = new PageInfo<>(marketGoods);
对应的代码如下
@Testpublic void test3() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); PageHelper.startPage(3, 20); List<MarketGoods> marketGoods = goodsMapper.selectAll();
PageInfo pageInfo = new PageInfo<>(marketGoods);}对应的PageInfo信息如下

也就是我们所需要的信息在PageInfo中都有,那么我们就可以做一个封装,将PageInfo中的信息封装给PageData
@Testpublic void test4() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); PageHelper.startPage(3, 20); List<MarketGoods> marketGoods = goodsMapper.selectAll();
PageInfo pageInfo = new PageInfo<>(marketGoods); PageData pageData = new PageData(); pageData.setList(pageInfo.getList()); pageData.setLimit(pageInfo.getPageSize()); pageData.setPage(pageInfo.getPageNum()); pageData.setPages(pageInfo.getPages()); pageData.setTotal((int) pageInfo.getTotal());}最后呢还可以在PageData中额外增加方法做PageInfo中的信息的封装
@Datapublic class PageData { private Integer limit; // 数据量的限制 private List<?> list; // 数据列表 private Integer page; // 当前页的页码 private Integer pages; // 总的页码 private Integer total; // 总的数据量
public static PageData list(List list) { PageInfo pageInfo = new PageInfo<>(list); PageData pageData = new PageData(); pageData.setList(pageInfo.getList()); pageData.setLimit(pageInfo.getPageSize()); pageData.setPage(pageInfo.getPageNum()); pageData.setPages(pageInfo.getPages()); pageData.setTotal((int) pageInfo.getTotal()); return pageData; }}如果想要获得PageData,这样子调用就可以了
@Testpublic void test5() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); PageHelper.startPage(3, 20); List<MarketGoods> marketGoods = goodsMapper.selectAll();
PageData pageData = PageData.list(marketGoods);}Example
Example类是MyBatisGenerator(逆向工程)生成的类,并且可以帮我们在Mapper接口中提供对应的方法,可以帮我们构造条件
生成的example中包含这几个成员变量
public class MarketGoodsExample { protected String orderByClause; // 用来做排序的
protected boolean distinct; // 用来做去重的
protected List<Criteria> oredCriteria; // 用来拼接条件的}Mapper接口中有selectByExample方法会传入example对象,可以帮我们做查询,并且可以使用distinct去重和排序
List<MarketGoods> selectByExample(MarketGoodsExample example);对应的映射文件如下
<select id="selectByExample" parameterType="com.cskaoyan.model.MarketGoodsExample" resultMap="BaseResultMap"> <!--@mbg.generated--> select <if test="distinct"> distinct </if> <include refid="Base_Column_List" /> from market_goods <if test="_parameter != null"> <include refid="Example_Where_Clause" /> </if> <if test="orderByClause != null"> order by ${orderByClause} </if></select>distinct和orderByClause
我们先来使用一下distinct和orderByClause,先不构造这两个值来看一下预编译的SQL语句
@Testpublic void test1() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); // 传入null或new MarketGoodsExample(); List<MarketGoods> marketGoods = goodsMapper.selectByExample(null);
sqlSession.close();}==> Preparing: select id, goods_sn, `name`, category_id, brand_id, gallery, keywords, brief, is_on_sale, sort_order, pic_url, share_url, is_new, is_hot, unit, counter_price, retail_price, detail, add_time, update_time, deleted from market_goods接着呢使用一下distinct和orderByClause
@Testpublic void test2() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); MarketGoodsExample example = new MarketGoodsExample(); // 构造example的成员变量distinct和orderByClause example.setDistinct(true); example.setOrderByClause("add_time desc"); List<MarketGoods> marketGoods = goodsMapper.selectByExample(example);
sqlSession.close();}==> Preparing: select distinct id, goods_sn, `name`, category_id, brand_id, gallery, keywords, brief, is_on_sale, sort_order, pic_url, share_url, is_new, is_hot, unit, counter_price, retail_price, detail, add_time, update_time, deleted from market_goods order by add_time desc在select和id之间添加了distinct
在最后添加了order by add_time desc
条件
Example中的另一个成员变量
List<Criteria> oredCriteria是用来帮我们拼接条件的,如果需要添加条件则向这个list中添加值
这里是一个Criteria的List
其中Criteria的定义如下,其中的Criteria又是Criterion的List
public static class Criteria extends GeneratedCriteria {
protected Criteria() { super(); }}protected abstract static class GeneratedCriteria { protected List<Criterion> criteria;
protected GeneratedCriteria() { super(); criteria = new ArrayList<>(); }}这里的Criterion其实是单个条件
我们画一下Example组成图如下

比如我们想要构造一个这样的条件
where goods_sn = '1006013' and name like '%羊毛%' and category_id = 1008009这里其实我们使用其中的一个Criteria就可以了,其中的Criteria就是一个List<Criterion>
goods_sn = ‘1006013’
name like ‘%羊毛%’
category_id = 1008009
这里分别都是一个Criterion
我们绝大多数情况构造的也是一个Criteria就可以了,其中的Criteria就是一个List<Criterion>
我们实现的代码如下
@Testpublic void test3() { SqlSession sqlSession = MyBatisUtil.getSqlSession(); MarketGoodsMapper goodsMapper = sqlSession.getMapper(MarketGoodsMapper.class); MarketGoodsExample example = new MarketGoodsExample(); // 构造example的成员变量distinct和orderByClause example.setDistinct(true); example.setOrderByClause("add_time desc"); // 构造一个Criteria也就是一个List<Criterion> MarketGoodsExample.Criteria criteria = example.createCriteria(); // 构造一个Criterion criteria.andGoodsSnEqualTo("1006013"); // 构造一个Criterion criteria.andNameLike("%羊毛%"); // 构造一个Criterion criteria.andCategoryIdEqualTo(1008009);
List<MarketGoods> marketGoods = goodsMapper.selectByExample(example);
sqlSession.close();}对应的Example如下

对应的预编译SQL语句会在这里添加条件
==> Preparing: select distinct id, goods_sn, `name`, category_id, brand_id, gallery, keywords, brief, is_on_sale, sort_order, pic_url, share_url, is_new, is_hot, unit, counter_price, retail_price, detail, add_time, update_time, deleted from market_goods WHERE ( goods_sn = ? and `name` like ? and category_id = ? ) order by add_time descWHERE ( goods_sn = ? and name like ? and category_id = ? ) 这部分内容是我们的这几行代码提供的
// 构造一个Criteria也就是一个List<Criterion>MarketGoodsExample.Criteria criteria = example.createCriteria();// 构造一个Criterioncriteria.andGoodsSnEqualTo("1006013");// 构造一个Criterioncriteria.andNameLike("%羊毛%");// 构造一个Criterioncriteria.andCategoryIdEqualTo(1008009);我们在后面使用过程中添加对应的条件使用
criteria.and-字段名称-比较逻辑运算符(对应的值)
基本上可以构造出所有的基于单表的条件
其他
另外创建Criteria也可以使用example的or方法来获取
MarketGoodsExample.Criteria criteria = example.or();如果要清空example中的内容可以使用其clear方法
example.clear();注意
example不要全局共享(线程不安全),每次创建一个新的example对象
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!