JSP+MYSQL分页这么弄?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JSP+MYSQL分页这么弄?相关的知识,希望对你有一定的参考价值。

参考技术A 分类: 电脑/网络 >> 软件
问题描述:

我制作的是留言版,回复时得弄分页,但是不知道分页怎么弄,网上的代码没有注释,也看不懂。

请各位大哥大姐们一定要帮帮我,后面加上注释,谢谢!

注意:我不用JavaBean写,就用前台写。

解析:

作为参考:

<%@ page contentType="text/;charset=8859_1" %>

<%

变量声明

java.sql.Connection sqlCon; 数据库连接对象

java.sql.Statement sqlStmt; SQL语句对象

java.sql.ResultSet sqlRst; 结果集对象

javang.String strCon; 数据库连接字符串

javang.String strSQL; SQL语句

int intPageSize; 一页显示的记录数

int intRowCount; 记录总数

int intPageCount; 总页数

int intPage; 待显示页码

javang.String strPage;

int i;

设置一页显示的记录数

intPageSize = 2;

取得待显示页码

strPage = request.getParameter("page");

if(strPage==null)表明在QueryString中没有page这一个参数,此时显示第一页数据

intPage = 1;



else将字符串转换成整型

intPage = javang.Integer.parseInt(strPage);

if(intPage<1) intPage = 1;



装载JDBC驱动程序

java.sql.DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());

设置数据库连接字符串

strCon = "jdbc:oracle:thin:@linux:1521:ora4cweb";

连接数据库

sqlCon = java.sql.DriverManager.getConnection(strCon,"hzq","hzq");

创建一个可以滚动的只读的SQL语句对象

sqlStmt = sqlCon.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY);

准备SQL语句

strSQL = "select name,age from test";

执行SQL语句并获取结果集

sqlRst = sqlStmt.executeQuery(strSQL);

获取记录总数

sqlRstst();

intRowCount = sqlRst.getRow();

记算总页数

intPageCount = (intRowCount+intPageSize-1) / intPageSize;

调整待显示的页码

if(intPage>intPageCount) intPage = intPageCount;

%>

<>

<head>

<meta -equiv="Content-Type" content="text/; charset=gb2312">

<title>JSP数据库操作例程 - 数据分页显示 - JDBC 2.0 - Oracle</title>

</head>

<body>

<table border=1 cellspacing="0" cellpadding="0">

<tr>

<th>姓名</th>

<th>年龄</th>

</tr>

<%

if(intPageCount>0)

将记录指针定位到待显示页的第一条记录上

sqlRst.absolute((intPage-1) * intPageSize + 1);

显示数据

i = 0;

while(i<intPageSize && !sqlRst.isAfterLast())

%>

<tr>

<td><%=sqlRst.getString(1)%></td>

<td><%=sqlRst.getString(2)%></td>

</tr>

<%

sqlRst.next();

i++;





%>

</table>

第<%=intPage%>页 共<%=intPageCount%>页 <%if(intPage<intPageCount)%><a href="jdbc20-oracle.jsp?page=<%=intPage+1%>">下一页</a><%%> <%if(intPage>1)%><a href="jdbc20-oracle.jsp?page=<%=intPage-1%>">上一页</a><%%>

</body>

</>

<%

关闭结果集

sqlRst.close();

关闭SQL语句对象

sqlStmt.close();

关闭数据库

sqlCon.close();

%>

可以试试先!

祝你好运!

----------------------------------

也可以用jsp+xml+来实现,下面给出一个saucer(思归)给的xml+的分页例子,不妨参考一下:

<>

<body>

<!--the following XML document is "stolen" from MSXML4 documentation-->

<xml id="xmldoc">

<catalog>

<book id="bk101">

<author>Gambardella, Matthew</author>

<title>XML Developer's Guide</title>

<genre>Computer</genre>

<price>44.95</price>

<publish_date>2000-10-01</publish_date>

<description>An in-depth look at creating applications

with XML.</description>

</book>

<book id="bk102">

<author>Ralls, Kim</author>

<title>Midnight Rain</title>

<genre>Fantasy</genre>

<price>5.95</price>

<publish_date>2000-12-16</publish_date>

<description>A former architect battles corporate zombies,

an evil sorceress, and her own childhood to bee queen

