mybatis中pageHelper分页失效原因分析及处理方案

mybatis中pageHelper分页失效原因分析及处理方案,第1张

1.问题描述以及业务代码展示
2.源码角度分析分页失效原因分析以及处理方案

1.问题描述以及业务代码展示
    查询资讯列表信息中需要支持分页查询,使用的是pagehelper进行查询。执行查询资讯列表信息之前需要进行查询点赞信息。按照指定页数和每页显示条数查询出来的资讯列表信息不准确,结果都是查询出来所有资讯列表信息。下面贴一下业务的伪代码
需要的依赖:

<dependency>
		<groupId>com.github.pagehelper</groupId>
		<artifactId>pagehelper-spring-boot-starter</artifactId>
		<version>1.2.3</version>
		<exclusions>
			<exclusion>
				<groupId>org.mybatis</groupId>
				<artifactId>mybatis</artifactId>
			</exclusion>
			<exclusion>
				<groupId>org.mybatis</groupId>
				<artifactId>mybatis-spring</artifactId>
			</exclusion>
		</exclusions>
	</dependency>

TestPageHelper .java

@RestController
@RequestMapping("/testPageHelper")
public class TestPageHelper {

    @Autowired
    private PageHelperServiceImpl pageHelperService;

    @GetMapping
    public ResultVo<PageInfo<News>> findNews(Integer currentPage,Integer pageSize){
        PageHelper.startPage(currentPage,pageSize);
        List<News> newsList = pageHelperService.findNews();
        PageInfo<News> newsPageInfo = new PageInfo<News>(newsList);
        newsPageInfo.setList(newsList);
        return ResultVoUtil.success(newsPageInfo);
    }
}

PageHelperServiceImpl.java

@Service
public class PageHelperServiceImpl {
    @Autowired
    private NewsMapper newsMapper;
    
    // 查询资讯信息
    public List<News> findNews(){

        // 查询点赞信息
        List<FindLike> like = newsMapper.findLike();
        
        // 查询资讯信息
        List<News> news = newsMapper.findNews();

        return news;
    }
}

    mapper查询逻辑不进行展示,仅用作说明问题过程。
    实际测试发现:当前页为1,每页显示2条,查询的点赞列表信息分页正常,查询出资讯列表信息为6条(总条数);当前页为2,每页显示2条,查询的点赞列表信息分页正常,查询出资讯列表信息也是6条(总条数);分页失效!!

2.源码角度分析分页失效原因分析以及处理方案
    pageHelper关键组件中有一个拦截器 PageInterceptor,每一个涉及数据库查询的mapper方法都会被该拦截器进行拦截,里面执行的主要逻辑是查询总数处理和执行分页处理。
关于执行总条数处理,细心的同学应该在控制台看过打印的业务sql前面一般都会有一个select count(0) *** 作,如下图:

这个源码中都是可以进行设置的,继续往下看。pageHelper最核心的作用是执行分页,底层实现的逻辑其实就是在业务sql后面根据当前页以及每页显示的条数拼接limit。下面直接看源码。
     PageInterceptor.java中拦截处理逻辑:

 @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
           // 省略部分代码
            //调用方法判断是否需要进行分页,如果不需要,直接返回结果,最终是判断是否存在ThreadLocal,继续往下看
            if (!dialect.skip(ms, parameter, rowBounds)) {
              // 省略部分代码
                //判断是否需要进行 count 查询,就是决定控制台是否显示select count(0)
                if (dialect.beforeCount(ms, parameter, rowBounds)) {
                    // 处理总条数的逻辑省略
                    }
                  
                    //返回 true 时继续分页查询,false 时直接返回
                    if (!dialect.afterCount(count, parameter, rowBounds)) {
                        //当查询总数为 0 时,直接返回空的结果
                        return dialect.afterPage(new ArrayList(), parameter, rowBounds);
                    }
                }
                //判断是否需要进行分页查询
                if (dialect.beforePage(ms, parameter, rowBounds)) {
                    //生成分页的缓存 key
                    CacheKey pageKey = cacheKey;
                    //处理参数对象
                    parameter = dialect.processParameterObject(ms, parameter, boundSql, pageKey);
                    //调用方言获取分页 sql
                    String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, pageKey);
                    // 组装BoundSql,实际就是在业务代码后面拼接limit ?,?
                    BoundSql pageBoundSql = new BoundSql(configuration, pageSql, boundSql.getParameterMappings(), parameter);
                    //执行分页查询
                    resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);
                } else {
                    //不执行分页的情况下,也不执行内存分页
                    resultList = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
                }
            } else {
                //rowBounds用参数值,不使用分页插件处理时,仍然支持默认的内存分页
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }
            return dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
        	// 最终执行的是清空ThreadLocal *** 作,LOCAL_PAGE.remove()
            dialect.afterAll();
        }
    }

    一般在业务代码中都会写PageHelper.startPage(currentPage,pageSize),它的主要作用是设置ThreadLocal信息,其底层执行源码如下(setLocalPagef方法):

public abstract class PageMethod {
    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();

    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }
 public static <T> Page<T> getLocalPage() {
        return LOCAL_PAGE.get();
    }
public static void clearPage() {
        LOCAL_PAGE.remove();
    }
    // 省略部分代码
}

    PageInterceptor.java中判断是否支持分页主要是根据能否存在ThreadLocal,如果没有则不进行分页 *** 作。上文已经说过,每个mapper查询都会经过拦截器处理,拦截器处理的最后一步是dialect.afterAll(),最终执行的是LOCAL_PAGE.remove(),即移除本地变量。这也就是为什么案例中执行第一个mapper查询会按照指定页数和每页显示条数查询出对应分页数据(因为存在ThreadLocal),而第二个mapper查询的是所有(因为第一个mapper查询完成之后会将ThreadLocal进行清除)。清楚原因之后如何处理就简单了。如果同一个方法中多个mapper都需要支持分页 *** 作,那都保证每个mapper前面都进行ThreadLocal初始化赋值 *** 作。修改后代码如下:
TestPageHelper.java:

@RestController
@RequestMapping("/testPageHelper")
public class TestPageHelper {

    @Autowired
    private PageHelperServiceImpl pageHelperService;

    @GetMapping
    public ResultVo<PageInfo<News>> findNews(Integer currentPage,Integer pageSize){
        List<News> newsList = pageHelperService.findNews(currentPage,pageSize);
        PageInfo<News> newsPageInfo = new PageInfo<News>(newsList);
        newsPageInfo.setList(newsList);
        return ResultVoUtil.success(newsPageInfo);
    }
}

PageHelperServiceImpl.java:

@Service
public class PageHelperServiceImpl {
    @Autowired
    private NewsMapper newsMapper;

    // 查询资讯信息
    public List<News> findNews(Integer currentPage,Integer pageSize){
		
		
        PageHelper.startPage(currentPage,pageSize);
        // 查询点赞信息
        List<FindLike> like = newsMapper.findLike();

        PageHelper.startPage(currentPage,pageSize);
        // 查询资讯列表
        List<News> news = newsMapper.findNews();
        return news;
    }
}

    总结:对指定mapper查询支持分页,前面一定要有PageHelper.startPage(currentPage,pageSize),不能有其他mapper查询,否则会失效!
    欢迎评论区点赞留言,相互交流,共同进步!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/726395.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存