目录
一、基于注解的简单分页查询
1.定义对象
2.Mapper接口
3.Controller类
4.功能实现
二、基于注解的较复杂分页查询
1.定义shop实体类和page分页类
2.Mapper接口
3.Controller类
4.功能实现
三、基于mapper.xml的复杂分页查询
1.首先要配置一下xxxMapper.xml文件的地址:
1.定义Page类
2.Mapper接口
3.pageMapper.xml
4.pageService
5.pageService的实现类pageServiceImpl
6.功能实现
7.可扩展性测试
四、基于pagehelper插件的复杂分页查询
1. 导入依赖
2. 添加配置
3.OrderMapper与orderMapper.xml
4. OrderService与OrderServiceImpl
5.OrderController
6.功能实现
7.Mybatis 分页插件 Pagehelper 的 PageInfo 字段属性解释
五、MybtisPlus插件自动分页配置
1、引入依赖
2、MybatisPlusConfig配置文件
3、项目中各层具体写法:
项目中经常遇到需要查询数据列表的功能,一般需要前端去调用后端的相应数据列表的分页接口,所以后端需要实现数据库SQL的分页查询:
注意数据库要有相对应的数据库表
import ...
@Entity
@Data
@Table(name = "shop")
public class Shop {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "name")
private String name;
}
import ...
@Repository
public interface ShopMapper {
//分页查询
@Select("select * from shop limit #{pageBegin}, #{pageSize}")
List findByPage(@Param("pageBegin") Integer pageBegin,@Param("pageSize") Integer pageSize);
import ...
@RestController
@RequestMapping("shop")
public class ShopController {
@Autowired
private ShopMapper shopMapper;
//分页查询
@GetMapping("/allByPage")
public List findByPage(Integer page,Integer pageSize) {
Integer pageBegin = (page-1) * pageSize;
return shopMapper.findByPage(pageBegin,pageSize);
}
请求路径:http://localhost:8080/shop/allByPage?page=3&pageSize=5
请求结果:
[{"id":11,"name":"馒头店"},{"id":12,"name":"包子店"},{"id":13,"name":"豆腐脑油条"},{"id":14,"name":"北京烤鸭"},{"id":15,"name":"煎饼果子"}]
简单的分页查询功能已实现,但是功能需求多的话显然还不够用,比如要向前端展示一下共有多少页,有多少数据条,这些数据是从哪行到哪行等等,就实现不了了,因此对于分页的功能实现远远不止于此,下面就来进阶一下。
逻辑上跟简陋版是一样的,只不过多了一个Page类。这个类里面就包含了上边的Shop类,也就是之前返回的shop类的数组变成了Page类的一个属性值。
Shop类用之前的就可以,下面是Page分页类:
import ...
@Data
public class ShopPage {
//第几页
private Integer pageNum;
//每页数据条数
private Integer pageSize;
//总数据条数
private Integer size;
//起始(末尾)数据
private Integer startRow;
private Integer endRow;
//总页数
private Integer pages;
//上(下)一页页码
private Integer prePage;
private Integer nextPage;
//是否是第(最后)一行
private Boolean isFirstPage;
private Boolean isLastPage;
//有无前(后)页
private Boolean hasPreviousPage;
private Boolean hasNextPage;
private Integer navigatePages;
private List navigatePageNums;
//数据数组
private List data;
private Integer navigateFirstPage;
private Integer navigateLastPage;
//第(最后)一页
private Integer firstPage;
private Integer lastPage;
}
这样就定义好了调用接口时所返回的数据类型,可以看到里面有一个List
import ...
@Repository
public interface ShopPageMapper {
@Select("select * from shop limit #{pageBegin}, #{pageSize}")
List findData(@Param("pageBegin") Integer pageBegin, @Param("pageSize") Integer pageSize);
@Select("select count(*) from shop")
int findSize();
}
这里先简单写一下,之后还得再修改。
import ...
@RestController
@RequestMapping("page")
public class ShopPageController {
@Autowired
private ShopPageMapper shopPageMapper;
@GetMapping("/shop")
public ShopPage page(Integer page, Integer pageSize){
ShopPage shopPage = new ShopPage();
Integer pageBegin = (page-1) * pageSize;
//当前页码
shopPage.setPageNum(page);
//前(后)一页页码
shopPage.setPrePage(page - 1);
shopPage.setNextPage(page + 1 );
//总数据条数
shopPage.setSize(shopPageMapper.findSize());
//开始(末尾)数据是第几行
shopPage.setStartRow(pageBegin + 1);
shopPage.setEndRow(pageBegin + pageSize + 1);
//每页数据条数
shopPage.setPageSize(pageSize);
//总页数
Integer div = shopPage.getSize()/pageSize;
Integer pages = shopPage.getSize() % pageSize == 0 ? div : div + 1;
shopPage.setPages(pages);
//是否是第(最后)一页,是否有前(后)一页
shopPage.setIsFirstPage(page == 1);
shopPage.setHasPreviousPage(page != 1);
shopPage.setIsLastPage(page.equals(pages));
shopPage.setHasNextPage(!page.equals(pages));
//查询到的数据
shopPage.setData(shopPageMapper.findData(pageBegin,pageSize));
return shopPage;
}
}
请求路径:http://localhost:8888/page/shop?page=3&pageSize=5
请求结果:
{ "pageNum":3, "pageSize":5, "size":1000, "startRow":11, "endRow":16, "pages":200, "prePage":2, "nextPage":4, "isFirstPage":false, "isLastPage":false, "hasPreviousPage":true, "hasNextPage":true, "navigatePages":null, "navigatePageNums":null, "data":[{"id":11,"name":"小安汽修", {"id":12,"name":"大壮副食"}, {"id":13,"name":"小牛电动"}, {"id":14,"name":"哈哈笑超市"}, {"id":15,"name":"包子"}], "navigateFirstPage":null, "navigateLastPage":null, "firstPage":null, "lastPage":null }
这样请求的时候只需要传入page(页码)、pageSize(每页数量),就可以将数据和分页信息查询到。
存在的问题:上边的ShopPage、ShopMapper类和ShopPageController类都是跟实体类写死的,也就是跟实体类有很大的耦合性,那这样以后如果要给别的实体类写一个分页查询,还要大量重复这些相近的代码,所以完全可以将pageMapper类和pageController类写成所有实体类都可以使用的,这样再多传入一个动态的参数(也就是实体的名字),就可以实现Page类、PageMapper接口和PageController类的复用,这样就在实现功能的同时降低了代码的耦合性,减少代码。
上面提到了将分页类和实体类都写在一起了,下面就将它们拆开来,将Page、PageMapper、PageController写成能够被所有类使用的分页功能,下面是实现代码:
在com....包下面的config文件夹(没有就新建)中写一个CustomMybatisConfig类来对SqlSession进行配置:
import ...
@Configuration
public class CustomMybatisConfig {
@Autowired
private DataSource dataSource;
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource[] resources = resolver.getResources("classpath:.../*.xml");
sqlSessionFactoryBean.setMapperLocations(resources);
return sqlSessionFactoryBean;
}
}
getResources("classpath:.../*.xml")里面要写.xml文件的路径,这个路径也可以这样指定:在application.properties里面添加如下配置用来指定xml文件的存放位置是一样的:
mybatis.mapper-locations=classpath:/mybatis/mapper/*.xml
import ...
@Data
public class Page {
//第几页
private Integer pageNum;
//每页数据条数
private Integer pageSize;
//总数据条数
private Integer size;
//起始(末尾)数据
private Integer startRow;
private Integer endRow;
//总页数
private Integer pages;
//上(下)一页页码
private Integer prePage;
private Integer nextPage;
//是否是第(最后)一行
private Boolean isFirstPage;
private Boolean isLastPage;
//有无前(后)页
private Boolean hasPreviousPage;
private Boolean hasNextPage;
private Integer navigatePages;
private List navigatePageNums;
//数据数组
private List
import ...
@Mapper
public interface PageMapper {
List
这里推荐一个常用的插件:mybatis-helper,可以实现从xxxMapper到XXXMapper.xml方法的快捷跳转,效果如下:
新建一个xml文件,按照规范,名字应跟Mapper接口一致,然后将SQL写在xml中。
import ...
public interface PageService {
int selectSize(String entity);
Page paging(String entity, Integer pageNumber, Integer pageSize);
List
import ...
@Service
public class PageServiceImpl implements PageService {
@Autowired
private PageMapper pageMapper;
@Override
public int selectSize(String entity) {
return pageMapper.selectSize(entity);
}
@Override
public Page paging(String entity, Integer pageNumber, Integer pageSize) {
Page page = new Page();
Integer pageBegin = (pageNumber - 1) * pageSize;
//当前页码
page.setPageNum(pageNumber);
//前(后)一页页码
page.setPrePage(pageNumber - 1);
page.setNextPage(pageNumber + 1);
//总数据条数
page.setSize(pageMapper.selectSize(entity));
//开始(末尾)数据是第几行
page.setStartRow(pageBegin + 1);
page.setEndRow(pageBegin + pageSize + 1);
//每页数据条数
page.setPageSize(pageSize);
//总页数
Integer div = page.getSize() / pageSize;
Integer pages = page.getSize() % pageSize == 0 ? div : div + 1;
page.setPages(pages);
//是否是第(最后)一页,是否有前(后)一页
page.setIsFirstPage(pageNumber == 1);
page.setHasPreviousPage(pageNumber != 1);
page.setIsLastPage(pageNumber.equals(pages));
page.setHasNextPage(!pageNumber.equals(pages));
//查询到的数据
page.setTableData(pageMapper.selectTableData(entity, pageBegin, pageSize));
return page;
}
@Override
public List
请求路径:http://localhost:8888/page/shop?pageNumber=2&pageSize=10
请求结果:
{"pageNum":2, "pageSize":10, "size":1000, "startRow":11, "endRow":21, "pages":100, "prePage":1, "nextPage":3, "isFirstPage":false, "isLastPage":false, "hasPreviousPage":true, "hasNextPage":true, "navigatePages":null, "navigatePageNums":null, "data":[{"id":11,"name":"豆浆油条"}, {"id":12,"name":"子文包子"}, {"id":13,"name":"黄河馒头"}, {"id":14,"name":"长江小笼包"}, {"id":15,"name":"上海画图图图"}, {"id":16,"name":"小型制造"}, {"id":17,"name":"小花"}, {"id":18,"name":"小明"}, {"id":19,"name":"小刚"}, {"id":20,"name":"小吃"}], "navigateFirstPage":null, "navigateLastPage":null, "firstPage":null, "lastPage":null}
我们重新写一个类,并且在数据库重新建一个表:
import ...
@Entity
@Data
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Integer id;
@Column(name = "title")
private String title;
@Column(name = "description")
private String description;
@Column(name = "shopId")
private Integer shopId;
@Column(name = "price")
private String price;
}
在xml中添加sql语句,如下:
这个xml文件跟其它的mybatis配置文件一样,namespace可不写。这里baseResultMap没有看到定义,但是确实存在,因为这个是easymybatis提供的一个内置resultMap。
resultMap="BaseResultMap"
如果配置了resultMap,返回值统一使用 resultMap=“BaseResultMap”,mybatis会根据查询到的条目数量自动进行判断,如果是一条就返回对象,如果是多条就返回List对象列表。
在TUseroDao.java中添加:
TUser selectByName(@Param("username")String username);
如果有问题,解决方法:相应的model对应的mapping中将resultType改成resultMap即可。
扩展:还有一种方式是easymybatis,easymybatis提供的一些查询方式已经满足大部分的查询需求,但是有些复杂的sql语句还是需要写在xml文件中。easymybatis同样支持将sql语句写在xml中,具体配置如下:
上边的例子是自己封装了的page类和page方法,在增加实体类的同时不再需要写page方法,只要数据库里有相关的表名,就可以直接在路径上改变实体名来查找相应类的分页信息,减少了分页方法与其他方法的耦合,这样每一个类的分页使用的都是同一个Mapper。但是这样的弊端也很明显,自定义程度不高,也就是说如果这些要分页的信息如果都没有where条件,或者where条件相同的话,使用这种自封装的方法是没有问题的,但是如果每一个类的分页信息要求的where条件有很大区别,比如说Person类有按照年龄显示分页的where条件,price类有按照价格显示分页的where条件,那么这种封装的方法想要实现这种不同类的不同where条件的分页时就显得可有可无,因为每加一个这样的where条件,就要在Mapper中写一个这样的SQL语句,这样其实更加大了项目开发成本。更加复杂的分页场景就要考虑使用pagehelper分页插件。
pom.xml添加依赖:
com.github.pagehelper
pagehelper-spring-boot-starter
1.2.3
在aplication.yml或者application.properties添加配置:
#分页pageHelper
pagehelper:
helper-dialect: mysql
reasonable: true
support-methods-arguments: true
·helper-dialect:配置使用哪种数据库语言,不配置的话pageHelper也会自动检测
·reasonable:配置分页参数合理化功能,默认是false。
#启用合理化时,如果pageNum<1会查询第一页,如果pageNum>总页数会查询最后一页;
#禁用合理化时,如果pageNum<1或pageNum>总页数会返回空数据。
·params:为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值; 可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值, 默认值为pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero。
·support-methods-arguments:支持通过Mapper接口参数来传递分页参数,默认值false,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。
import ...;
@Mapper
public interface OrderMapper {
//查找所有
List selectAll();
}
import ...
public interface OrderService {
List selectAll();
//查找所有按照分页展示
List findAllUserByPageF(int pageNum,int pageSize);
//带分页信息查找所有按照分页展示
PageInfo findAllUserByPageS(int pageNum, int pageSize);
}
import ...
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private OrderMapper orderMapper;
@Override
public List selectAll(){return orderMapper.selectAll();}
@Override
public List findAllUserByPageF(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
return orderMapper.selectAll();
}
@Override
public PageInfo findAllUserByPageS(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List lists = orderMapper.selectAll();
return new PageInfo(lists);
}
}
import ...
@RestController
@RequestMapping("order")
public class OrderController {
@Resource
private OrderService orderService;
@GetMapping("/all")
public List selectAll(){
return this.orderService.selectAll();
}
@GetMapping("/page")
public List selectAllByPage(int pageNum,int pageSize){
return orderService.findAllUserByPageF(pageNum,pageSize);
}
@GetMapping("/pageandinfo")
public PageInfo testPageHelper1(int pageNum,int pageSize){
return orderService.findAllUserByPageS(pageNum,pageSize);
}
}
请求路径:http://localhost:8888/order/page?pageNum=3&pageSize=6
请求结果:
{[{"id":13,"name":"豆浆油条"}, {"id":14,"name":"子文包子"}, {"id":15,"name":"黄河馒头"}, {"id":16,"name":"长江小笼包"}, {"id":17,"name":"上海画图图图"}, {"id":18,"name":"小型制造"}]
请求路径:http://localhost:8888/order/page?pageNum=3&pageSize=6http://localhost:8888/order/pageandinfo?pageNum=2&pageSize=3
请求结果:
{"pageNum":2, "pageSize":3, "size":3, "startRow":4, "endRow":6, "total":6, "pages":2, "list":[{"id":13,"name":"豆浆油条"}, {"id":14,"name":"子文包子"}, {"id":15,"name":"黄河馒头"},], "prePage":1, "nextPage":0, "isFirstPage":false, "isLastPage":true, "hasPreviousPage":true, "hasNextPage":false, "navigatePages":8, "navigatepageNums":[1,2], "navigateFirstPage":1, "navigateLastPage":2, "firstPage":1, "lastPage":2}
pageNum=1 | 当前页码 |
pageSize=1 | 每页个数 |
size=1 | 当前页个数 |
startRow=1 | 由第几条开始 |
endRow=1 | 到第几条结束 |
total=3 | 总条数 |
pages=3 | 总页数 |
list= XXXX | 查出来的数据集合 |
prePage=0 | 上一页 |
nextPage=2 | 下一页 |
isFirstPage=true | 是否为首页 |
isLastPage=false | 是否为尾页 |
hasPreviousPage=false | 是否有上一页 |
hasNextPage=true | 是否有下一页 |
navigatePages=8 | 每页显示的页码个数 |
navigateFirstPage=1 | 首页 |
navigateLastPage=3 | 尾页 |
navigatepageNums=[1, 2, 3]} | 页码数 |
com.baomidou
mybatis-plus-boot-starter
3.3.1.tmp
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
Mapper.xml中的sql正常写即可:
xxxMapper中传入page,注意这里的Page的包是mybatisplus的依赖包里面的:
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
List GetAllByPage(Page page);
xxxService和xxxServiceImpl中传入page:
Page getAll(Page page);
@Override
public Page getAll(Page page) {
return xxxMapper.getAll(page);
}
xxxController中传入page和VO:
public Page getAll(int pageNum,int pageSize) {
Page page = new Page<>(pageNum,pageSize);
return xxxService.getAll(page);
}