of the world.</description>

</book>

<book id="bk103">

<author>Corets, Eva</author>

<title>Maeve Ascendant</title>

<genre>Fantasy</genre>

<price>5.95</price>

<publish_date>2000-11-17</publish_date>

<description>After the collapse of a nanotechnology

society in England, the young survivors lay the

foundation for a new society.</description>

</book>

<book id="bk104">

<author>Corets, Eva</author>

<title>Oberon's Legacy</title>

<genre>Fantasy</genre>

<price>5.95</price>

<publish_date>2001-03-10</publish_date>

<description>In post-apocalypse England, the mysterious

agent known only as Oberon helps to create a new life

for the inhabitants of London. Sequel to Maeve

Ascendant.</description>

</book>

<book id="bk105">

<author>Corets, Eva</author>

<title>The Sundered Grail</title>

<genre>Fantasy</genre>

<price>5.95</price>

<publish_date>2001-09-10</publish_date>

<description>The o daughters of Maeve, half-sisters,

battle one another for control of England. Sequel to

Oberon's Legacy.</description>

</book>

<book id="bk106">

<author>Randall, Cynthia</author>

<title>Lover Birds</title>

<genre>Romance</genre>

<price>4.95</price>

<publish_date>2000-09-02</publish_date>

<description>When Carla meets Paul at an ornithology

conference, tempers fly as feathers get ruffled.</description>

</book>

<book id="bk107">

<author>Thurman, Paula</author>

<title>Splish Splash</title>

<genre>Romance</genre>

<price>4.95</price>

<publish_date>2000-11-02</publish_date>

<description>A deep sea diver finds true love enty

thousand leagues beneath the sea.</description>

</book>

<book id="bk108">

<author>Knorr, Stefan</author>

<title>Creepy Crawlies</title>

<genre>Horror</genre>

<price>4.95</price>

<publish_date>2000-12-06</publish_date>

<description>An anthology of horror stories about roaches,

centipedes, scorpions and other insects.</description>

</book>

</catalog>

</xml>

<table id="mytable" datasrc="#xmldoc" border=1 DATAPAGESIZE="2">

<thead><th>Title</th><th>Author</th><th>Genre</th><th>Publish Date</th><th>Price</th></thead>

<tbody><tr>

<td><span datafld="title"></span></td>

<td><span datafld="author"></span></td>

<td><span datafld="genre"></span></td>

<td><span datafld="publish_date"></span></td>

<td><span datafld="price"></span></td>

</tr>

</tbody>

</table>

<input type=button value="previous page" onclick="mytable.previousPage()">

<input type=button value="next page" onclick="mytable.nextPage()">

</body>

</>

------------------------------------

分页显示的模板程序

<!--show_page.jsp-->

<%@ page import="javang.*" import="java.sql.*" import="java.util.*" contentType="text/;charset=GB2312"%>

<%@ page import="tax.*"%>

<jsp:useBean id="RegisterBean" class="tax.RegisterBean" scope="page"/>

<jsp:useBean id="itemlist" class="tax.itemlist" scope="page"/>

<%

int PageSize = 10;设置一页显示的记录数

int PageNum = 1; 初始化页码=1

int PageNumCount = (136+PageSize-1) / PageSize;记算总页数

计算要显示的页码

String strPageNum = request.getParameter("page");取得<href>提交的页码

if(strPageNum==null) 表明在QueryString中没有page这一个参数,此时显示第一页数据

PageNum = 1;



else

PageNum = javang.Integer.parseInt(strPageNum);将字符串转换成整型

if(PageNum<1) PageNum = 1;



if(PageNum>PageNumCount) PageNum = PageNumCount;调整待显示的页码

%>

<>

<head>

<meta -equiv="Content-Type" content="text/; charset=gb2312">

<title>JSP例程 - 数据分页显示 -JDK1.2 </title>

</head>

<body>

<%

if(PageNumCount>0)

out.println(PageNum);显示数据,此处只简单的显示页数



/*需要显示的数据,在此处显示

、、、

例如:

*/

显示一个简单的表格

%>

<table border=1 cellspacing="0" cellpadding="0">

<tr>

<th>总数</th>

<th>页数</th>

</tr>

<tr>

<th><%=PageNumCount%></th>

<th><%=PageNum%></th>

