MyBatis实现拦截器分页功能
1.原理
- 在mybatis使用拦截器(interceptor),截获所执行方法的sql语句与参数。
(1)修改sql的查询结果:将原sql改为查询count(*) 也就是条数
(2)将语句sql进行拼接 limit(MySql)或rownum(Oracle)语句达到分页效果 - 制作分页Page类,需要分页的bean继承分页Page类。
- jsp界面通过Page类的属性参数实现对页数控制。
2.具体步骤
(1)首先创建PageBean
(2)配置分页使用的拦截器实现Interceptor接口,重写其抽象方法
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }),
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = { Statement.class }) })
public class PageInterceptor implements Interceptor {
@Intercepts注解表示被会被拦截的类和方法参数
//SELECT_ID 根据被捕获的方法判断是否需要进行分页 如果方法名含有Page则进行分页
private static final String SELECT_ID = "Page";
//在intercept方法中获取mybatis.xml中的property属性值为PageCount赋值
private Properties properties;
//通过PageBean对象获取Count值的where条件后参数
private PageBean pageBean;
1. 拦截器的主方法则是具体功能的实现,主要做的事:
判断被拦截的方法是否需要进行分页
需要分页则截取sql语句,截取条件并拼接成查询条数,也就是将
select * from user ----> select count(*) from user
- 获取数据库连接对象,并执行拼接好的count(*)获取条数Sql语句获取数据库条数 ,并将值存到设计好的PageBean对象中
//获取数据库连接对象
Connection connection = (Connection) invocation.getArgs()[0];
//执行countSql语句 并获得Count值 为PageBean的totalCount赋值
PreparedStatement countStmt = null;
ResultSet rs = null;
int totalCount = 0;
try {
countStmt = connection.prepareStatement(countSql);
rs = countStmt.executeQuery();
if (rs.next()) {
totalCount = rs.getInt(1);
}
} catch (SQLException e) {
System.out.println("Ignore this exception" + e);
} finally {
try {
rs.close();
countStmt.close();
} catch (SQLException e) {
System.out.println("Ignore this exception" + e);
}
}
- 将原sql语句增加limit a,b a为查询结果第一条,b为所查询的条数
public String concatPageSql(String sql, PageBean pageBean) {
StringBuffer sb = new StringBuffer();
sb.append(sql);
sb.append(" limit ").append(pageBean.getPageLimit()).append(" ,").append(pageBean.getPageCount());
return sb.toString();
}
- 将拦截修改好的sql语句传递给dao层执行的方法 并返回对应的结果
//将处理好的SQL语句绑定
String pageSql = concatPageSql(sql, pageBean);
//将修改好的sql语句返回给方法
metaStatementHandler.setValue("delegate.boundSql.sql", pageSql);
- 退出拦截器,开始执行dao层方法,执行sql语句进行数据处理
//执行dao层方法
return invocation.proceed();
Controller中配置 接收参数与返回参数
- 参数为PageBean的子类对象–接收由JSP中传入的分页数据信息–模糊查询的参数等等
- 而参数在dao层方法完成后,会在拦截器中对其分页数据进行赋值–也就是经过拦截器处理后,对象的数据增加了
- 以User类为例
@RequestMapping("/selectLikeUser.do")
//参数为user 而mav.addObject("pageBean", user); 也是user,这是拦截器有对user进行分页信息赋值
public ModelAndView selectPageLikeAllUser(User user){
//执行的顺序Service-->拦截器(修改sql语句,注入分页参数)-->Dao方法-->返回结果
//所以有两个值 ① 分页的数据(当前页,总页数等数据)在user对象中
// ② 保存数据的集合userList
List<User> userList=this.userService.selectPageLikeAllUser(user);
ModelAndView mav=new ModelAndView();
mav.addObject("userList", userList);
//对应的前台Jsp界面
mav.setViewName("userList");
//将分页数据保存
mav.addObject("pageBean", user);
return mav;
}
前台Jsp界面
- 翻页需要的button模块
<div class="div_input">
//分页需要用到的参数有JS对应的方法进行表单提交时传递参数 而limtr 从0开始,后台执行sql语句 limit 0,每页的条数
//每次点击下一页 变化的都是limit 前面的参数 limit 参数*条数,条数
//第一页 limit 0*2,2
//第二页 limit 1*2,2
//第三页 limit 2*2,2 …………
<input type="button" class="input_scan" value="首页" onclick="page(0)"/>
//判断上一页的方法,当表单无上一页是 当前input会被隐藏
<c:if test="${pageBean.previous}">
<input type="button" class="input_scan" value="上页" onclick="page(${pageBean.pageNo - 1})"/>
</c:if>
//判断下一页的方法,当前为最大页时,input隐藏
<c:if test="${pageBean.next}">
<input type="button" class="input_scan" value="下页" onclick="nextPage(${pageBean.pageNo + 1},${pageBean.totalPage})"/>
</c:if>
<input type="button" class="input_scan" value="末页" onclick="page(${pageBean.totalPage -1})"/>
<span class="span_text">跳到第</span>
<input type="text" class="input_text" name="pageNo" id="pageID"/>
<span class="span_text">页</span>
<input type="button" class="input_scan" value="跳转" onclick="pageSubmit(${pageBean.totalPage})"/>
</div>
- 对应的JS方法
//每一次表单的提交,在controller中捕获的都是pageID的那个type='text'的那个文本框
//方法中的逻辑判断则是为了避免一些提交bug,保证pageID的文本框中数值显示正常以及跳转按钮等等
<script type="text/javascript">
function page(pageArg){
var myform = document.getElementById("myform");
var pageNo = document.getElementById("pageID");
pageNo.value = pageArg;
if(pageArg!=-1){
myform.submit();
}else{
pageNo.value ="";
}
}
function pageSubmit(pageArg){
var myform = document.getElementById("myform");
var pageNo = document.getElementById("pageID");
if(pageNo.value-1 < pageArg){
pageNo.value=Number(pageNo.value)-1;
if(pageNo.value>-1){
myform.submit();
}else{
pageNo.value="";
}
}
}
function nextPage(pageArg,totalPage){
var myform = document.getElementById("myform");
var pageNo = document.getElementById("pageID");
pageNo.value = pageArg;
if(pageArg<totalPage)
myform.submit();
}
</script>
**
下面为完整代码
**
拦截器中,因为模糊查询,所以多了一些判断参数的方法,以及为sql语句的参数赋值的过程,仅供参考
PageBean为对应的分页model类,而selectInfo为模糊查询参数,在拦截器中会使用此参数在查询count条数时,修改参数
**
PageBean
**
package com.chinasofti.lexian.bean;
import java.util.HashMap;
public class PageBean {
private int totalCount;//总的条数
private int pageNo;//当前的页数
private int pageCount;//每页的条数
private int pageLimit;//下一页的条件 等于pageNo*pageCount
private String storename;
private String starttime;
private String closetime;
private String selectInfo;
private int state;
private static HashMap<String, String> infoHashMap;//模糊查询配置参数
static{
infoHashMap = new HashMap<String, String>();
}
//无参构造方法
public PageBean() {
super();
// TODO Auto-generated constructor stub
}
//全参构造方法
public PageBean(int totalCount, int pageNo, int pageCount) {
this.totalCount = totalCount;