zoukankan      html  css  js  c++  java
  • mybatis

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

    <dependency>
         <groupId>org.mybatis.spring.boot</groupId>
         <artifactId>mybatis-spring-boot-starter</artifactId>
         <version>1.3.2</version>
    </dependency>

    application.yml配置:

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

    加入一个 UserService:

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

    UserMapper.java 中加入一个方法:

    List<User> 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<SqlSessionFactory> 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<Page> LOCAL_PAGE = new ThreadLocal();
    protected static boolean DEFAULT_COUNT = true;    
    
    public static <E> Page<E> startPage(int pageNum, int pageSize) {
        return startPage(pageNum, pageSize, DEFAULT_COUNT);
    }
    
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count) {
        return startPage(pageNum, pageSize, count, (Boolean)null, (Boolean)null);
    }
    
    public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
        Page<E> page = new Page(pageNum, pageSize, count);
        page.setReasonable(reasonable);
        page.setPageSizeZero(pageSizeZero);
        Page<E> 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 <E> List<E> 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<String, Object> 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方法的时候, 就不会受到影响了.

  • 相关阅读:
    201521123074 《Java程序设计》第5周学习总结
    201521123074 《Java程序设计》第4周学习总结
    201521123074 《Java程序设计》第3周学习总结
    201521123005 《Java程序设计》 第十四周学习总结
    201521123005《Java程序设计》第十三周学习总结
    201521123005 《Java程序设计》 第十二周学习总结
    201521123005 《Java程序设计》 第十一周学习总结
    201521123005 《Java程序设计》 第十周学习总结
    201521123005 《Java程序设计》 第九周学习总结
    201521123005 《java程序设计》 第八周学习总结
  • 原文地址:https://www.cnblogs.com/elvinle/p/12312139.html
Copyright © 2011-2022 走看看