</tr>

</table>

第<%=PageNum%>页 共<%=PageNumCount%>页

<%if(PageNum<PageNumCount)%><a href="show_page.jsp?page=<%=PageNum+1%>">下一页</a><%%>

<%if(PageNum>1)%><a href="show_page?page=<%=PageNum-1%>">上一页</a><%%>

</body>

</>

---------------------------------

一个bean,按照文档说的用。也希望你给出修改意见。

package mshtang;

/**

* <p>Title: DataBaseQuery</p>

* <p>Description: 用于数据库翻页查询操作</p>

* <p>Copyright: 厦门一方软件公司版权所有Copyright (c) 2002</p>

* <p>Company: 厦门一方软件公司</p>

* @author 小唐蔡

* @version 1.0

*/

import java.sql.*;

import javax.servlet..*;

import java.util.*;

import mshtang.StringAction;

public class DataBaseQuery



private HttpServletRequest request;

private StringAction S;

private String sql;

private String userPara;

private String[][] resultArray;

private String[] columnNameArray;

private String[] columnTypeArray;

private int pageSize;

private int columnCount;

private int currentPageNum;

private int currentPageRecordNum;

private int totalPages;

private int pageStartRecord;

private int totalRecord;

private static boolean initSuccessful;

private String currentJSPPageName;

private String displayMessage;

public DataBaseQuery()



S = new StringAction();

sql = "";

pageSize = 10;

totalRecord = 0;

initSuccessful = false;

currentJSPPageName = "";

displayMessage = "";

columnNameArray = null;

columnTypeArray = null;

currentPageRecordNum = 0;

columnCount = 0;



/**功能:数据库初始化操作,其它操作的前提。

*

* @param conn:数据库连接;

* @param request:jsp页面request对象;

* @param querySQL:查询语句;

* @param pageSize:每页显示记录数;

* @param startPageNum:开始显示页码

*/

public void init(Connection conn, HttpServletRequest request, String querySQL, int pageSize, int startPageNum)



if(conn != null)



this.request = request;

this.sql = request.getParameter("querySQL");

this.userPara = request.getParameter("userPara");

if(sql == null || sql.equals(""))



sql = querySQL;



if(this.userPara == null)



this.userPara = "";



if(S.isContains(sql, "select;from", ";", true))



try



Statement st = conn.createStatement();

ResultSet rs = st.executeQuery(sql);

ResultSetMetaData r *** d = rs.getMetaData();

columnCount = r *** d.getColumnCount();

columnNameArray = new String[columnCount];

columnTypeArray = new String[columnCount];

String columnName;

String value;

while(rs.next())



totalRecord++;

if(totalRecord == 1)



for(int i = 0; i < columnCount; i++)



columnNameArray[i] = r *** d.getColumnName(i + 1);

columnTypeArray[i] = r *** d.getColumnTypeName(i + 1);







rs.close();

在总记录数大于0的情况下进行下列操作

获取链接图象

if(totalRecord > 0 && pageSize > 0 && columnCount > 0 && startPageNum > 0)



获取总页数

totalPages = totalRecord / pageSize;

int tempNum = totalRecord % pageSize;

if(tempNum != 0)



totalPages++;



获得当前页页码

String currentPage = request.getParameter("currentPageNum");

currentPageNum = (currentPage == null || currentPage.equals(""))? startPageNum:Integer.parseInt(currentPage);

currentPageNum = (currentPageNum > totalPages)?totalPages:currentPageNum;

currentPageNum = (currentPageNum <= 0)?1:currentPageNum;

获得当前页起始显示记录数

pageStartRecord = (currentPageNum - 1) * pageSize + 1;

pageStartRecord = (pageStartRecord <= 0)?1:pageStartRecord;

pageStartRecord = (pageStartRecord > totalRecord)?totalRecord:pageStartRecord;

获得当前页显示记录数

if(currentPageNum * pageSize > totalRecord)



currentPageRecordNum = totalRecord - (currentPageNum - 1) * pageSize;



else



currentPageRecordNum = pageSize;



resultArray = new String[currentPageRecordNum][columnCount];

用于跳过前面不需显示的记录

int continueRowNum = 0;

用于跳过后面不再显示的记录

int breakRowNum = 0;

ResultSet rs2 = st.executeQuery(sql);

