分页设计原理和多条件过滤查询

分页设计和原理

设计

分页页面的数据,主要由两部分组成

  1. 当前页的结果集数据,比如这一页有哪些信息。

  2. 分页条信息,比如包含【首页】【上页】【下页】【末页】等

分页需要依赖的变量 :

beginPage =1首页 prevPage 上一页 nextPage下一页 totalPage 总页数/末页 totalCount 总条数
currentPage 当前页 pageSize 每页显示多少条数据 当前页的数据 result

数据的来源

  • 需要用户传入的两个数据

    int currentPage = 1; 当前页,跳转到第几页

    int pageSize = 10; 每页最多多少条数据

  • 需要两条SQL查询出来的数据

    int totalCount; 数据总条数

    List result; 每一页的结果集数据

  • 自己计算出来的三个值

    int prevPage :上一页

    int nextPage :下一页

    int totalPage :总页数/末页

    计算方式:

    int totalPage = rows % pageSize == 0 ? rows / pageSize : rows / pageSize + 1; (优先计算)

    int prevPage = currentPage > 1 ? currentPage - 1 : 1;

    int nextPage = currentPage < totalPage ? currentPage + 1 : totalPage

原理

通过SQL查询出来数据总条数和每一页的结果集数据

第一条SQL:查询符合条件的结果总数(totalCount)

SELECT COUNT(*) FROM 表名 [WHERE 条件]

第二条SQL:查询符合条件的结果集(result)

SELECT * FROM 表名 [WHERE 条件] LIMIT ?,?;

第一个?:从哪一个索引的数据开始截取(从0开始) beginIndex = (currentPage - 1) * pageSize

第二个?:pageSize 每页多少条数据

把分页查询的结果对象封装在page包中方便管理

/**
 * 用来封装分页数据
 */
@Getter
@ToString
public class PageResult {
    // 两个用户输入
    private int currentPage; // 当前页码
    private int pageSize; // 分页显示的最大记录数

    // 两个SQL的执行结果
    private int totalCount; // 总记录数
    private List<?> result; // 当前页数的数据

    // 三个计算结果
    private int totalPage; //总页数,尾页码
    private int prevPage; // 上一页
    private int nextPage; // 下一页

    public PageResult(int currentPage, int pageSize) {
       this(currentPage,pageSize,0,new ArrayList<>());
    }

    public PageResult(int currentPage, int pageSize, int totalCount, List<?> result) {
        this.currentPage = currentPage;
        this.pageSize = pageSize;
        this.totalCount = totalCount;
        this.result = result;
        // 计算
        this.totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
        this.prevPage = currentPage - 1 > 1 ? currentPage - 1 : 1;
        this.nextPage = currentPage + 1 < totalCount ? currentPage + 1 : totalPage;
    }
}

分页后台的实现

需要在DAO组件增加两个方法

int queryForCount(int currentPage, int pageSize); List<?> queryForList(int currentPage, int pageSize);

但是在MyBatis提供的操作方法传入执行SQL任务的参数对象只能是一个对象所以要把前台传递过来的两个值封装

/**
 * 封装每页操作,前台用户传来的数据
 */
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
public class QueryObject {
    // 分页依赖的两个数据
    private int currentPage = 1; // 当前页
    private int pageSize = 3; // 每页显示条数

    /**
     * 返回每页开始的数据的索引
     * limit #{beginIndex},#{pageSize}
     *
     * @return
     */
    public int getBeginIndex() {
        return (currentPage - 1) * pageSize;
    }
}

此时的DAO中方法:

int queryForCount(QueryObject qo); List<?> queryForList(QueryObject qo);

思路整理

准备工作:

  1. 定义PageResult类,用于封装分页查询后每页的所有数据(结果集和分页条信息),存放于query包中。

  2. 定义QueryObject类,用于封装分页查询前台用户传入数据,存放于query包中。

