博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis 的插件应用:分页处理(分页插件PageHelper)
阅读量:5782 次
发布时间:2019-06-18

本文共 20951 字,大约阅读时间需要 69 分钟。

hot3.png

mybatis 框架虽然使用起来相当简单,且灵活,自己管理着 sql 语句,但是开发起来还是有着不少的工作量,比如在处理分页问题的时候,我们通常需要另外再查询一次总共的记录数

其实我们是希望把获取分页信息的工作给统一起来,简洁代码,减少工作量

我们可以利用 mybatis 的插件功能(拦截器)来处理分页,这里我们尝试着自己去写一个简单的分页插件,后面也有介绍一个优秀的 mybatis 分页插件 PageHelper 的使用方法

一、自己写一个简单的分页插件

在 mybatis xml 配置文件中,我们可以配置插件(plugins),mybatis 允许你在已映射语句执行过程中的某一点进行拦截调用,这样我们可以通过拦截查询方法,添加分页查询条件,包装查询结果

1、创建一个插件

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可

/* *  指定方法签名: *      下面的配置表面 将会拦截在 Executor 实例中所有的 “query” 方法调用,  *      这里的 Executor 是负责执行低层映射语句的内部对象 */@Intercepts({@Signature(    type = Executor.class,    method = "query",    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class PageInterceptor implements Interceptor {        /**     * 拦截目标对象中目标方法的执行     */    @Override    public Object intercept(Invocation invocation) throws Throwable {        //TODO 覆盖目标方法,我们在这里覆盖目标方法                // 执行目标方法,并返回结果(这里没有处理)        return invocation.proceed();    }    /**     * 包装目标对象,即为目标对象创建一个代理对象     */    @Override    public Object plugin(Object target) {        // 借助 Plugin 的 wrap(Object target,Interceptor interceptor); 包装我们的目标对象        // target: 目标对象, interceptor: 拦截器, this 表示使用当前拦截器        return  Plugin.wrap(target, this);    }    /**     * 获取插件注册时,传入的property属性     */    @Override    public void setProperties(Properties properties) {        // TODO     }}

同时在 mybatis的配置文件中注册插件

上面的只是先创建了一个拦截器,还没有做具体的处理,现在我们先理清楚需要做的事情

  1. 拦截查询的目标方法,通过 Invocation 参数获取目标方法原来的参数信息
  2. 因为是分页查询,需要传入分页的参数条件,将通过是否传入有效的分页参数来判断是否要进行分页处理
  3. 除了要执行原本的查询语句,还要查询count总记录数,并计算出其他分页信息
  4. 将查询出来的结果和分页信息包装在一起并返回结果

2、创建接口和返回结果集

查询的时候,我们需要传入分页条件,这里我们创建一个 IPage 接口,为了规范统一

public interface IPage {    /**     * 当前页     */    Integer getPageNum();        /**     * 每页数量     */    Integer getPageSize();        /**     * 开始行     */    Integer getStartRow();        /**     * 排序条件     */    String getOrderBy();}

并实现一个简单类 PageConfig.java

public class PageConfig implements IPage {    private Integer pageNum;    private Integer pageSize;    private Integer startRow;    private String orderBy;        public PageConfig() {        this(1,10);    }    public PageConfig(Integer pageNum,Integer pageSize) {        this.pageNum = pageNum;        this.pageSize = pageSize;        this.startRow = (this.pageNum -1) * this.pageSize;    }    public Integer getPageNum() {        return pageNum;    }    public void setPageNum(Integer pageNum) {        this.pageNum = pageNum;        this.startRow = (this.pageNum -1) * this.pageSize;    }    public Integer getPageSize() {        return pageSize;    }    public void setPageSize(Integer pageSize) {        this.pageSize = pageSize;        this.startRow = (this.pageNum -1) * this.pageSize;    }    public String getOrderBy() {        return orderBy;    }    public void setOrderBy(String orderBy) {        this.orderBy = orderBy;    }    public Integer getStartRow() {        return startRow;    }    public void setStartRow(Integer startRow) {        this.startRow = startRow;    }}

同时我们需要包装一下返回结果集,mybatis 直接查询返回的结果集类型为 ArrayList,我们要在此基础上加上分页信息

Page.java

public class Page
extends ArrayList
{ private static final long serialVersionUID = 1L; private int pageNum; // 页码 private int pageSize; // 每页数量 private long total; // 总记录数 private int pages; // 页数 public Page() { super(); } public Page(int pageNum, int pageSize) { this(pageNum, pageSize, 0); } public Page(int pageNum, int pageSize, long total) { this.pageNum = pageNum; this.pageSize = pageSize; this.total = total; this.pages = (int)(this.pageSize == 0 ? 0 : this.total % this.pageSize == 0 ? this.total / this.pageSize : (this.total / this.pageSize + 1)); } public int getPageNum() { return pageNum; } public void setPageNum(int pageNum) { this.pageNum = pageNum; } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public Long getTotal() { return total; } public void setTotal(long total) { this.total = total; this.pages = (int)(this.pageSize == 0 ? 0 : this.total % this.pageSize == 0 ? this.total / this.pageSize : (this.total / this.pageSize + 1)); } public int getPages() { return pages; } public void setPages(int pages) { this.pages = pages; } @Override public String toString() { return "Page [pageNum=" + pageNum + ", pageSize=" + pageSize + ", total=" + total + ", pages=" + pages + "]"; }}

3、处理查询方法

我们再顺一下拦截查询方法之后的流程

  1. 通过 Invocation 参数获取目标方法原来的参数信息
  2. 分析参数是否要进行分页处理,不需要不处理,执行原来的方法
  3. 添加分页条件,并查询结果(包括查询总数)
  4. 包装结果集并返回

这里直接附上实现代码

/* * 指定方法签名: 下面的配置表面 将会拦截在 Executor 实例中的 “query” 方法调用, 这里的 Executor 是负责执行低层映射语句的内部对象 */@SuppressWarnings({"rawtypes", "unchecked"})@Intercepts({ @Signature(    type = Executor.class,     method = "query",    args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})public class PageInterceptor implements Interceptor {    private final static String COUNT_SUFFIX = "_COUNT"; // count查询语句Id后缀    /**     * 拦截目标对象中目标方法的执行     */    @Override    public Object intercept(Invocation invocation) throws Throwable {        // 获得目标对象        Executor executor = (Executor)invocation.getTarget();        // 获得目标方法的参数        Object[] args = invocation.getArgs();        // MappedStatement表示的是XML中的一个SQL        MappedStatement mappedStatement = (MappedStatement)args[0];        Object parameter = args[1];        System.out.println(parameter.getClass());        RowBounds rowBounds = (RowBounds)args[2];        ResultHandler resultHandler = (ResultHandler)args[3];        BoundSql boundSql = mappedStatement.getBoundSql(parameter);        CacheKey cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, boundSql);        //获取分页参数        IPage pageConfig = getPageConfig(parameter);        if (pageConfig != null) {            return pageQuery(executor, mappedStatement, parameter, resultHandler, boundSql, cacheKey,                pageConfig);        } else {            // 不用分页,执行目标方法            return invocation.proceed();        }    }    /**     * 分页查询     *      * @param executor     * @param ms     * @param parameter     * @param resultHandler     * @param boundSql     * @param cacheKey     * @param pageConfig     * @return     * @throws SQLException     */    private Object pageQuery(Executor executor, MappedStatement ms, Object parameter,        ResultHandler resultHandler, BoundSql boundSql, CacheKey cacheKey, IPage pageConfig) throws SQLException {        Page page = new Page(pageConfig.getPageNum(), pageConfig.getPageSize());        // 查询总数        Long count = countQuery(executor, ms, parameter, resultHandler, boundSql);        // 总数大于 0 ,开始分页查询        if (count != null && count > 0) {            page.setTotal(count);            // 生成分页的缓存 key            CacheKey pageKey = cacheKey;            // 获取分页 sql            StringBuilder pageSql = new StringBuilder(boundSql.getSql());            if (pageConfig.getStartRow() == 0) {                pageSql.append(" LIMIT ").append(pageConfig.getPageSize());            } else {                pageSql.append(" LIMIT ").append(pageConfig.getStartRow()).append(",").append(pageConfig.getPageSize());            }            BoundSql pageBoundSql =                new BoundSql(ms.getConfiguration(), pageSql.toString(), boundSql.getParameterMappings(), parameter);            List list = executor.query(ms, parameter, RowBounds.DEFAULT, resultHandler, pageKey, pageBoundSql);            page.addAll(list);        }        return page;    }    /**     * count 查询,获取总记录数     *      * @param executor     * @param mappedStatement     * @param parameter     * @param resultHandler     * @param boundSql     * @return     * @throws SQLException     */    private Long countQuery(Executor executor, MappedStatement mappedStatement, Object parameter,        ResultHandler resultHandler, BoundSql boundSql) throws SQLException {        // 创建新的 MappedStatement 用于count查询        MappedStatement countMs = newCountMappedStatement(mappedStatement);        // 创建 count 查询的缓存 key        CacheKey countKey = executor.createCacheKey(countMs, parameter, RowBounds.DEFAULT, boundSql);        // 创建 countBoundSql        String countSql = "SELECT COUNT(*) FROM (" + boundSql.getSql() + ") T";        BoundSql countBoundSql =            new BoundSql(countMs.getConfiguration(), countSql, boundSql.getParameterMappings(), parameter);        // 执行 count 查询        Object countResult =            executor.query(countMs, parameter, RowBounds.DEFAULT, resultHandler, countKey, countBoundSql);        Long count = (Long)((List)countResult).get(0);        return count;    }    /**     * 新建用于count查询的MappedStatement     *      * @param ms     * @return     */    private MappedStatement newCountMappedStatement(MappedStatement ms) {        String countMsId = ms.getId() + COUNT_SUFFIX;        MappedStatement.Builder builder =            new MappedStatement.Builder(ms.getConfiguration(), countMsId, ms.getSqlSource(), ms.getSqlCommandType());        builder.resource(ms.getResource());        builder.fetchSize(ms.getFetchSize());        builder.statementType(ms.getStatementType());        builder.keyGenerator(ms.getKeyGenerator());        builder.resultSetType(ms.getResultSetType());        builder.cache(ms.getCache());        builder.flushCacheRequired(ms.isFlushCacheRequired());        builder.useCache(ms.isUseCache());        builder.timeout(ms.getTimeout());        builder.parameterMap(ms.getParameterMap());        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {            StringBuilder keyProperties = new StringBuilder();            for (String keyProperty : ms.getKeyProperties()) {                keyProperties.append(keyProperty).append(",");            }            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());            builder.keyProperty(keyProperties.toString());        }        // 设置返回结果为 Long        List
resultMaps = new ArrayList
(); ResultMap resultMap = new ResultMap.Builder(ms.getConfiguration(), ms.getId(), Long.class, new ArrayList
(0)) .build(); resultMaps.add(resultMap); builder.resultMaps(resultMaps); return builder.build(); } /** * 获取分页信息 * * @param parameter * @param rowBounds * @return */ private IPage getPageConfig(Object parameter) { IPage pageConfig = null; if (parameter instanceof IPage) { pageConfig = (IPage)parameter; }else if(parameter instanceof MapperMethod.ParamMap) { MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap)parameter; for(Object value:paramMap.values()) { if (value instanceof IPage) { pageConfig = (IPage)value; break; } } } return pageConfig; } /** * 包装目标对象,即为目标对象创建一个代理对象 */ @Override public Object plugin(Object target) { // 借助 Plugin 的 wrap(Object target,Interceptor interceptor); 包装我们的目标对象 // target: 目标对象, interceptor: 拦截器, this 表示使用当前拦截器 return Plugin.wrap(target, this); } /** * 获取插件注册时,传入的property属性 */ @Override public void setProperties(Properties properties) { }}

4、测试结果

要使用插件,记得在 mybatis xml 中配置plugin

我这个例子适用于使用 mapper 接口,只要接口方法有 IPage 的分页查询参数,就会分页查询

public interface UpmsUserMapper {    List
selectUser(UpmsUser upmsUser, PageConfig pageConfig); List
selectUser(IPage pageConfig);}

我们创建测试类测试一下结果

@RunWith(SpringJUnit4ClassRunner.class)  //使用junit4进行测试  @ContextConfiguration ("/conf/spring/applicationContext*.xml") public class UpmsUserMapperTest {    @Autowired    private UpmsUserMapper upmsUserMapper;        @Test    public void testSelectUserUpmsUserPageConfig() {        PageConfig pageConfig = new PageConfig(1, 2);        List
list = upmsUserMapper.selectUser(new UpmsUser(), pageConfig); System.out.println(list.getClass()); System.out.println(list.toString()); for (UpmsUser upmsUser : list) { System.out.println(upmsUser.toString()); } }}

测试结果如下

class com.brave.page.PagePage [pageNum=1, pageSize=2, total=3, pages=2][userId=10001][loginname=zou][password=123456][locked=null][userId=10002][loginname=zou][password=123456][locked=null]

二、分页插件 PageHelper 使用

上面的例子,只是简陋的实现一个分页插件,明白大概的流程。下面来介绍分页插件 PageHelper 的使用

PageHelper 的 github 地址

1、添加依赖

com.github.pagehelper
pagehelper
5.1.8

由于使用了 sql 解析工具,还会引入 jsqlparser.jar 包,如果不是使用 maven,还需要自己导入 pagehelper 依赖版本一致的 jsqlparser.jar 包

2、配置拦截器插件

在 mybatis 配置文件中添加拦截器插件配置

如果是 spring 项目,也可以在 spring 配置文件的 SqlSessionFactoryBean 里面添加插件配置

helperDialect=mysql

两个地方的配置选其就好,不要同时生效

下面是具体分页插件参数

参数 描述 默认值
helperDialect 分页插件会自动检测当前的数据库链接,自动选择合适的分页方式。该属性可以指定分页插件使用哪种方言。配置值:oracle,mysql,mariadb,sqlite,hsqldb,postgresql,db2,sqlserver,informix,h2,sqlserver2012,derby --
offsetAsPageNum 该参数对使用 RowBounds 作为分页参数时有效。设为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页 false
rowBoundsWithCount 该参数对使用 RowBounds 作为分页参数时有效。设置为true时,使用 RowBounds 分页会进行 count 查询 false
pageSizeZero 设置为 true 时,如果 pageSize=0 或者 RowBounds.limit = 0 就会查询出全部的结果(相当于没有执行分页查询,但是返回结果仍然是 Page 类型) false
reasonable 分页合理化参数。设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页。默认false 时,直接根据参数进行查询 false
params 为了支持startPage(Object params)方法,增加了该参数来配置参数映射,用于从对象中根据属性名取值,可以配置 pageNum,pageSize,count,pageSizeZero,reasonable,不配置映射的用默认值 pageNum=pageNum;pageSize=pageSize;count=countSql;reasonable=reasonable;pageSizeZero=pageSizeZero
supportMethodsArguments 支持通过 Mapper 接口参数来传递分页参数,分页插件会从查询方法的参数值中,自动根据上面 params 配置的字段中取值,查找到合适的值时就会自动分页。 使用方法可以参考测试代码中的 com.github.pagehelper.test.basic 包下的 ArgumentsMapTest 和 ArgumentsObjTest false
autoRuntimeDialect 设置为 true 时,允许在运行时根据多数据源自动识别对应方言的分页 false
closeConn 当使用运行时动态数据源或没有设置 helperDialect 属性自动获取数据库类型时,会自动获取一个数据库连接,通过该属性来设置是否关闭获取的这个连接,默认true关闭,设置为 false 后,不会关闭获取的连接,这个参数的设置要根据自己选择的数据源来决定 true
aggregateFunctions(5.1.5+) 默认为所有常见数据库的聚合函数,允许手动添加聚合函数(影响行数),所有以聚合函数开头的函数,在进行 count 转换时,会套一层。其他函数和列会被替换为 count(0),其中count列可以自己配置。

当 offsetAsPageNum=false 的时候,由于 PageNum 问题,RowBounds查询的时候 reasonable 会强制为 false。使用 PageHelper.startPage 方法不受影响。

这些参数是在默认情况(dialect)下有效,默认情况下会使用 PageHelper 方式进行分页,如果想要实现自己的分页逻辑,可以实现 Dialect(com.github.pagehelper.Dialect) 接口,然后配置该属性为实现类的全限定名称。

3、使用方法

这里主要是4种使用方法

  • RowBounds方式的调用
  • PageHelper 的静态方法调用
  • 接口方法使用分页参数
  • ISelect 接口方式

1)RowBounds方式的调用

使用 RowBounds 参数进行分页,这种方式侵入性最小,用只是使用了这个参数,并没有增加其他任何内容

@Repositorypublic class UpmsUserDaoImpl extends SuperDao implements UpmsUserDao {    @Override    public List
selectUser(int offset,int limit) { List
list = getSqlSession().selectList("com.brave.dao.UpmsUserDao2.selectUser", null, new RowBounds(offset, limit)); return list;}}

测试代码

@Testpublic void testSelectUser() {    List
upmsUsers = upmsUserDao.selectUser(0, 10); for (UpmsUser upmsUser : upmsUsers) { System.out.println(upmsUser.toString()); }}

我们在配置插件的时候有两个参数是关于 RowBounds 的

  • offsetAsPageNum 设置为 true 时,会将 RowBounds 中的 offset 参数当成 pageNum 使用,可以用页码和页面大小两个参数进行分页,也就是说上面的例子,我们查第一页时为 upmsUserDao.selectUser(1, 10)
  • rowBoundsWithCount RowBounds 查询默认是没有 count 查询的,设置为true时,使用 RowBounds 分页会进行 count 查询

当 rowBoundsWithCount 设置为true时,会有 count 查询,会获得查询记录总数

@Testpublic void testSelectUser() {    Page
upmsUsers = (Page
)upmsUserDao.selectUser(1, 10); System.out.println("总页数:"+upmsUsers.getTotal()); for (UpmsUser upmsUser : upmsUsers) { System.out.println(upmsUser.toString()); }}

没有设置或为false的话,total 为 -1

如果不设置 rowBoundsWithCount,也可以通过 PageRowBounds 进行 count 查询。

getSqlSession().selectList("com.brave.dao.UpmsUserDao.selectUser", null, new PageRowBounds (offset, limit))
PageRowBounds 继承 RowBounds,多了 total 属性

另外使用接口的时候也可以增加RowBounds参数

List<Country> selectAll(RowBounds rowBounds);

2)PageHelper 的静态方法调用

PageHelper 类有 startPage 和 offsetPage 方法去设置分页参数

在需要进行分页的 mybatis 查询方法前调用 PageHelper.startPage 静态方法即可,紧跟在这个方法后的第一个 mybatis 查询方法会被进行分页。

通常是在 service 的 bean 中调用 dao 的查询方法前 或者是 mapper 接口方法前 调用 PageHelper 的静态方法

@Testpublic void testSelectUser2() {    // request: url?pageNum=1&pageSize=10    // 支持 ServletRequest,Map,POJO 对象,需要配合 params 参数    // PageHelper.startPage(request);    PageHelper.startPage(1, 10);    Page
upmsUsers = (Page
)upmsUserDao.selectUser(); System.out.println("总页数:" + upmsUsers.getTotal()); for (UpmsUser upmsUser : upmsUsers) { System.out.println(upmsUser.toString()); }}

mapper 接口

@Servicepublic class UpmsUserServiceImpl implements UpmsUserService {    @Autowired    private UpmsUserMapper upmsUserMapper;    @Override    public List
listUser(int pageNum, int pageSize) { PageHelper.startPage(pageNum, pageSize); return upmsUserMapper.selectUser(); }}

使用 PageInfo

PageInfo 包含了非常全面的分页属性

List
list = (Page
)upmsUserDao.selectUser();PageInfo
pageInfo = new PageInfo(list);

private int pageNum;//当前页    private int pageSize;//每页的数量    private int size; //当前页的数量        //由于startRow和endRow不常用,这里说个具体的用法    //可以在页面中"显示startRow到endRow 共size条数据"    private int startRow;//当前页面第一个元素在数据库中的行号    private int endRow;//当前页面最后一个元素在数据库中的行号        private int pages;//总页数    private int prePage;//前一页    private int nextPage;//下一页    private boolean isFirstPage = false;//是否为第一页    private boolean isLastPage = false;//是否为最后一页    private boolean hasPreviousPage = false;//是否有前一页    private boolean hasNextPage = false;//是否有下一页    private int navigatePages;//导航页码数    private int[] navigatepageNums;//所有导航页号    private int navigateFirstPage;//导航条上的第一页    private int navigateLastPage;//导航条上的最后一页

3)使用参数的方法

想要使用参数方式,需要配置 supportMethodsArguments 参数为 true,同时要配置 params 参数

可以在使用接口声明时就带上 pageNum 和 pageSize

//存在以下 Mapper 接口方法,你不需要在 xml 处理后两个参数public interface CountryMapper {    List
selectByPageNumSize( @Param("upmsUser") UpmsUser upmsUser, @Param("pageNum") int pageNum, @Param("pageSize") int pageSize);}//配置supportMethodsArguments=true//在代码中直接调用:List
list = countryMapper.selectByPageNumSize(user, 1, 10);

也可以把 pageNum 和 pageSize 存在于对象中,如 UpmsUser

//存在以下 Mapper 接口方法,不需要在 xml 处理后两个参数public interface CountryMapper {    List
selectByPageNumSize(UpmsUser upmsUser);}//当 user 中的 pageNum!= null && pageSize!= null 时,会自动分页List
list = countryMapper.selectByPageNumSize(upmsUser);