while(rs2.next())



跳过前面不需显示的记录

continueRowNum++;

if(continueRowNum < pageStartRecord)



continue;



存取当前页需显示的记录到二维数组

for(int i = 0; i < columnCount; i++)



value = rs2.getString(columnNameArray[i]);

value = (value == null)?"":value.trim();

resultArray[breakRowNum][i] = value;



跳过后面不再显示的记录

breakRowNum++;

if(breakRowNum >= currentPageRecordNum)



break;





rs2.close();



st.close();



catch(SQLException e)



e.printStackTrace();





transferSQL(sql);

initSuccessful = true;





/**功能:数据库初始化操作,其它操作的前提,默认每页显示10条记录。

*

* @param conn:数据库连接;

* @param request:jsp页面request对象;

* @param querySQL:查询语句;

* @param startPageNum:开始显示页码

*/

public void init(Connection conn, HttpServletRequest request, String querySQL, int startPageNum)



init(conn, request, querySQL, 10, startPageNum);



/**功能:数据库初始化操作,其它操作的前提,默认从第一页开始显示。

*

* @param conn:数据库连接;

* @param request:jsp页面request对象;

* @param querySQL:查询语句;

* @param pageSize:每页显示记录数;

*/

public void init(Connection conn, HttpServletRequest request, int pageSize, String querySQL)



init(conn, request, querySQL, pageSize, 1);



/**功能:数据库初始化操作,其它操作的前提,默认从第一页开始显示,每页显示10条记录。

*

* @param conn:数据库连接;

* @param request:jsp页面request对象;

* @param querySQL:查询语句;

*/

public void init(Connection conn, HttpServletRequest request, String querySQL)



init(conn, request, querySQL, 10, 1);



/**功能:给出没有初始化的提醒信息,内部调用。

*

*/

private static void getMessage()



if(!initSuccessful)



System.out.println("没有完成初始化");





/**功能:得到查询结果的总记录数。

*

* @return

*/

public int getTotalRecord()



getMessage();

return totalRecord;



/**功能:得到当前页的页码

*

* @return

*/

public int getCurrentPageNum()



getMessage();

return currentPageNum;



/**功能:获得当前页记录数

*

* @return

*/

public int getCurrentPageRecord()



getMessage();

return currentPageRecordNum;



/**功能:获得总页数

*

* @return

*/

public int getTotalPages()



getMessage();

return totalPages;



/**获得调用该javaBean的jsp页面文件名,用于翻页操作,可以免去外界输入页面参数的错误,用于内部调用。

*

* @return:调用该javaBean的jsp页面文件名

*/

private String getCurrentJSPPageName()



getMessage();

if(request != null)



String tempPage = request.getRequestURI();

String[] tempArray = S.stringSplit(tempPage, "/");

if(tempArray != null && tempArray.length > 0)



currentJSPPageName = tempArray[tempArray.length - 1];





return currentJSPPageName;



/**功能:用于显示图片链接或字符串(上一页、下一页等链接)。用于翻页操作,内部调用

*

* @param imageSource:图片来源;

* @param i:翻页信息,1表示第一页,2表示上一页,3表示下一页,4表示尾页,

* @return:显示的链接图片或链接文字

*/

private void displayMessage(String imageSource, int i)



getMessage();

if(imageSource != null && !imageSource.equals(""))



displayMessage = "<img src=\"" + imageSource + "\" border=\"0\">";



else



switch(i)



case 1:

displayMessage = "<font size=\"2\">[首页]</font>";

break;

case 2:

displayMessage = "<font size=\"2\">[上一页]</font>";

break;

case 3:

displayMessage = "<font size=\"2\">[下一页]</font>";

break;

case 4:

displayMessage = "<font size=\"2\">[尾页]</font>";







/**功能:链接到相应页面,内部调用。

*

* @param imageSource:图片来源;

* @param i:翻页信息,1表示第一页,2表示上一页,3表示下一页,4表示尾页,

* @return:相应页面的链接

*/

private String getNavigation(String imageSource, int i)



displayMessage(imageSource, i);

int pageNum = 0;

switch(i)



case 1:

pageNum = 1;

break;

case 2:

pageNum = currentPageNum - 1;

break;

case 3:

pageNum = currentPageNum + 1;

