Mybatis分页插件PageHelper的配置和使用方法

Mybatis分页插件PageHelper的配置和使用方法

 

  • MySQL对分页的支持

简单来说MySQL对分页的支持是通过limit子句。请看下面的例子。

 

limit关键字的用法是
LIMIT [offset,] rows
offset是相对于首行的偏移量(首行是0),rows是返回条数。

# 每页10条记录,取第一页,返回的是前10条记录
select * from tableA limit 0,10;
# 每页10条记录,取第二页,返回的是第11条记录,到第20条记录,
select * from tableA limit 10,10;

 

这里提一嘴的是,MySQL在处理分页的时候是这样的:

limit 1000,10 - 过滤出1010条数据,然后丢弃前1000条,保留10条。当偏移量大的时候,性能会有所下降。

limit 100000,10 - 会过滤10w+10条数据,然后丢弃前10w条。如果在分页中发现了性能问题,可以根据这个思路调优。

 

  • Mybatis分页插件PageHelper

在使用Java Spring开发的时候,Mybatis算是对数据库操作的利器了。不过在处理分页的时候,Mybatis并没有什么特别的方法,一般需要自己去写limit子句实现,成本较高。好在有个PageHelper插件。

1、POM依赖

Mybatis的配置就不多提了。PageHelper的依赖如下。需要新的版本可以去maven上自行选择

1

2

3

4

5

     com.github.pagehelper

     pagehelper

     4.1.4

 

 

2、Mybatis对PageHelper的配置

打开Mybatis配置文件,一般在Resource路径下。我这里叫mybatis-config.xml。

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

"1.0" encoding="UTF-8"?>

 

"-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

 

    

    "cacheEnabled" value="true"/>

    

    "lazyLoadingEnabled" value="true"/>

    

    "aggressiveLazyLoading" value="true"/>

    

    "multipleResultSetsEnabled" value="true"/>

    

    "useColumnLabel" value="true"/>

    

    "useGeneratedKeys" value="true"/>

    

    "autoMappingBehavior" value="PARTIAL"/>

    

    "defaultExecutorType" value="SIMPLE"/>

    

    "mapUnderscoreToCamelCase" value="true"/>

    

    "localCacheScope" value="SESSION"/>

    

    "jdbcTypeForNull" value="NULL"/>

 

    "com.github.pagehelper.PageHelper">

        "dialect" value="mysql"/>

        "offsetAsPageNum" value="false"/>

        "rowBoundsWithCount" value="false"/>

        "pageSizeZero" value="true"/>

        "reasonable" value="false"/>

        "supportMethodsArguments" value="false"/>

        "returnPageInfo" value="none"/>

    

  

这里要注意的是PageHelper相关的配置。 

 

如果你没有加载Mybatis配置文件,那么使用的是Mybatis默认的配置。如何加载Mybatis配置文件呢?

到你的dataSrouce配置中。

在配置sqlSessionFactory的时候,指定Mybatis核心配置文件和mapper的路径,代码如下

1

2

3

4

5

6

7

8

9

@Bean(name = "moonlightSqlSessionFactory")

@Primary