两种情况都需要 pageNum 和 pageSize 两个属性同时存在才会分页,且在 xml 中的sql中不需要处理这两个参数

4)ISelect 接口方式

另外还可以使用 ISelect 接口方式方式

Page
page = PageHelper.startPage(1, 10).doSelectPage(new ISelect() { @Override public void doSelect() { upmsUserMapper.selectUser(); }});// lambda用法Page
page = PageHelper.startPage(1, 10).doSelectPage(()-> upmsUserMapper.selectUser());//也可以直接返回PageInfo,注意doSelectPageInfo方法和doSelectPagepageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(new ISelect() { @Override public void doSelect() { upmsUserMapper.selectUser(); }});//对应的lambda用法pageInfo = PageHelper.startPage(1, 10).doSelectPageInfo(() -> upmsUserMapper.selectUser());//count查询,返回一个查询语句的count数long total = PageHelper.count(new ISelect() { @Override public void doSelect() { upmsUserMapper.selectUser(country); }});//lambdatotal = PageHelper.count(()->upmsUserMapper.selectUser(country));

ISelect 接口方式除了可以保证安全外,还特别实现了将查询转换为单纯的 count 查询方式,这个方法可以将任意的查询方法,变成一个 select count(*) 的查询方法。

4、各使用方法的安全性考虑

  • 使用 RowBounds 和 PageRowBounds 参数方式是极其安全的
  • 使用参数方式是极其安全的
  • 使用 ISelect 接口调用是极其安全的