break;

case 4:

pageNum = totalPages;



currentJSPPageName = "<a columnName, true);

if(resultArray != null && columnIndex != -1)



columnValue = resultArray[recordIndex][columnIndex];





return columnValue;



/**功能:方法重载。返回特定行特定列的值。

*

* @param recordIndex:行索引,从0开始;

* @param columnIndex:列索引,从1开始;

* @return

*/

public String g

MySQL 跨库分页/ 分表分页/ 跨库分页,为什么这么难?

点击关注公众号,Java干货及时送达

作者:菩提树下的杨过
出处:www.cnblogs.com/yjmyzz/p/12149737.html

当业务数据达到一定量级(比如:mysql单表记录量>1千万)后,通常会考虑“分库分表”将数据分散到不同的库或表中,这样可以大大提高读/写性能。

但是问题来了,对于 select * from table limit offset , pagesize 这种分页方式,原来一条语句就可以简单搞定的事情会变得很复杂,本文将与大家一起探讨分库分表后"分页"面临的新问题。

一、分表对分页的影响

比如有一张表,里面有8条记录(为简单起见,假设该表上只有1个自增ID),数学上可以抽象成1个(有序)数列(注:为方便讨论,不加特殊说明的情况下,文本中数列的顺序,均指升序)

(1,2,3,4,5,6,7,8)

如果要取出上面红色标识的2,3这二条记录,limit 1,2 就行了。

现在假如分成2张表(即:原来的数列,拆分成2个非空子数列),一般来讲,有二种常用分法:

1.1 分段法(比如:有时间属性的数据,类似订单这种,可以按下单时间拆分,每个月1张表)

(1,2,3,4)

(5,6,7,8)

沿用之前的limit x,y的思路,每个分表上 limit 1,2,会得到如下2个子数列:

(2,3)

(6,7)

然后在内存中合并排序,再取前2条 (2,3,6,7) => (2,3) ,貌似看上去也符合预期(这个思路也称为归并),但这只是假象。当要取的分页数据落在不同的子数列上时,就能发现问题:

(1,2,3,4,5,6,7,8) 

比如,我们要从4个位置开始,连续取2个元素,即: limit 3,2

(1,2,3,4) => limit 3,2 =>(4)

(5,6,7,8) => limit 3,2 =>(8)

最后合并出来的结果是(4,8) 与正确结果 (4,5)相比,显然不对。

1.2 模余均摊法(比如:字段值对2取模求余数,根据余数决定分到哪个表,该方法也简称为取余法)

(1,3,5,7)

(2,4,6,8)

归并排序的思路在分段法上行不通,对于取模均摊同样也不行,仍以 limit 1,2为例,原始序列取出来的结果是(2,3),如果用归并的思路:

(1,3,5,7)=> limit 1,2 =>(3 ,5)

(2,4,6,8)=> limit 1,2 =>(4, 6)

内存合并排序后,取前2个,最终结果为(3 , 4)

结论:不管分库分表采用什么分法,简单归并的思路,都无法正确解决分页问题。另外,MySQL 系列面试题和答案全部整理好了,微信搜索Java技术栈,在后台发送:面试,可以在线阅读。

二、全局法(limit x+y)

反思一下刚才的归并思路,本质上我们在每个子数列(即:分表)上limit x,y 时,取出来的数据就有可能已经产生缺失了。

推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice

网上有一篇广为流转的文章"业界难题-跨库分页”,作者在文中提出了一个方案:把范围扩大,分表sql上的limit x,y 变成 limit 0, x+y ,这样改写后,相当于分表中把"每页最后一条数据"之前的所有数据全都取出来了(当然:这里面可能会有不需要的多余数据),然后内存中合并在一起,再取x偏移量后的y条数据。

用前面的例子验证一下:

原序列:(1,2,3,4,5,6,7,8),需要取出limit 1,2 ,即:(2,3)

2.1 按分段法拆成2段:

(1 , 2 , 3 , 4) => limit 1,2 =>改写成 limit 0, 1+2 => (1,2,3)

(5 , 6 , 7 , 8) => limit 1,2 =>改写成 limit 0, 1+2 => (5,6,7)

将子数列合并排序=> { 1,2,3,5,6,7} => 按原始偏移量 limit 1,2 =>{2,3} 正确