操作步骤:

  1. 在DAO接口中定义两个方法,分别用于查询结果总数和结果集。

  2. 在Mapper文件中编写分页查询的两条SQL语句,在DAO实现类中执行查询操作。

    Mapper文件:

    <!--当前页数的数据-->
      <select id="queryForList" resultType="cn.wolfcode.emis.daomain.Employee">
          select * from employeelimit #{beginIndex},#{pageSize}
      </select>
    <!-- 查询总记录数, resultType="int"  告诉mybatis 帮咱们把long 转成 int -->
        <select id="queryForCount" resultType="int">
            select count(id) from employee
        </select>
    

    DAO实现类:

    	public List<Employee> queryForList(EmployeeQueryObject qo) {
    		SqlSession sqlSession = MyBatisUtil.openSession();
    		List<Employee> list = sqlSession.selectList("cn.wolfcode.emis.mapper.EmployeeMapper.queryForList",qo);
    		sqlSession.commit();
    		sqlSession.close();
    		return list;
    	}
    
    	public int queryForCount(EmployeeQueryObject qo) {
    		SqlSession sqlSession = MyBatisUtil.openSession();
    		int selectOne = sqlSession.selectOne("cn.wolfcode.emis.mapper.EmployeeMapper.queryForCount", qo);
    		sqlSession.commit();
    		sqlSession.close();
    		return selectOne;
    	}
    
  3. 在service接口中增强分页查询方法,并在service实现类中实现。

    service实现类:

    public PageResult query(EmployeeQueryObject qo) {
    		IEmployeeDAO emp = new EmployeeDAOImpl();
    		// 通过DAO获取总记录数
    		int totalCount = emp.queryForCount(qo);
    		// 判断是是否有数据,有返回查询到的数据,没有返回一个空集合
    		if (totalCount < 1){
    			return new PageResult(qo.getCurrentPage(),qo.getPageSize());
    		}
    		int pageSize = qo.getPageSize();
    		int totalPage = totalCount % pageSize == 0 ? totalCount / pageSize : totalCount / pageSize + 1;
    		if (qo.getCurrentPage() > totalPage) {
    			qo.setCurrentPage(totalPage);
    		}
    		if (qo.getCurrentPage() < 1){
    			qo.setCurrentPage(1);
    		}
    		List<Employee> list = emp.queryForList(qo);
    		return new PageResult(qo.getCurrentPage(),qo.getPageSize(),totalCount,list);
    	}
    

分页前台实现

操作步骤:

  1. 必须先完成业务层组件,保证后台测试通过

  2. 根据MVC思想,浏览器发出分页请求参数( 去往第几页 / 每页多少条数据)

  3. 在Servlet中接收currentPage参数,并封装到QueryObject对象,调用service中分页查询方法(query)

  4. 把得到的分页查询结果对象(PageResult)共享在请求作用域中,跳转到JSP,显示即可

  5. 修改JSP页面,编写出分页条信息(分页条中的信息来源于PageResult对象)

代码:

Servlet:

protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        EmployeeQueryObject qo = new EmployeeQueryObject();
        req2qo(req, qo);
        PageResult pageResult = service.query(qo);
        req.setAttribute("PageResult", pageResult);
        req.getRequestDispatcher("/WEB-INF/view/list.jsp").forward(req, resp);
    }

    private void req2qo(HttpServletRequest req, EmployeeQueryObject qo) {
        String currentPage = req.getParameter("currentPage");
        if (StringUtil.hashLength(currentPage)) {
            qo.setCurrentPage(Integer.valueOf(currentPage));
            req.setAttribute("currentPage", qo.getCurrentPage());
        }
        String pagesize = req.getParameter("pageSize");
        if (StringUtil.hashLength(pagesize)) {
            qo.setPageSize(Integer.valueOf(pagesize));
            req.setAttribute("pageSize", qo.getPageSize());
        }
    }

list.jsp:

多条件过滤查询

高级查询,就是多条件过滤,根据多个查询条件去筛选结果集。如,在商品列表中,用户可以根据自己的需要,按照商品的名称或者价格范围对商品进行查询。

