Spring hibernate pagination with taglib (Phân trang trong Spring Mvc )

Hi all, Tiếp tục chuỗi bài học mình đang vọc vạch về Spring Mvc. Với dev hai từ "phân trang" đã quá quen thuộc. Bài toán cụ thể là khi dữ liệu bản ghi trả về rất lớn, nếu chúng ta hiển thị toàn bộ dữ liệu đó ra một page mà không có phân trang thì sao???. Giả sử nếu có 10^6 bản ghi, và người dùng muốn xem bản ghi ở vị trí thứ tự 500, ở vị trí thứ tự 1000, sẽ phải mất bao nhiêu lần lăn chuột??? Rồi sau đó nếu người dùng muốn quay lại xem bản ghi đầu tiên (vị trí 1) nữa thì sao??? Nếu đặt dev vào vị trí người dùng, chắc hẳn chúng ta sẽ không chấp nhận điều đó.
Hôm nay mình xin giới thiệu cách phân trang trên một view trong Spring, kết hợp Hibernate. Bài viết này mình sử dụng hibernate annotation và cấu hình xml để connect tới DB (dự án thực thì bạn cần dùng try/catch để bắt lỗi, ở đây mình đơn giản hóa nên không dùng). Nếu bạn cần sử dụng cách cấu hình hibernate xml (fileName.hbm.xml) thì để lại comment bên dưới mình sẽ làm một ví dụ về cách đó. Cấu trúc thư mục của ví dụ như sau: 1. Model

package com.duongvantien.model;
import javax.persistence.*;
import java.io.Serializable;

@Entity
@Table(name = "tblPerson")
public class Person implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;

    @Column(name = "fullname")
    private String fullname;

    @Column(name = "address")
    private String address;

    @Column(name = "phone")
    private String phone;

   // get/set
}

2. DAO (data access object)

  • PersonDAO (interface)
package com.duongvantien.dao;

import com.duongvantien.model.Person;
import java.io.Serializable;
import java.util.List;

public interface PersonDAO extends Serializable {
    public List<Person> findAll(Integer offset, Integer maxResult);
    public Long count();
}
  • PersonDAOImpl (implements)
package com.duongvantien.dao;

import com.duongvantien.model.Person;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;

@Repository
public class PersonDAOImpl implements PersonDAO {

    @Autowired
    private SessionFactory sessionFactory;

    @SuppressWarnings("unchecked")
    public List<Person> findAll(Integer offset, Integer maxResults) {
        return sessionFactory.openSession()
                .createCriteria(Person.class)
                .setFirstResult(offset!=null?offset:0)
                .setMaxResults(maxResults!=null?maxResults:10)
                .list();
    }

    @SuppressWarnings("unchecked")
    public Long count() {
        return (Long)sessionFactory.openSession()
                .createCriteria(Person.class)
                .setProjection(Projections.rowCount())
                .uniqueResult();
    }
}

3. Service

  • Service (interface)
package com.duongvantien.service;

import com.duongvantien.model.Person;
import java.io.Serializable;
import java.util.List;

public interface PersonService extends Serializable{
    public List<Person> findAll(Integer offset, Integer maxResult);
    public Long count();
}
  • PersonServiceImpl (implements)
package com.duongvantien.service;

import com.duongvantien.dao.PersonDAO;
import com.duongvantien.model.Person;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class PersonServiceImpl implements PersonService {

    @Autowired
    private PersonDAO personDAO;

    public List<Person> findAll(Integer offset, Integer maxResult) {
        return personDAO.findAll(offset, maxResult);
    }

    public Long count() {
        return personDAO.count();
    }
}

4. Taglib Tạo một class để xử lý nút nhấn trên thanh phân trang

package com.duongvantien.taglib;

