SSM框架-实现Mybatis分页功能-foreknow_cms

##分页处理

分页
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

image.png

对应依赖的库 tag 复制到pom.xml中
image.png

#####定义一个自定义标签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>
	跳转&nbsp;<input type="text" id="currentPageText" value="1" class="allInput w28">&nbsp;&nbsp;
	<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] / i f ( ! r u l e . t e s t ( c u r r e n t P a g e ) ) c u r r e n t P a g e = 1 ; e v a l ( &quot; / if(!rule.test(currentPage)) { currentPage = 1; } eval(&quot; {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> &gt; 广告管理</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"/>&nbsp;&nbsp;&nbsp;&nbsp;
						<%--<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>&nbsp;&nbsp;&nbsp;&nbsp;
								<%--</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} 是什么意思?
解答:因为在自定义标签中 有两个参数
image.png

所以 在jsMethodName=“search” 让他调用JS 函数提交
第二: searchParam是控制器的addAttribute Map集合里的Key

adAdd.js

adList.js

image.png

所以 这就实现了分页的功能


  1. 0-9 ↩︎

猜你喜欢

转载自blog.csdn.net/qq_30225725/article/details/86656029