mybatis - pagehelper

在开发过程中, 在获取列表的时候, 很多时候, 并不是一把拉出来展示, 更多的时候, 是以分页列表展示. 这时候, 就需要集成一个分页插件了: pagehelper

<dependency>
     <groupId>org.mybatis.spring.bootgroupId>
     <artifactId>mybatis-spring-boot-starterartifactId>
     <version>1.3.2version>
dependency>

application.yml配置:

pagehelper:
    helperDialect: mysql
    #分页合理化, 针对不合理的分页自动处理
    resonable: true

加入一个 UserService:

@Service
public class UserService {
    @Autowired
    UserMapper userMapper;

    public PageInfo getPageList(){
        PageHelper.startPage(1, 10);
        List list = userMapper.getList();
        System.out.println(JSON.toJSONString(list));
        PageInfo pageList = new PageInfo<>(list);
        return pageList;
    }
}

UserMapper.java 中加入一个方法:

List getList();

UserMapper.xml 加入一个配置

    <select id="getList" resultType="com.study.demo.mybatis.vo.User">
        select *  from user
    select>

 

从例子上看, getList 并不是一个分页方法. 那么他又是如何分页呢? getList 即可以分页, 又可以不分页. 看起来神奇, 其实道理也很简单. 

假如在 getList() 方法中, 定义一个分页变量 doPage = false, 那么默认情况下, 他就是不分页的. 然后通过调用方法, 将 doPage 改成 true, 那么就在 sql 后面加上 " limit n, m " 语句, 完成分页.

事实上, pagehelper 确实是这么干的, 用的也是这套原理. 

 

1. PageHelperAutoConfiguration

@Configuration
@ConditionalOnBean({SqlSessionFactory.class})
@EnableConfigurationProperties({PageHelperProperties.class})
@AutoConfigureAfter({MybatisAutoConfiguration.class})
public class PageHelperAutoConfiguration {
    @Autowired
    private List sqlSessionFactoryList;
    @Autowired
    private PageHelperProperties properties;

    public PageHelperAutoConfiguration() {
    }

    @Bean
    @ConfigurationProperties(
        prefix = "pagehelper"
    )
    public Properties pageHelperProperties() {
        return new Properties();
    }

    @PostConstruct
    public void addPageInterceptor() {
        PageInterceptor interceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.putAll(this.pageHelperProperties());
        properties.putAll(this.properties.getProperties());
        interceptor.setProperties(properties);
        Iterator var3 = this.sqlSessionFactoryList.iterator();

        while(var3.hasNext()) {
            SqlSessionFactory sqlSessionFactory = (SqlSessionFactory)var3.next();
            sqlSessionFactory.getConfiguration().addInterceptor(interceptor);
        }

    }
}

这里主要就是为 SqlSessionFactory 加入组件:  PageInterceptor

 

2. PageHelper.startPage(1, 10)

protected static final ThreadLocal LOCAL_PAGE = new ThreadLocal();
protected static boolean DEFAULT_COUNT = true;    

public static  Page startPage(int pageNum, int pageSize) {
    return startPage(pageNum, pageSize, DEFAULT_COUNT);
}

public static  Page startPage(int pageNum, int pageSize, boolean count) {
    return startPage(pageNum, pageSize, count, (Boolean)null, (Boolean)null);
}

public static  Page startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
    Page page = new Page(pageNum, pageSize, count);
    page.setReasonable(reasonable);
    page.setPageSizeZero(pageSizeZero);
    Page oldPage = getLocalPage();
    if (oldPage != null && oldPage.isOrderByOnly()) {
        page.setOrderBy(oldPage.getOrderBy());
    }

    setLocalPage(page);
    return page;
}

这里主要看 setLocalPage(page) 方法, 看看干了啥:

    protected static void setLocalPage(Page page) {
        LOCAL_PAGE.set(page);
    }

LOCAL_PAGE.set(page) 就是存储了一个线程变量, 后面还可以通过 LOCAL_PAGE.get() 方法拿出这个变量. 

 

3. PageInterceptor

public Object intercept(Invocation invocation) throws Throwable {
        Object var16;
        try {
            Object[] args = invocation.getArgs();
            MappedStatement ms = (MappedStatement)args[0];
            Object parameter = args[1];
            RowBounds rowBounds = (RowBounds)args[2];
            ResultHandler resultHandler = (ResultHandler)args[3];
            Executor executor = (Executor)invocation.getTarget();
            CacheKey cacheKey;
            BoundSql boundSql;
            if (args.length == 4) {
                boundSql = ms.getBoundSql(parameter);
                cacheKey = executor.createCacheKey(ms, parameter, rowBounds, boundSql);
            } else {
                cacheKey = (CacheKey)args[4];
                boundSql = (BoundSql)args[5];
            }

            this.checkDialectExists();
            List resultList;
            if (!this.dialect.skip(ms, parameter, rowBounds)) {
                if (this.dialect.beforeCount(ms, parameter, rowBounds)) {
                    Long count = this.count(executor, ms, parameter, rowBounds, resultHandler, boundSql);
                    if (!this.dialect.afterCount(count, parameter, rowBounds)) {
                        Object var12 = this.dialect.afterPage(new ArrayList(), parameter, rowBounds);
                        return var12;
                    }
                }

                resultList = ExecutorUtil.pageQuery(this.dialect, executor, ms, parameter, rowBounds, resultHandler, boundSql, cacheKey);
            } else {
                resultList = executor.query(ms, parameter, rowBounds, resultHandler, cacheKey, boundSql);
            }

            var16 = this.dialect.afterPage(resultList, parameter, rowBounds);
        } finally {
            if (this.dialect != null) {
                this.dialect.afterAll();
            }

        }

        return var16;
    }

 