public SqlSessionFactory moonlightSqlSessionFactory(@Qualifier("moonlightData") DataSource dataSource) throws Exception {

    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();

    bean.setDataSource(dataSource);

    bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mybatis-mapper/*.xml"));

    bean.setConfigLocation(new ClassPathResource("mybatis-config.xml"));

    return bean.getObject();

} 

说明:

这里配置的mapper.xml存放路径,在Resource/mybatis-mapper文件夹下

这里配置的mybatis-config.xml文件,在Resource/下

 

 

3、分页

准备一个mapper.xml,测试就随便写一个吧,干脆就用工程里的一个。

这里这个查询,是一个典型的多条件查询,我们要做的是对多条件匹配到的记录进行分页。

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

"1.0" encoding="UTF-8"?>

"-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

"com.kangaroo.studio.moonlight.dao.mapper.MoonlightMapper">

  "geoFenceList" type="com.kangaroo.studio.moonlight.dao.model.GeoFence">

    

      "id" javaType="java.lang.Integer" jdbcType="INTEGER" />

      "name" javaType="java.lang.String" jdbcType="VARCHAR" />

      "type" javaType="java.lang.Integer" jdbcType="INTEGER" />

      "group" javaType="java.lang.String" jdbcType="VARCHAR" />

      "geo" javaType="java.lang.String" jdbcType="VARCHAR" />

      "createTime" javaType="java.lang.String" jdbcType="VARCHAR" />

      "updateTime" javaType="java.lang.String" jdbcType="VARCHAR" />

    

  

 

  "base_column">id, name, type, `group`, geo, createTime, updateTime

 

  

  

在Mapper.java接口中编写对应的方法

1

List queryGeoFence(GeoFenceQueryParam geoFenceQueryParam);

  

先上分页代码,后面再说明

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

@RequestMapping(value = "/fence/query", method = RequestMethod.POST)

    @ResponseBody

    public ResponseEntity queryFence(@RequestBody GeoFenceQueryParam geoFenceQueryParam) {

        try {

            Map data = new HashMap<>();

            Integer pageNum = geoFenceQueryParam.getPageNum()!=null?geoFenceQueryParam.getPageNum():1;

            Integer pageSize = geoFenceQueryParam.getPageSize()!=null?geoFenceQueryParam.getPageSize():10;

            Page page = PageHelper.startPage(pageNum, pageSize, true);

            List list = moonlightMapper.queryGeoFence(geoFenceQueryParam);

            data.put("total", page.getTotal());

            data.put("nowPage", pageNum);

            data.put("data", list);

            return new ResponseEntity<>(

                    new Response(ResultCode.SUCCESS, "查询geoFence成功", data),

                    HttpStatus.OK);

        catch (Exception e) {

            logger.error("查询geoFence失败", e);

            return new ResponseEntity<>(

                    new Response(ResultCode.EXCEPTION, "查询geoFence失败"null),

                    HttpStatus.INTERNAL_SERVER_ERROR);

        }

    }

 

 

说明:

1、PageHelper的优点是,分页和Mapper.xml完全解耦。实现方式是以插件的形式,对Mybatis执行的流程进行了强化,添加了总数count和limit查询。属于物理分页。

2、Page page = PageHelper.startPage(pageNum, pageSize, true); - true表示需要统计总数,这样会多进行一次请求select count(0); 省略掉true参数只返回分页数据。 

1)统计总数,(将SQL语句变为 select count(0) from xxx,只对简单SQL语句其效果,复杂SQL语句需要自己写)

    Page page = PageHelper.startPage(1,-1);

    long count = page.getTotal();

2)分页,pageNum - 第N页, pageSize - 每页M条数

    A、只分页不统计(每次只执行分页语句)

    PageHelper.startPage([pageNum],[pageSize]);

    List pagelist = queryForList( xxx.class, "queryAll" , param);

    //pagelist就是分页之后的结果

    B、分页并统计(每次执行2条语句,一条select count语句,一条分页语句)适用于查询分页时数据发生变动,需要将实时的变动信息反映到分页结果上

    Page page = PageHelper.startPage([pageNum],[pageSize],[iscount]);

    List pagelist = queryForList( xxx.class , "queryAll" , param);

    long count = page.getTotal();

    //也可以 List pagelist = page.getList();  获取分页后的结果集

3)使用PageHelper查全部(不分页)

    PageHelper.startPage(1,0);

    List alllist = queryForList( xxx.class , "queryAll" , param);

4)PageHelper的其他API

    String orderBy = PageHelper.getOrderBy();    //获取orderBy语句

    Page page = PageHelper.startPage(Object params);

    Page page = PageHelper.startPage(int pageNum, int pageSize);

    Page page = PageHelper.startPage(int pageNum, int pageSize, boolean isCount);

    Page page = PageHelper.startPage(pageNum, pageSize, orderBy);

    Page page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable);    //isReasonable分页合理化,null时用默认配置

    Page page = PageHelper.startPage(pageNum, pageSize, isCount, isReasonable, isPageSizeZero);    //isPageSizeZero是否支持PageSize为0,true且pageSize=0时返回全部结果,false时分页,null时用默认配置

5)、默认值

    //RowBounds参数offset作为PageNum使用 - 默认不使用

    private boolean offsetAsPageNum = false;

    //RowBounds是否进行count查询 - 默认不查询

    private boolean rowBoundsWithCount = false;

    //当设置为true的时候,如果pagesize设置为0(或RowBounds的limit=0),就不执行分页,返回全部结果

    private boolean pageSizeZero = false;

    //分页合理化

    private boolean reasonable = false;

    //是否支持接口参数来传递分页参数,默认false

    private boolean supportMethodsArguments = false;  

 

3、有一个安全性问题,需要注意一下,不然可能导致分页错乱。我这里直接粘贴了这篇博客里的一段话。

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

4. 什么时候会导致不安全的分页?

 

PageHelper 方法使用了静态的 ThreadLocal 参数,分页参数和线程是绑定的。

 

只要你可以保证在 PageHelper 方法调用后紧跟 MyBatis 查询方法,这就是安全的。因为 PageHelper 在 finally 代码段中自动清除了 ThreadLocal 存储的对象。

 

如果代码在进入 Executor 前发生异常,就会导致线程不可用,这属于人为的 Bug(例如接口方法和 XML 中的不匹配,导致找不到 MappedStatement 时), 这种情况由于线程不可用,也不会导致 ThreadLocal 参数被错误的使用。

 

但是如果你写出下面这样的代码,就是不安全的用法:

 

PageHelper.startPage(110);

List list;

if(param1 != null){

    list = countryMapper.selectIf(param1);

else {

    list = new ArrayList();

}

这种情况下由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页。

 

上面这个代码,应该写成下面这个样子:

 

List list;

if(param1 != null){

    PageHelper.startPage(110);

    list = countryMapper.selectIf(param1);

else {

    list = new ArrayList();

}

这种写法就能保证安全。

 

如果你对此不放心,你可以手动清理 ThreadLocal 存储的分页参数,可以像下面这样使用:

 

List list;

if(param1 != null){

    PageHelper.startPage(110);

    try{

        list = countryMapper.selectAll();

    finally {

        PageHelper.clearPage();

    }

else {

    list = new ArrayList();

}

这么写很不好看,而且没有必要。

你可能感兴趣的:(开发)