数据库分页原理
在Mysql数据库中提供了分页语句:
SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
LIMIT 子句可以被用于强制 SELECT 语句返回指定的记录数。LIMIT 接受一个或两个数字参数。参数必须是一个整数常量。如果给定两个参数,第一个参数指定第一个返回记录行的偏移量,第二个参数指定返回记录行的最大数目。初始记录行的偏移量是 0(而不是 1): 为了与 PostgreSQL 兼容,MySQL 也支持句法: LIMIT # OFFSET #。
LIMIT子句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如果给出两个参数, 第一个参数指定返回的第一行在所有数据中的位置,从0开始(注意不是1),第二个参数指定最多返回行数。例如:
select * from table WHERE … LIMIT 10; #返回前10行
select * from table WHERE … LIMIT 0,10; #返回前10行
select * from table WHERE … LIMIT 10,20; #返回第10-20行数据
在实际设计中,我们会发现分页具有多个属性,分别是页码(pageIndex)、页大小(pageSize)、从数据库中查询的数据总条数(totalSize)、以及用来存放每页查询的数据的集合(list)。(当然也包括页总数(pageCount),不过页总数可以通过(totalSize/pageSize)来获取),所以我们可以设计一个PageBean对象类封装。
具体代码如下:
import java.util.List;
public class PageBean<T>{
/**
* 页码
*/
private int pageNum;
/**
* 页大小
*/
private int pageSize;
/**
* 数据总数
*/
private long totalSize;
/**
* 总页数
*/
private int pageCount;
/**
* 页面数据
*/
private List<T> data;
public PageBean() {
}
public PageBean(int pageNum, int pageSize, long totalSize, List<T> data) {
this.pageNum = pageNum;
this.pageSize = pageSize;
this.totalSize = totalSize;
pageCount= (int) (totalSize%pageSize==0?totalSize/pageSize:totalSize/pageSize+1);
this.data = data;
}
public int getPageNum() {
return pageNum;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public long getTotalSize() {
return totalSize;
}
public void setTotalSize(long totalSize) {
this.totalSize = totalSize;
}
public int getPageCount() {
return pageCount;
}
public void setPageCount(int pageCount) {
this.pageCount = pageCount;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
@Override
public String toString() {
return "PageBean{" +
"pageNum=" + pageNum +
", pageSize=" + pageSize +
", totalSize=" + totalSize +
", pageCount=" + pageCount +
", data=" + data +
'}';
}
}
现在有一个学生表,需要实现在页面上显示并使用分页。首先创建一个Student类,属性与员工表属性保持一致即可。
tb_emp数据表结构:
- sid:学生学号
- sname:学生姓名
- age:学生年龄
- gender:学生性别
- date:入学日期(由于接收的日期是字符串格式,在存入数据库中会和数据库中的date类型字段不匹配,抛出异常,所以我们可以转换,使用simpledateformate,这里我使用beanutils工具类中提供的转换工具ConverUtils完成转换)
ConvertUtils.register(new Converter() {
@Override
public <T> T convert(Class<T> aClass, Object o) {
if(o!=null){
if(o instanceof String){
String string=(String)o;
SimpleDateFormat[] sdf={new SimpleDateFormat("yyyy-MM-dd HH:MM:ss"),new SimpleDateFormat("yyyy-MM-dd"),new SimpleDateFormat("yyyy/MM/dd HH:MM:ss"),new SimpleDateFormat("yyyy/MM/dd")};
for (SimpleDateFormat format : sdf) {
try {
return (T)format.parse(string);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
return null;
}
}, Date.class);)
- banlance(这个属性没什么用,只是我用来测试ThreadLocal时用的)
Student代码:
public class Student {
private String sid;
private String sname;
private Integer age;
private String gender;
public Student() {
}
public Student(String sid, String sname, Integer age, String gender) {
this.sid = sid;
this.sname = sname;
this.age = age;
this.gender = gender;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Student{" +
"sid='" + sid + '\'' +
", sname='" + sname + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
有了这些java域对象后,我们实现DAO层的工作,主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此。设计一个StudentDao接口,在写一个该接口的实现类StudentDaoImpl。在这里我使用Dbutils工具类以及DruidDateSource连接池。
要使用Dbutils工具类,需要导入commons-dbutils-1.6.jar包,数据库连接池导入druid-1.1.5.jar包
mysql数据库连接导入mysql-connector-java-5.1.7-bin.jar, 版本根据自己开发环境自行选择。
- StudentDao接口
import java.util.List;
public interface StudentDao {
/**
*分页
* @param pageNum 页码
* @param pageSize 页大小
* @return
*/
List<Student> findByPage(int pageNum,int pageSize);
void add(Student student);
long getCount();
}
- StudentDaoImpl
public class StudentDaoImpl implements StudentDao {
@Override
public List<Student> findByPage(int pageNum, int pageSize) {
QueryRunner qr=new QueryRunner(DataSourceUtils.getDateSource());
try {
return qr.query("select *from stu order by sid limit ?,?", new BeanListHandler<>(Student.class),(pageNum-1)*pageSize,pageSize);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
@Override
public void add(Student student) {
QueryRunner qr=new QueryRunner(DataSourceUtils.getDateSource());
Object[] params={student.getSid(),student.getSname(),student.getAge(),student.getGender()};
try {
qr.update("insert into stu values (?,?,?,?)", params);
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException("添加失败", e);
}
}
@Override
public long getCount() {
QueryRunner qr=new QueryRunner(DataSourceUtils.getDateSource());
try {
return qr.query("select count(*) from stu", new ScalarHandler<>());
} catch (SQLException e) {
e.printStackTrace();
}
return 0;
}
}
实现dao层代码后,接下来就是实现业务层代码,由于业务相对较少,所以业务层代码会相对简单。设计一个StudentService业务接口以及实现类
- StudentService接口
public interface StudentService {
/**
*
* @param pageNum
* @param pageSize
* @return 返回PageBean对象
*/
PageBean<Student> findPage(int pageNum,int pageSize);
}
- 实现类StudentServiceImpl
import java.util.List;
public class StudentServiceImpl implements StudentService {
private StudentDao studentDao=new StudentDaoImpl();
@Override
public PageBean<Student> findPage(int pageNum, int pageSize) {
List<Student> data = studentDao.findByPage(pageNum, pageSize);
long count = studentDao.getCount();
PageBean<Student> pageBean=new PageBean<>(pageNum, pageSize, count, data);
return pageBean;
}
}
业务层写完后,可以实现View(视图层),额,界面比较丑。界面要想实现数据的展示,那么jsp界面必须能够接收数据,所以这里要借助EL表达式以及jstl标签库。
- stulist.jsp实现代码:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>学生信息界面</title>
<style>
td {
border: 1px solid gray;
text-align: center;
}
table {
border: 1px solid gray;
}
</style>
</head>
<body>
<h2 align="center">学生信息</h2>
<table style=" margin: auto; width: 600px;height: 400px" cellspacing="0" cellpadding="3px" >
<tr style="background: aquamarine">
<th>学生ID</th>
<th>学生姓名</th>
<th>学生年龄</th>
<th>学生性别</th>
</tr>
<c:forEach var="stu" items="${pages.data}">
<tr>
<td>${stu.sid}</td>
<td>${stu.sname}</td>
<td>${stu.age}</td>
<td>${stu.gender}</td>
</tr>
</c:forEach>
</table>
<div align="center">
<a href="${pageContext.request.contextPath}/stulist?pageNum=1&pageSize=${pages.pageSize}" style="font-size: large">首页</a>
<a href="${pageContext.request.contextPath}/stulist?pageNum=${pages.pageNum-1}&pageSize=${pages.pageSize}" style="font-size: large">上一页</a>
<a href="${pageContext.request.contextPath}/stulist?pageNum=${pages.pageNum+1<pages.pageCount?pages.pageNum+1:pages.pageCount}&pageSize=${pages.pageSize}" style="font-size: large">下一页</a>
<a href="${pageContext.request.contextPath}/stulist?pageNum=${pages.pageCount}&pageSize=${pages.pageSize}" style="font-size: large">尾页</a>
</div>
</body>
</html>
最后就是使用servlet来实现数据传递到jsp页面
*StuListServlet
@WebServlet(name = "StuListServlet", value = "/stulist")
public class StuListServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset:utf-8");
String pageNum = request.getParameter("pageNum");
String pageSize = request.getParameter("pageSize");
int pn=1;
int ps=5;
if(!StringUtils.isEmpty(pageNum)) {
pn = Integer.parseInt(pageNum);
if (pn < 1)
pn = 1;
}
if(!StringUtils.isEmpty(pageSize)) {
ps = Integer.parseInt(pageSize);
if(ps<1){
ps=5;
}
}
StudentService studentService=new StudentServiceImpl();
PageBean<Student> pages = studentService.findPage(pn, ps);
request.setAttribute("pages", pages);
request.getRequestDispatcher("/stulist.jsp").forward(request, response);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doPost(request, response);
}
}
到这里,分页显示学生信息的基本需求已经完成了,看一下最终效果吧