3.1 this.dialect.skip()

    public boolean skip(MappedStatement ms, Object parameterObject, RowBounds rowBounds) {
        if (ms.getId().endsWith("_COUNT")) {
            throw new RuntimeException("在系统中发现了多个分页插件,请检查系统配置!");
        } else {
            Page page = this.pageParams.getPage(parameterObject, rowBounds);
            if (page == null) {
                return true;
            } else {
                if (StringUtil.isEmpty(page.getCountColumn())) {
                    page.setCountColumn(this.pageParams.getCountColumn());
                }

                this.autoDialect.initDelegateDialect(ms);
                return false;
            }
        }
    }

这里主要看一下 pageParams.getPage() 方法:

    public Page getPage(Object parameterObject, RowBounds rowBounds) {
        Page page = PageHelper.getLocalPage();
        if (page == null) {
            if (rowBounds != RowBounds.DEFAULT) {
                if (this.offsetAsPageNum) {
                    page = new Page(rowBounds.getOffset(), rowBounds.getLimit(), this.rowBoundsWithCount);
                } else {
                    page = new Page(new int[]{rowBounds.getOffset(), rowBounds.getLimit()}, this.rowBoundsWithCount);
                    page.setReasonable(false);
                }

                if (rowBounds instanceof PageRowBounds) {
                    PageRowBounds pageRowBounds = (PageRowBounds)rowBounds;
                    page.setCount(pageRowBounds.getCount() == null || pageRowBounds.getCount());
                }
            } else if (parameterObject instanceof IPage || this.supportMethodsArguments) {
                try {
                    page = PageObjectUtil.getPageFromObject(parameterObject, false);
                } catch (Exception var5) {
                    return null;
                }
            }

            if (page == null) {
                return null;
            }

            PageHelper.setLocalPage(page);
        }

        if (page.getReasonable() == null) {
            page.setReasonable(this.reasonable);
        }

        if (page.getPageSizeZero() == null) {
            page.setPageSizeZero(this.pageSizeZero);
        }

        return page;
    }

这里的 PageHelper.getLocalPage() 执行的就是:  (Page)LOCAL_PAGE.get()

去线程中拿取存储的变量,

1. 如果拿到了, 则表示这个方法要分页, 去执行 ExecutorUtil.pageQuery() 方法

2. 如果拿不到, 则表示不用分页, 去执行 executor.query() 方法

 

3.2 ExecutorUtil.pageQuery()

public static  List pageQuery(Dialect dialect, Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql, CacheKey cacheKey) throws SQLException {
        if (!dialect.beforePage(ms, parameter, rowBounds)) {
            return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, boundSql);
        } else {
            parameter = dialect.processParameterObject(ms, parameter, boundSql, cacheKey);
            String pageSql = dialect.getPageSql(ms, boundSql, parameter, rowBounds, cacheKey);
            BoundSql pageBoundSql = new BoundSql(ms.getConfiguration(), pageSql, boundSql.getParameterMappings(), parameter);
            Map additionalParameters = getAdditionalParameter(boundSql);
            Iterator var12 = additionalParameters.keySet().iterator();

            while(var12.hasNext()) {
                String key = (String)var12.next();
                pageBoundSql.setAdditionalParameter(key, additionalParameters.get(key));
            }

            return executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, cacheKey, pageBoundSql);
        }
    }

getPageSql()最终会调用 MySqlDialect.java 中的 getPageSql() 方法:

    public String getPageSql(String sql, Page page, CacheKey pageKey) {
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (page.getStartRow() == 0) {
            sqlBuilder.append(" LIMIT ? ");
        } else {
            sqlBuilder.append(" LIMIT ?, ? ");
        }

        return sqlBuilder.toString();
    }

 

3.3 afterAll()

在finally中, 执行了一个 Pagehelper.afterAll() 方法:

    public void afterAll() {
        AbstractHelperDialect delegate = this.autoDialect.getDelegate();
        if (delegate != null) {
            delegate.afterAll();
            this.autoDialect.clearDelegate();
        }

        clearPage();
    }

看一下 clearPage() 方法:

    public static void clearPage() {
        LOCAL_PAGE.remove();
    }

这里将 线程中存储的 page 删除掉了. 

 

分页的生命周期到这里就差不多结束了, 后面执行别的sql方法的时候, 就不会受到影响了.

 

你可能感兴趣的:(mybatis - pagehelper)