要为用户实现多条件过滤查询,需要解决两个问题:

  1. 如何封装用户传递到后台的请求参数(过滤条件数据)
  2. 如何根据用户传递的参数,拼接对应的SQL
    SQL语法:
    SELECT * FROM 表名 WHERE 条件1 AND/OR 条件2 AND/OR 条件3
    注意:
    1、第一个条件前面必须使用 WHERE 。
    2、多个条件之间使用 AND 或者 OR 来连接。

实现SQL的拼接需要MyBatis的动态SQL

阅读MyBatis官方文档,搞明白 if 和 where两个元素是用来干什么的,有什么作用。

if元素,用于判断,一般用作是否应该符合某一个查询条件

<if test="boolean表达式"></if>
<!--
xml 中<> 转义符
 <  :  &lt;
 >  :  &gt:
-->

where元素,只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。where 和 if的配合使用案例

<select id="query" resultType="Product">
    SELECT * FROM product 
    <where>
       <if test="minPrice !=null">
            AND salePrice >= #{minPrice }
       </if>
       <if test="maxPrice !=null">
            AND salePrice &lt;= #{maxPrice }
       </if>
    </where>
</select>

查询条件的封装

/**
 * 封装员工的过滤查询和分页查询的条件
 */
@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class EmployeeQueryObject extends QueryObject {
    // 过滤查询需要的条件
    private String name;
    private BigDecimal minSalary;
    private BigDecimal maxSalary;

}

QueryObect是其他查询对象的父类,可以在该类中封装查询对象的相同成员

条件过滤的实现

操作步骤:

  1. 修改list.jsp页面,增加高级查询表单,包含需要查询的信息,注意:让该表单(form)包含列表的table元素。

  2. 在query包中定义EmployeeQueryObject类,继承于QueryObject,该类用于封装货品对象的高级查询信息。注意:一般的,为了避免不必要的麻烦,让字段名称和表单中的请求参数名称相同。

  3. 在Servlet中调用query方法之前,接受高级查询数据,并封装到EmployeeQueryObject对象中。

  4. 重新回去修改list.jsp页面,使之支持回显出查询的信息。在元素中,value属性表示该文本框中显示什么数据。

  5. 在productMapper.xml中拼写查询的SQL语句,查询结果总数和查询结果集都要拼写,且查询条件是一样的。

    <!--当前页数的数据-->
      <select id="queryForList" resultType="cn.wolfcode.emis.daomain.Employee">
          select * from employee
          <where>
              <if test="name != null and name != ''">
                  <!--concat函数:在模糊查询时,使用该函数拼接 % 和关键字-->
                  AND name like concat('%',#{name},'%')
              </if>
              <if test="minSalary">
                  and salary &gt;= #{minSalary}
              </if>
              <if test="maxSalary">
                  and salary &lt;= #{maxSalary}
              </if>
          </where>
          limit #{beginIndex},#{pageSize}
      </select>
    <!-- 查询总记录数, resultType="int"  告诉mybatis 帮咱们把long 转成 int -->
        <select id="queryForCount" resultType="int">
            select count(id) from employee
            <where>
                <if test="name != null and name != ''">
                    AND name like concat('%',#{name},'%')
                </if>
                <if test="minSalary">
                    and salary &gt;= #{minSalary}
                </if>
                <if test="maxSalary">
                    and salary &lt;= #{maxSalary}
                </if>
            </where>
        </select>
    

解决翻页数据丢失问题

解决方案:

  1. 在超链接上拼接高级查询的参数,如果参数过多,实现比较麻烦

  2. 使用JS来实现翻页操作,将超链接上的分页相关的参数放到表单中

    其原理是:在高级查询表单中提供currentPage的文本框,我们把需要跳转的页码设置到该文本框,再使用JS提交高级查询表单,此时就可以把查询条件和currentPage一起提交到后台,并封装到XxxQueryObject中。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小云很优秀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值