目录
一、基于注解的简单分页查询
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的分页查询:
一、基于注解的简单分页查询
1.定义对象
注意数据库要有相对应的数据库表
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;
}
2.Mapper接口
import ...
@Repository
public interface ShopMapper {
//分页查询
@Select("select * from shop limit #{pageBegin}, #{pageSize}")
List<Shop> findByPage(@Param("pageBegin") Integer pageBegin,@Param("pageSize") Integer pageSize);
3.Controller类
import ...
@RestController
@RequestMapping("shop")
public class ShopController {
@Autowired
private ShopMapper shopMapper;
//分页查询
@GetMapping("/allByPage")
public List<Shop> findByPage(Integer page,Integer pageSize) {
Integer pageBegin = (page-1) * pageSize;
return shopMapper.findByPage(pageBegin,pageSize);
}
4.功能实现
请求路径: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类的一个属性值。
1.定义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<Shop> data;
private Integer navigateFirstPage;
private Integer navigateLastPage;
//第(最后)一页
private Integer firstPage;
private Integer lastPage;
}
这样就定义好了调用接口时所返回的数据类型,可以看到里面有一个List<Shop>,这就是我们查到的数据,和主要数据一起传给前端的还有各种各样的页码和条数数据,总之就是几乎将这些数据条的所有信息都传给前端。
2.Mapper接口
import ...
@Repository
public interface ShopPageMapper {
@Select("select * from shop limit #{pageBegin}, #{pageSize}")
List<Shop> findData(@Param("pageBegin") Integer pageBegin, @Param("pageSize") Integer pageSize);
@Select("select count(*) from shop")
int findSize();
}
这里先简单写一下,之后还得再修改。
3.Controller类
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;
}
}
4.功能实现
请求路径: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类的复用,这样就在实现功能的同时降低了代码的耦合性,减少代码。
三、基于mapper.xml的复杂分页查询
上面提到了将分页类和实体类都写在一起了,下面就将它们拆开来,将Page、PageMapper、PageController写成能够被所有类使用的分页功能,下面是实现代码:
1.首先要配置一下xxxMapper.xml文件的地址:
在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
1.定义Page类
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<Map<String,Object>> tableData;
private Integer navigateFirstPage;
private Integer navigateLastPage;
//第(最后)一页
private Integer firstPage;
private Integer lastPage;
}
2.Mapper接口
import ...
@Mapper
public interface PageMapper {
List<Map<String,Object>> selectTableData(String entity, Integer pageBegin, Integer pageSize);
int selectSize(String entity);
}
这里推荐一个常用的插件:mybatis-helper,可以实现从xxxMapper到XXXMapper.xml方法的快捷跳转,效果如下:
3.pageMapper.xml
新建一个xml文件,按照规范,名字应跟Mapper接口一致,然后将SQL写在xml中。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.....PageMapper"> <!--namespace就是mapper文件的路径,文件名必须跟Dao类名字一致,因为是根据文件名做关联-->
<select id="selectSize" statementType="STATEMENT" resultType="int" >
select count(*) from
${entity}
</select>
<select id="selectTableData" resultType="map" >
select * from
${entity}
limit
#{pageBegin}, #{pageSize}
</select>
</mapper>
4.pageService
import ...
public interface PageService {
int selectSize(String entity);
Page paging(String entity, Integer pageNumber, Integer pageSize);
List<Map<String,Object>> selectTableData(String entity, Integer pageBegin, Integer pageSize);
}
5.pageService的实现类pageServiceImpl
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<Map<String,Object>> selectTableData(String entity, Integer pageBegin, Integer pageSize) {
pageBegin = pageBegin - 1;
return pageMapper.selectTableData(entity,pageBegin,pageSize);
}
}
6.功能实现
请求路径: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}
7.可扩展性测试
我们重新写一个类,并且在数据库重新建一个表:
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 version="1.0" encoding="utf-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 注意:文件名必须跟Dao类名字一致,因为是根据文件名做关联。 -->
<mapper>
<select id="selectByName" parameterType="String" resultMap="baseResultMap">
select * from t_user t where t.username = #{username} limit 1
</select>
</mapper>
这个xml文件跟其它的mybatis配置文件一样,namespace可不写。这里baseResultMap没有看到定义,但是确实存在,因为这个是easymybatis提供的一个内置resultMap。
<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分页插件。
四、基于pagehelper插件的复杂分页查询
1. 导入依赖
pom.xml添加依赖:
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
2. 添加配置
在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 配置的字段中取值,查找到合适的值时就会自动分页。
3.OrderMapper与orderMapper.xml
import ...;
@Mapper
public interface OrderMapper {
//查找所有
List<Order> selectAll();
}
<select id="selectAll" resultType=".../Order">
select * from order_table
</select>
4. OrderService与OrderServiceImpl
import ...
public interface OrderService {
List<Order> selectAll();
//查找所有按照分页展示
List<Order> findAllUserByPageF(int pageNum,int pageSize);
//带分页信息查找所有按照分页展示
PageInfo<Order> findAllUserByPageS(int pageNum, int pageSize);
}
import ...
@Service
public class OrderServiceImpl implements OrderService {
@Resource
private OrderMapper orderMapper;
@Override
public List<Order> selectAll(){return orderMapper.selectAll();}
@Override
public List<Order> findAllUserByPageF(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
return orderMapper.selectAll();
}
@Override
public PageInfo<Order> findAllUserByPageS(int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Order> lists = orderMapper.selectAll();
return new PageInfo<Order>(lists);
}
}
5.OrderController
import ...
@RestController
@RequestMapping("order")
public class OrderController {
@Resource
private OrderService orderService;
@GetMapping("/all")
public List<Order> selectAll(){
return this.orderService.selectAll();
}
@GetMapping("/page")
public List<Order> selectAllByPage(int pageNum,int pageSize){
return orderService.findAllUserByPageF(pageNum,pageSize);
}
@GetMapping("/pageandinfo")
public PageInfo<Order> testPageHelper1(int pageNum,int pageSize){
return orderService.findAllUserByPageS(pageNum,pageSize);
}
}
6.功能实现
请求路径: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}
7.Mybatis 分页插件 Pagehelper 的 PageInfo 字段属性解释
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]} | 页码数 |
五、MybtisPlus插件自动分页配置
1、引入依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1.tmp</version>
</dependency>
2、MybatisPlusConfig配置文件
@EnableTransactionManagement
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
3、项目中各层具体写法:
Mapper.xml中的sql正常写即可:
<select id="getAll" resultMap="BaseResultMap" >
select * from staff_t
</select>
xxxMapper中传入page,注意这里的Page的包是mybatisplus的依赖包里面的:
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
List<StaffPO> GetAllByPage(Page page);
xxxService和xxxServiceImpl中传入page:
Page<StaffPO> getAll(Page page);
@Override
public Page<staffPO> getAll(Page page) {
return xxxMapper.getAll(page);
}
xxxController中传入page和VO:
public Page<staffPO> getAll(int pageNum,int pageSize) {
Page<staffVO> page = new Page<>(pageNum,pageSize);
return xxxService.getAll(page);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)