美文网首页互联网技术IT交流圈
SSM框架-实现Mybatis分页功能-foreknow_cms

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

作者: Java成长记_Camel | 来源:发表于2019-01-21 22:04 被阅读5次

分页处理

分页
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 = /^[0-9][1-9][0-9]/ if(!rule.test(currentPage)) { currentPage = 1; } eval("{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

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

相关文章

网友评论

    本文标题:SSM框架-实现Mybatis分页功能-foreknow_cms

    本文链接:https://www.haomeiwen.com/subject/lppldqtx.html