MyBatis实现分页查询

目录

一、基于注解的简单分页查询

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 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 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 data;
    private Integer navigateFirstPage;
    private Integer navigateLastPage;
    //第(最后)一页
    private Integer firstPage;
    private Integer lastPage;
}

这样就定义好了调用接口时所返回的数据类型,可以看到里面有一个List,这就是我们查到的数据,和主要数据一起传给前端的还有各种各样的页码和条数数据,总之就是几乎将这些数据条的所有信息都传给前端。

2.Mapper接口

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();
}

这里先简单写一下,之后还得再修改。

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> tableData;
    private Integer navigateFirstPage;
    private Integer navigateLastPage;
    //第(最后)一页
    private Integer firstPage;
    private Integer lastPage;

}

2.Mapper接口

import ...

@Mapper
public interface PageMapper {

    List>  selectTableData(String entity, Integer pageBegin, Integer pageSize);

    int selectSize(String entity);
}

这里推荐一个常用的插件:mybatis-helper,可以实现从xxxMapper到XXXMapper.xml方法的快捷跳转,效果如下:

MyBatis实现分页查询_第1张图片MyBatis实现分页查询_第2张图片

3.pageMapper.xml

新建一个xml文件,按照规范,名字应跟Mapper接口一致,然后将SQL写在xml中。





   

    

    

4.pageService

import ...

public interface PageService {

    int selectSize(String entity);

    Page paging(String entity, Integer pageNumber, Integer pageSize);

    List> 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> 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文件跟其它的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分页插件。

四、基于pagehelper插件的复杂分页查询

1. 导入依赖

pom.xml添加依赖:


    com.github.pagehelper
    pagehelper-spring-boot-starter
    1.2.3

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 selectAll();
}

 4. OrderService与OrderServiceImpl

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);
    }

}

5.OrderController

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);
    }
}

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、引入依赖


	com.baomidou
	mybatis-plus-boot-starter
	3.3.1.tmp

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正常写即可:

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);
    }

你可能感兴趣的:(数据库,MySQL,mybatis,数据库,sql,mysql,java,mybatis)