##分页处理
分页
1、前台分页
2、数据库(后台)分页
3、存储过程
Orade (Rownum) Mysql(limit) sqlservier(Top N)
第一步 :
要在mybatis 核心xml中引入 拦截器插件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<plugins>
<plugin interceptor="com.foreknow.dao.interceptor.PageInterceptor"></plugin>
</plugins>
</configuration>
第二步:
创建com.foreknow.util 包 下
Page.java 总记录数 当前页数 总页数 每页要显示的额记录数
package com.foreknow.util;
public class Page {
// 总记录数
private int totalNumber;
// 当前页数
private int currentPage;
// 总页数
private int totalPage;
// 每页要显示的记录数
private int pageNumber;
public Page() {
this.currentPage = 1;
this.pageNumber = 5;
}
public int getTotalNumber() {
return totalNumber;
}
/**
* 计算总页数
*/
public void count() {
this.totalPage = this.totalNumber / this.pageNumber;
if (this.totalNumber % this.pageNumber > 0) {
this.totalPage++;
}
if (this.totalPage <= 0) {
this.totalPage = 1;
}
if (this.currentPage > this.totalPage) {
this.currentPage = this.totalPage;
}
if (this.currentPage <= 0) {
this.currentPage = 1;
}
}
public void setTotalNumber(int totalNumber) {
this.totalNumber = totalNumber;
count();
}
public int getCurrentPage() {
return currentPage;
}
public void setCurrentPage(int currentPage) {
this.currentPage = currentPage;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
public int getPageNumber() {
return pageNumber;
}
public void setPageNumber(int pageNumber) {
this.pageNumber = pageNumber;
}
}
MD5Util.java 加密
package com.foreknow.util;
import java.io.File;
import java.io.FileInputStream;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.security.MessageDigest;
/**
* MD5加密工具类
*/
public class MD5Util {
private static final char DIGITS[] = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
/**
* 获取文件的MD5码
*
* @param absPath
* 文件路径
* @return 文件的MD5码
*/
public final static String getFileMD5(String absPath) {
try {
File file = new File(absPath);
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
FileInputStream fis = new FileInputStream(file);
FileChannel filechannel = fis.getChannel();
MappedByteBuffer mbb = filechannel
.map(FileChannel.MapMode.READ_ONLY, 0, file.length());
mdTemp.update(mbb);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = DIGITS[byte0 >>> 4 & 0xf];
str[k++] = DIGITS[byte0 & 0xf];
}
fis.close();
return new String(str);
} catch (Exception e) {
return "";
}
}
/**
* 获取指定字符串的MD5码
*
* @param s
* 字符串
* @return 字符串的MD5码
*/
public final static String getMD5(String s) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f' };
try {
byte[] strTemp = s.getBytes();
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(strTemp);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
public static void main(String[] args) {
System.out.println(getMD5("admin"));
}
}
FileUtil.java 对文件的处理 获取 删除 文件的保存 上传
package com.foreknow.util;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
public class FileUtil {
/**
* 将MultipartFile保存到指定的路径下
*
* @param file
* Spring的MultipartFile对象
* @param savePath
* 保存路径
* @return 保存的文件名,当返回NULL时为保存失败。
* @throws IOException
* @throws IllegalStateException
*/
public static String save(MultipartFile file, String savePath) throws IllegalStateException, IOException {
if (file != null && file.getSize() > 0) {
File fileFolder = new File(savePath);
if (!fileFolder.exists()) {
fileFolder.mkdirs();
}
File saveFile = getFile(savePath, file.getOriginalFilename());
file.transferTo(saveFile);
return saveFile.getName();
}
return null;
}
/**
* 删除文件
*
* @param filePath
* 文件路径
* @return 是否删除成功:true-删除成功,false-删除失败
*/
public static boolean delete(String filePath) {
File file = new File(filePath);
if (file.isFile()) {
file.delete();
return true;
}
return false;
}
private static File getFile(String savePath, String originalFilename) {
String fileName = System.currentTimeMillis() + "_" + originalFilename;
File file = new File(savePath + fileName);
if (file.exists()) {
return getFile(savePath, originalFilename);
}
return file;
}
}
CommonUtil.java
package com.foreknow.util;
import java.util.UUID;
/**
* 共通工具类.
*/
public class CommonUtil {
/**
* 方法描述:判断一个字符串是否为null或空字符串(被trim后为空字符串的也算)。
*
* @param str
* 需要判断的字符串
* @return false:不是空字符串,true:是空字符串
*/
public static boolean isEmpty(String str) {
if (str == null || "".equals(str.trim())) {
return true;
}
return false;
}
/**
* 生成指定位数的随机整数
*
* @param number
* 位数
* @return 随机整数
*/
public static int random(int number) {
return (int) ((Math.random() * 9 + 1) * Math.pow(10, number - 1));
}
/**
* 获取UUID
*
* @return UUID
*/
public static String getUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
// /**
// * 判断session中存放的动作dto列表中是否包含指定的url
// * @param session
// * @param url
// * @param method http动作
// * @return true:包含,false:不包含
// */
// public static boolean contains(HttpSession session,String url,String method) {
// Object obj = session.getAttribute(SessionKeyConst.ACTION_INFO);
// if(obj != null) {
// @SuppressWarnings("unchecked")
// List<ActionDto> dtoList = (List<ActionDto>)obj;
// for(ActionDto actionDto : dtoList) {
// if(!isEmpty(actionDto.getMethod()) && !actionDto.getMethod().equals(method)) {
// continue;
// }
// if(!url.matches(actionDto.getUrl())) {
// continue;
// }
// return true;
// }
// }
// return false;
// }
}
然后在bean包下 创建BaseBean.java 专门给分页使用 让Ad也继承这个类 因为只要是查询就涉及分页 就是查询需要分页 别的不需要分页 所以创建一个
##让其每一个bean 都继承BaseBean.java
package com.foreknow.bean;
import com.foreknow.util.Page;
/**
* 分页的基础bean
* @author ttc
*
*/
public class BaseBean {
//引入工具类 Page的接口
private Page page;
public BaseBean() {
this.page = new Page();
}
public Page getPage() {
return page;
}
public void setPage(Page page) {
this.page = page;
}
}
然后 实现 我们的自定义拦截器 mybatis
com.foreknow.dao.interceptor包下 PageInterceptor.java
package com.foreknow.dao.interceptor;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import com.foreknow.bean.BaseBean;
import com.foreknow.util.Page;
/**
* 自定义拦截器
* 获取的Mybatis的Statement是在StatementHandler这个对象中进行的 所以我们
* type=StatementHandler.class
* method="prepare" 实现接口的 prepare 方法
* @Intercepts:标识当前的类是一个拦截器
* @Signature(type=StatementHandler.class,method="prepare",args={Connection.class})
* 它标记了当前的拦截器只会拦截StatementHandler接口中的prepare方法,因为这个方法的的参数是Connection类型的,
* 所以获取到这个方法 传参数需要使用args={Connection.class}指定参数的类型
* @author ttc
*
*/
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
public class PageInterceptor implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
// 获取目标对象
StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
// 获取到元数据对象,从这个对象中可以通过其中的方法获取到我们要处理(拦截)的操作
MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
// 根据KEY获取到映射对象MappedStatement---------------AdDao.xml
MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
// 获取到要AdDao.xml里的ID拦截操作的ID
String id = mappedStatement.getId();
//一般uerdao.xml中 分页都是后面带ByPage的 所以下面判断一下
if (id.endsWith("ByPage")) {
BoundSql boundSql = statementHandler.getBoundSql();
// 获取到Mapper.xml中的SQL
String sql = boundSql.getSql();
// 计算总的记录数 t是别名
String countSql = "select count(*) from(" + sql + ")t";
// 获取到Statement prepare(Connection connection)
Connection conn = (Connection) invocation.getArgs()[0];
PreparedStatement statement = conn.prepareStatement(countSql);
// 通过代理获取到ParameterHandler对象,目的是要将?替换为具体值
ParameterHandler parameterHandler = (ParameterHandler) metaObject.getValue("delegate.parameterHandler");
parameterHandler.setParameters(statement);
// JDBC的执行查询
ResultSet rs = statement.executeQuery();
BaseBean bean = (BaseBean) boundSql.getParameterObject();
Page page = bean.getPage();
if (rs.next()) {
page.setTotalNumber(rs.getInt(1));
}
// 通过上面的分析分页的格式:
// select * from table limit (start-1)*limit,limit;
// 其中start是页码,limit是每页显示的条数。
String pageSql = sql + " limit "+(page.getCurrentPage()-1)*page.getPageNumber()+","+page.getPageNumber();
//重写分页的SQL 因为以前的 adDAO.xml里的sql不带分页 所以要重写 delegate.boundSql.sql 就这么写 写死的
metaObject.setValue("delegate.boundSql.sql", pageSql);
}
//将执行权交给下一个拦截器
return invocation.proceed();
}
public Object plugin(Object target) {
// TODO Auto-generated method stub
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
// TODO Auto-generated method stub
}
}
##分页的步骤:
首先自己定义的拦截器
- 列表第一项1.通过MappedStatement 里的方法 获取adDao.xml 依赖注入里的id
- 列表第一项2.因为id有多个 判断一下 page结尾的id
- 列表第一项3.获取到sql 后 记录总记录数
- 列表第一项4.对JDBC的操作 设置connection 参数后 执行查询
- 列表第一项5. 拼SQL
- 列表第一项6.重写SQL
- 列表第一项7.如果有下个拦截器 交给下一个 如果没有 就交给conntroller处理
然后在JSP中建一个自定义标签文件 tags
对应依赖的库 tag 复制到pom.xml中
#####定义一个自定义标签tag 在WEB-INF 下
<%@ tag language="java" pageEncoding="utf-8"%>
<!-- 获取页面的一些信息 引用一下
Page" name="page" 页数 和总记录数
获取属性 用attribute -->
<%@ attribute type="com.foreknow.util.Page" name="page" required="true"%>
<%@ attribute type="java.lang.String" name="jsMethodName" required="true"%>
<script type="text/javascript">
/* 传一个当前的页码 参数 */
function transCurrenPage(currentPage) {
var rule = /^[0-9]*[1-9][0-9]*$/
if(!rule.test(currentPage)) {
currentPage = 1;
}
eval("${jsMethodName}(currentPage)");
}
</script>
<div class="page fix">
<a href="javascript:transCurrenPage('1')" class="first">首页</a>
<a href="javascript:transCurrenPage('${page.currentPage-1}')" class="pre">上一页</a>
当前第<span>${page.currentPage}/${page.totalPage}</span>页
<a href="javascript:transCurrenPage('${page.currentPage+1}')" class="next">下一页</a>
<a href="javascript:transCurrenPage('${page.totalPage}')" class="last">末页</a>
跳转 <input type="text" id="currentPageText" value="1" class="allInput w28"> 页
<a href="javascript:transCurrenPage($('#currentPageText').val())" class="go">GO</a>
</div>
如果不想鼠标放上去 就显示url 为了网页的安全 可以写js
#####问题1:
引用的<%@ attribute type=“java.lang.String” name=“jsMethodName” required=“true”%> 什么意思?
解答:我们引用的是这个自定义的标签 那么 我们就应该把自定义标签定义一个名字
ame=“jsMethodName
#####问题2:
function transCurrenPage(currentPage) {
var rule = /1[1-9][0-9]
{jsMethodName}(currentPage)”);
}
解答: 这里我们运用的是一个正则表达式 判断一下 如果不符合这个规则 那么当前
当前页显示的是第一页
eval("${jsMethodName}(currentPage)");
意思说通过方法eval 把实际点击的这个当前页数传到自定义标签上 来解析这个jsMethodName 对应的JS 方法 search
adList.js
//显示信息
$(function(){
common.showMessage($("#message").val())
})
//这个方法什么时候调用?
function search(currentPage){
$("#currentPage").val(currentPage);
$("#mainForm").submit();
}
这个#currentPage 定义的在JSP页面中的ID 对应的隐藏域 value 对应的是1页 也就说 默认是从第一页开始的 , 让后id=mainForm 是action里的 表单提交
adList.jsp
<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="pager" tagdir="/WEB-INF/tags/" %>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9; IE=8; IE=7; IE=EDGE"/>
<title></title>
<link rel="stylesheet" type="text/css" href="${basePath}/css/all.css"/>
<link rel="stylesheet" type="text/css" href="${basePath}/css/pop.css"/>
<link rel="stylesheet" type="text/css" href="${basePath}/css/main.css"/>
<script type="text/javascript" src="${basePath}/js/common/jquery-1.8.3.js"></script>
<script type="text/javascript" src="${basePath}/js/common/common.js"></script>
<script type="text/javascript" src="${basePath}/js/content/adList.js"></script>
</head>
<body style="background: #e1e9eb;">
<form action="${basePath}/ad/search" id="mainForm" method="post">
<input type="hidden" id="id" name="id"/>
<input type="hidden" id="message" value="${pageCode.msg}"/>
<input type="hidden" id="basePath" value="${basePath}"/>
<input type="hidden" name="page.currentPage" id="currentPage" value="1"/>
<div class="right">
<div class="current">当前位置:<a href="#">内容管理</a> > 广告管理</div>
<div class="rightCont">
<p class="g_title fix">广告列表</p>
<table class="tab1">
<tbody>
<tr>
<td align="right" width="80">标题:</td>
<td>
<input name="title" id="title" value="" class="allInput" type="text"/>
</td>
<td style="text-align: right;" width="150">
<input class="tabSub" value="查询" onclick="search('1');" type="button"/>
<%--<t:auth url="/ad/addInit">--%>
<input class="tabSub" value="添加" onclick="location.href='${basePath}/ad/addInit'" type="button"/>
<%--</t:auth>--%>
</td>
</tr>
</tbody>
</table>
<div class="zixun fix">
<table class="tab2" width="100%">
<tbody>
<tr>
<th>序号</th>
<th>标题</th>
<th>链接地址</th>
<th>操作</th>
</tr>
<c:forEach items="${list}" var="item" varStatus="s">
<tr>
<td>${s.index+1}</td>
<td>${item.title}</td>
<td>${item.link}</td>
<td>
<%--<t:auth url="/ad/modifyInit">--%>
<a href="javascript:void(0);" onclick="">修改</a>
<%--</t:auth>--%>
<%--<t:auth url="/ad/remove">--%>
<a href="javascript:void(0);" onclick="">删除</a>
<%--</t:auth>--%>
</td>
</tr>
</c:forEach>
</tbody>
</table>
<pager:page jsMethodName="search" page="${searchParam.page}"></pager:page>
</div>
</div>
</div>
</form>
</body>
</html>
其中
引用一下这个page
<%@ taglib prefix=“pager” tagdir="/WEB-INF/tags/" %>
就是引用的分页
<pager:page jsMethodName=“search” page="${searchParam.page}"></pager:page>
问题3
jsMethodName=“search” page="${searchParam.page} 是什么意思?
解答:因为在自定义标签中 有两个参数
所以 在jsMethodName=“search” 让他调用JS 函数提交
第二: searchParam是控制器的addAttribute Map集合里的Key
adAdd.js
adList.js
所以 这就实现了分页的功能
0-9 ↩︎