如果原数列中要取的数据,正好落在2个子数列上(1,2,3,4,5,6,7,8),需要取出limit 3,2 ,即:(4,5)

(1 , 2 , 3 , 4) => limit 3,2 =>改写成 limit 0, 3+2 => (1,2,3,4)

(5 , 6 , 7 , 8) => limit 3,2 =>改写成 limit 0, 3+2 => (5,6,7,8)

将子数列合并排序=> (1,2,3,4,5,6,7,8) => 按原始偏移量 limit 3,2 => (4,5) 也符合预期。

2.2 取模均摊拆成2段

(1,3,5,7) => limit 1,2 ->改写成 limit 0, 1+2 => (1,3 ,5)

(2,4,6,8) => limit 1,2 ->改写成 limit 0, 1+2=> (2,4,6)

将子序列合并=> (1,2,3,4,5,6) => 按原始偏移量 limit 1,2 =>(2,3) 正确

该方法缺点也很明显:取出的记录太多了,比如 limit 10000000,10 -> 改写后变成 limit 0, 10000010 遇到海量数据,mysql中查询有可能直接超时,这么多数据从db传到应用层,网络开销也很大,更不用说如果是java应用,大量数据放到List或Map中,容易出现OOM。

注:一般情况下,需要用分库分表的场景,数据量必然很大,所以这个方法,实际中基本上没法用。

三、二次查询法

这也是"业界难题-跨库分页”一文中提到的一个方法,大致思路如下:在某1页的数据均摊到各分表的前提下(注:这个前提很重要,也就是说不会有一个分表的数据特别多或特别少),换句话说:这个方案不适用分段法,按如下步骤操作:

1)原sql中的limit offset,pagesize 改写成 limit offset/n ,pagesize (注:n为分表个数,如果offset/n除不尽,向下取整,避免最后的结果丢数据)-- 这个的意思,其实就是假设原表这一页的数据,会均分到各个分表(所以,我一再强调,前提是数据是均摊的,如果某个分表的记录很少,极端情况下,甚至是空的,这个就不对了,最终结果会少数据)

2)分表上,执行改写后的sql,得到一堆结果集,然后找出这堆结果中的最小id (假设id是关键的排序字段),记为min_id -- 这一步的目的,是为了找出最小的起始点,保证第1页数据起点正确。

3)各分表上的sql,where条件部分改写成 id between min_id and origin_max_id (注:origin_max_id为上一步,每个分表查询结果集中的最大值,显然min_id=自身最小id的那张分表,不用再重复查询) -- 这一步的目的在于,因为步骤1)查出来的结果,通常会比原表上该页的数据少,所以这里重新将起始点设置到正确的位置,即:min_id,再查1次,相当于范围扩大了,以保证数据不会丢。不过,这里有一个可优化的地方,仔细想想,这1次查询出来的结果,跟步骤1)中的查出来的结果,必然有一部分是重复的,因此改写部分,只需要 id between min_id and origin_min_id就可以了(origin_min_id 即为原来分表结果上的最小id)

4)将上一步查询出来的结果,在内存中合并排序去重(注:如果上一步采用了优化方案,就应该是把1)与3)这二次查询的结果全取出来合并排序去重),然后从开始连续取pagesize条数据即可(注:offset/n除不尽的话,向下取整了,也就是起始点可能向前多移了,所以有可能开始的第1条记录,其实是上1页的最后1条记录,要追求精确的话,可以在应用层记录上一页最后1条记录的id,然后跟本次查询结果前1条记录对比,如果发现是一样的,开始取数据的位置,就要向后移1位,如果考虑id有重复的话,就要根据情况多移几位)

点击关注公众号,Java干货及时送达

验证一下看看效果:

场景1(前提:取余法)

原序列:(1,2,3,4,5,6,7,8),需要取出limit 2,2 ,即:(3,4)

第1次查询

(1,3,5,7) -> limit 2,2 -> 改写成 limit 1,2 -> (3,5)

(2,4,6,8) -> limit 2,2 -> 改写成 limit 1,2 -> (4,6)

最小值为3

第2次查询

(1,3,5,7) -> between 3 and 5 -> (3,5)

(2,4,6,8) -> between 3 and 6 -> (4,6)

将第2次查询的结果合并:

(3,4,5,6) ->取头开始,取pageSize即2个元素 -> (3,4) 正确

-----------------------------------------------------

场景2(前提:取余法)

原序列:(1,2,3,4,5,6,7,8),需要取出limit 1,2 ,即:(2,3)

第1次查询

(1,3,5,7) -> limit 1,2 -> 改写成 limit 0,2 -> (1,3) --注:因为1/2除不尽,这里向下取整了

(2,4,6,8) -> limit 1,2 -> 改写成 limit 0,2 -> (2,4)

最小值为1

第2次查询

(1,3,5,7) -> between 1 and 3 -> (1,3)

(2,4,6,8) -> between 1 and 4 -> (2,4)

将上面的结果合并:

(1,2,3,4) -> (2,3) (注:起始点,第1次查询改写时,向下取整了,所以这里要向移1位,从第2个数字开始取pagesize条数据)

--------------------------------------------------------

场景3(前提:分段法)

为什么说分段法,这个方案不适用,可以看下面的分析:

原序列:(1,2,3,4,5,6,7,8),需要取出limit 2,2 ,即:(3,4)

第1次查询

(1,2,3,4) -> limit 2,2 -> limit 1,2 -> {2,3} --注:这里就已经把正确的数据给丢掉了

(5,6,7,8) -> limit 2,2 -> limit 1,2 -> {5,6} --注:这一段里根本就没有这一页的数据

最小值2

第2次查询

(1,2,3,4) -> between 2 and 3 -> {2,3}

(5,6,7,8) -> between 2 and 6 -> {5,6}

(2,3,5,6) -> (2,3) 这个跟预期结果就对不上了。

-------------------------------------------------------

场景4(前提:取余法)

取余法的前提下,如果某个分表的数据,被清掉一部分,也就是某个分表数据偏少,会发生什么?

比如下面这个,按奇数、偶数分成2个子序列,但是偶数故意删除了几个(相当于现实业务中,这个分表上的数据被干掉了一部分)

原序列:(1,3,5,6,7,8,9,11),需要取出limit 2,2 ,即:(5,6)

第1次查询

(1,3,5,7,9,11) -> limit 2,2 -> 改写成limit 1,2 -> (3,5)

(6,8) -> limit 2,2 -> 改写成limit 1,2 -> (8)

第2次查询

(1,3,5,7,9,11) -> between 3 and 5 -> (3,5)

(6,8) -> between 3 and 8 -> (6,8)

合并后

(3,5,6,8) -> (3,5) 这跟预期结果也对不上。

四、禁止跳页

相当于只允许向上或向下翻页,原理很简单,比如:下一页,先记录上一页的最大id,然后下一页时,只需要 where id > 上一页最大id limit pagesize, 每次只翻1页。显然,这是一个牺牲用户体验的做法。

结论:

分表分页不存在一个通用的解决方案,要么性能有问题(比如:全局法 limit x+y),要么必须具备一定的前提条件(比如:二次查询),或者产品设计上牺牲用户体验,仍然是一个业内难题。另外,关注公众号Java技术栈,在后台回复:面试,可以获取我整理的 MySQL 系列面试题和答案,非常齐全。

参考文章:

https://juejin.im/post/5d1f52e46fb9a07eb3099bbf

https://shardingsphere.apache.org/document/current/cn/features/sharding/use-norms/pagination/

https://stackoverflow.com/questions/3927537/how-do-you-implement-sorting-and-paging-on-distributed-data

http://kmiku7.github.io/2019/08/01/Do-Pagination-With-Table-Database-Sharding/

https://segmentfault.com/a/1190000013225860

https://mp.weixin.qq.com/s/h99sXP4mvVFsJw6Oh3aU5A

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。



关注Java技术栈看更多干货

获取 Spring Boot 实战笔记!

以上是关于JSP+MYSQL分页这么弄?的主要内容,如果未能解决你的问题,请参考以下文章

jsp中利用MySQL实现分页技术

MySQL 跨库分页/ 分表分页/ 跨库分页,为什么这么难?

MySQL 跨库分页/ 分表分页/ 跨库分页,为什么这么难?

mysql——分页技术实现(基于layui框架)

jsp:使用jsp完成数据的分页显示

mysql百万数据分页查询4秒,求教怎么优化