import java.io.Writer;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class PaginationTaglib extends SimpleTagSupport {
	private String uri;
	private int offset;
	private int count;
	private int max = 10;
	private int steps = 10;
	private String previous = "Previous";
	private String next = "Next";

	private Writer getWriter() {
		JspWriter out = getJspContext().getOut();
		return out;
	}

	@Override
	public void doTag() throws JspException {
		Writer out = getWriter();

		try {
			out.write("<nav>");
			out.write("<ul class=\"pagination\">");
			
			if(offset<steps)
				out.write(constructLink(1, previous, "disabled", true));
			else
				out.write(constructLink(offset-steps, previous, null, false));
			
			for(int itr=0;itr<count;itr+=steps) {
				if(offset==itr)
					out.write(constructLink((itr/10+1)-1 *steps, String.valueOf(itr/10+1), "active", true));
				else
					out.write(constructLink(itr/10*steps, String.valueOf(itr/10+1), null , false));
			}

			if(offset+steps>=count)
				out.write(constructLink(offset+steps, next, "disabled", true));
			else
				out.write(constructLink(offset+steps, next, null , false));
			
			out.write("</ul>");
			out.write("</nav>");
		} catch (java.io.IOException ex) {
			throw new JspException("Error in Paginator tag", ex);
		}
	}


	private String constructLink(int page, String text, String className, boolean disabled) {
		StringBuilder link = new StringBuilder("<li");
		if (className != null) {
			link.append(" class=\"");
			link.append(className);
			link.append("\"");
		}
		if(disabled)
			link.append(">").append("<a href=\"#\">"+text+"</a></li>");
		else
			link.append(">").append("<a href=\""+uri+"?offset="+page + "\">"+text+"</a></li>");
		return link.toString();
	}
}
  • Tạo thẻ taglib cho các nút nhấn trên thanh phân trang
<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.1" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-jsptaglibrary_2_1.xsd">
    <tlib-version>1.0</tlib-version>
    <short-name>paginator</short-name>
    <uri>/WEB-INF/taglibs/customTaglib.tld</uri>

    <tag>
        <name>paginate</name>
        <tag-class>com.duongvantien.taglib.PaginationTaglib</tag-class>
        <body-content>empty</body-content>

        <attribute>
            <name>next</name>
            <rtexprvalue>true</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <name>previous</name>
            <rtexprvalue>true</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <name>uri</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <type>java.lang.String</type>
        </attribute>
        <attribute>
            <name>offset</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <type>int</type>
        </attribute>
        <attribute>
            <name>count</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
            <type>int</type>
        </attribute>
        <attribute>
            <name>max</name>
            <rtexprvalue>true</rtexprvalue>
            <type>int</type>
        </attribute>
    </tag>
</taglib>

5. Controller

package com.duongvantien.controller;

import com.duongvantien.model.Person;
import com.duongvantien.service.PersonService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;

public class PersonController {

    @Autowired
    private PersonService personService;

    @RequestMapping(value = "/persons", method = RequestMethod.GET)
    public String findAll(Model model, Integer offset, Integer maxResults) {
        List<Person> list = personService.findAll(offset, maxResults);
        model.addAttribute("count", personService.count());
        model.addAttribute("offset", offset);
        model.addAttribute("listPersons", list);
        return "persons";
    }
}

6. View Tạo một trang để hiển thị kết quả

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="tag" uri="/WEB-INF/taglibs/customTaglib.tld" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" type="text/css" href="/css/bootstrap.css"/>
    <link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css"/>
    <link rel="stylesheet" type="text/css" href="/css/font-awesome.min.css"/>
    <title>Persons</title>
</head>
<body>
<div class="container">
    <div class="well">
        <strong>List of Persons</strong>
    </div>
    <table class="table table-stripped">
        <tr>
            <th>STT</th>
            <th>Full name</th>
            <th>Address</th>
            <th>Phone</th>
        </tr>
        <c:forEach items="${listPersons}" var="person" varStatus="itr">
            <tr>
                <td>${offset + itr.index +1 }</td>
                <td>${person.fullname }</td>
                <td>${person.address }</td>
                <td>${person.phone }</td>
            </tr>
        </c:forEach>
    </table>
    <tag:paginate max="10" offset="${offset}" count="${count}" uri="/persons" next="&raquo;" previous="&laquo;"/>
</div>
<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/bootstrap.min.js"></script>
</body>
</html>

Và đây là kết quả thu được 😄 Các bạn có thể xem chi tiết tại đây : https://github.com/DuongVanTien/SpringHibernatePagination.git Chúc các bạn thành công!