不安全的时候:

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

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

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

下面这样的代码,就是不安全的用法:

//由于 param1 存在 null 的情况,就会导致 PageHelper 生产了一个分页参数,但是没有被消费,这个参数就会一直保留在这个线程上。当这个线程再次被使用时,就可能导致不该分页的方法去消费这个分页参数,这就产生了莫名其妙的分页PageHelper.startPage(1, 10);List
list;if(param1 != null){ list = UpmsUserMapper.selectUser(param1);} else { list = new ArrayList
();}

我们只要把 PageHelper.startPage(1, 10); 方法放到if里面查询语句之前就好了

转载于:https://my.oschina.net/morgan412/blog/3000209

你可能感兴趣的文章
AOP
查看>>
进阶开发——文档,缓存,ip限速
查看>>
vue中子组件需调用父组件通过异步获取的数据
查看>>
uva 11468 - Substring(AC自己主动机+概率)
查看>>
Mysql 数据备份与恢复,用户创建,授权
查看>>
沉思录
查看>>
Angular.js中的$injector服务
查看>>
构建之法读书笔记01
查看>>
linux - lsof 命令最佳实践
查看>>
kafka性能测试
查看>>
现实世界的Windows Azure:h.e.t软件使用Windows Azure削减50%的成本
查看>>
深入.net框架
查看>>
聚合类新闻client产品功能点详情分析
查看>>
湘潭邀请赛——Alice and Bob
查看>>
js设置定时器
查看>>
数据库除运算
查看>>
LeetCode--112--路径总和
查看>>
DeviceIOControl与驱动层 - 缓冲区模式
查看>>
感悟贴2016-05-13
查看>>
vim使用教程
查看>>