`

hibernate分页

阅读更多
本文介绍一种分页组件的完整代码,最后封装了一个简单的jsp自定义标签

分页效果如下:



没有加页面效果,只是意思一下。这个分页的功能比较简单,不过更复杂的分页功能,原理也是差不多的

首先是Page对象

Java代码 
1.public class Page {  
2. 
3.    private static final int DEFAULT_PAGE_SIZE = 10;  
4. 
5.    private static final int DEFAULT_CURRENT_PAGE = 1;  
6. 
7.    private int currentPage;// 当前页数,通常在Action层设置  
8. 
9.    private int pageSize;// 每页记录数,通常在Action层设置  
10. 
11.    private int totalCount;// 总记录数,在DAO层设置  
12. 
13.    public Page(int currentPage, int pageSize) {  
14.        this.currentPage = currentPage;  
15.        this.pageSize = pageSize;  
16.    }  
17. 
18.    public Page(int currentPage) {  
19.        this.currentPage = currentPage;  
20.        this.pageSize = DEFAULT_PAGE_SIZE;  
21.    }  
22. 
23.    public Page() {  
24.        this.currentPage = DEFAULT_CURRENT_PAGE;  
25.        this.pageSize = DEFAULT_PAGE_SIZE;  
26.    }  
27. 
28.    public int getFirstIndex() {  
29.        return pageSize * (currentPage - 1);  
30.    }  
31. 
32.    public boolean hasPrevious() {  
33.        return currentPage > 1;  
34.    }  
35. 
36.    public boolean hasNext() {  
37.        return currentPage < getTotalPage();  
38.    }  
39. 
40.    public int getTotalPage() {  
41. 
42.        long remainder = totalCount % this.getPageSize();  
43. 
44.        if (0 == remainder) {  
45.            return totalCount / this.getPageSize();  
46.        }  
47. 
48.        return totalCount / this.getPageSize() + 1;  
49.    }  
50.} 
public class Page {

private static final int DEFAULT_PAGE_SIZE = 10;

private static final int DEFAULT_CURRENT_PAGE = 1;

private int currentPage;// 当前页数,通常在Action层设置

private int pageSize;// 每页记录数,通常在Action层设置

private int totalCount;// 总记录数,在DAO层设置

public Page(int currentPage, int pageSize) {
this.currentPage = currentPage;
this.pageSize = pageSize;
}

public Page(int currentPage) {
this.currentPage = currentPage;
this.pageSize = DEFAULT_PAGE_SIZE;
}

public Page() {
this.currentPage = DEFAULT_CURRENT_PAGE;
this.pageSize = DEFAULT_PAGE_SIZE;
}

public int getFirstIndex() {
return pageSize * (currentPage - 1);
}

public boolean hasPrevious() {
return currentPage > 1;
}

public boolean hasNext() {
return currentPage < getTotalPage();
}

public int getTotalPage() {

long remainder = totalCount % this.getPageSize();

if (0 == remainder) {
return totalCount / this.getPageSize();
}

return totalCount / this.getPageSize() + 1;
}
}

省略了必须的getter和setter方法。这里主要有3个字段currentPage、pageSize、totalCount,分别表示当前页、每页记录数、总记录数。基本上所有的分页组件,都需要这些字段

其实总页数totalPage也是必须的,但是这个字段是由pageSize和totalCount算出来的,所以即时计算得到比较好,如果也作为一个字段的话,那么和另外2个字段就不正交

getFirstIndex()方法也很重要,因为后面查询数据库的时候,需要作为第一条记录的标识,作为参数传给查询数据库的方法

下面是DAO的写法

Java代码 
1.public interface IBookDAO extends IGenericDAO<Book> {  
2. 
3.    public Book queryByIsbn(String isbn);  
4. 
5.    public List<Book> queryByNameWithPage(String name, Page page);  
6. 
7.    public List<Book> queryByNameWithoutPage(String name);  
8. 
9.} 
public interface IBookDAO extends IGenericDAO<Book> {

public Book queryByIsbn(String isbn);

public List<Book> queryByNameWithPage(String name, Page page);

public List<Book> queryByNameWithoutPage(String name);

}

其中涉及到分页查询的就是queryByNameWithPage()方法,这个Page对象是从Action传递下来的

Java代码 
1.@Repository 
2.public class BookDAO extends GenericDAO<Book> implements IBookDAO {  
3. 
4.    public BookDAO() {  
5.        super(Book.class);  
6.    }  
7. 
8.    @Override 
9.    public Book queryByIsbn(String isbn) {  
10.        String hql = "from Book b where b.isbn = ?";  
11.        return queryForObject(hql, new Object[] { isbn });  
12.    }  
13. 
14.    @Override 
15.    public List<Book> queryByNameWithPage(String name, Page page) {  
16.        String hql = "from Book b where b.name = ?";  
17.        return queryForList(hql, new Object[] { name }, page);  
18.    }  
19. 
20.    @Override 
21.    public List<Book> queryByNameWithoutPage(String name) {  
22.        String hql = "from Book b where b.name = ?";  
23.        return queryForList(hql, new Object[] { name });  
24.    }  
25. 
26.} 
@Repository
public class BookDAO extends GenericDAO<Book> implements IBookDAO {

public BookDAO() {
super(Book.class);
}

@Override
public Book queryByIsbn(String isbn) {
String hql = "from Book b where b.isbn = ?";
return queryForObject(hql, new Object[] { isbn });
}

@Override
public List<Book> queryByNameWithPage(String name, Page page) {
String hql = "from Book b where b.name = ?";
return queryForList(hql, new Object[] { name }, page);
}

@Override
public List<Book> queryByNameWithoutPage(String name) {
String hql = "from Book b where b.name = ?";
return queryForList(hql, new Object[] { name });
}

}

这里调用的是GenericDAO里的方法queryForList()

Java代码 
1.@SuppressWarnings("unchecked")  
2.    protected List<T> queryForList(String hql, Object[] params, Page page) {  
3. 
4.        generatePageTotalCount(hql, params, page);  
5. 
6.        Query query = sessionFactory.getCurrentSession().createQuery(hql);  
7.        setQueryParams(query, params);  
8.        query.setFirstResult(page.getFirstIndex());  
9.        query.setMaxResults(page.getPageSize());  
10.        return query.list();  
11.    }  
12. 
13.    /** 
14.     * 该方法会改变参数page的totalCount字段 
15.     *  
16.     * @param originHql 
17.     *            原始hql语句 
18.     * @param params 
19.     *            原始参数 
20.     * @param page 
21.     *            页面对象 
22.     */ 
23.    private void generatePageTotalCount(String originHql, Object[] params,  
24.            Page page) {  
25.        String generatedCountHql = "select count(*) " + originHql;  
26.        Query countQuery = sessionFactory.getCurrentSession().createQuery(  
27.                generatedCountHql);  
28.        setQueryParams(countQuery, params);  
29.        int totalCount = ((Long) countQuery.uniqueResult()).intValue();  
30.        page.setTotalCount(totalCount);  
31.    } 
@SuppressWarnings("unchecked")
protected List<T> queryForList(String hql, Object[] params, Page page) {

generatePageTotalCount(hql, params, page);

Query query = sessionFactory.getCurrentSession().createQuery(hql);
setQueryParams(query, params);
query.setFirstResult(page.getFirstIndex());
query.setMaxResults(page.getPageSize());
return query.list();
}

/**
* 该方法会改变参数page的totalCount字段
*
* @param originHql
*            原始hql语句
* @param params
*            原始参数
* @param page
*            页面对象
*/
private void generatePageTotalCount(String originHql, Object[] params,
Page page) {
String generatedCountHql = "select count(*) " + originHql;
Query countQuery = sessionFactory.getCurrentSession().createQuery(
generatedCountHql);
setQueryParams(countQuery, params);
int totalCount = ((Long) countQuery.uniqueResult()).intValue();
page.setTotalCount(totalCount);
}

这段代码关键有2点

一个是调用Page对象的getFirstIndex()方法和getPageSize()方法,确定查询结果的范围

另一个是设置Page对象自身的totalCount字段,因为Action里持有该Page对象的引用,所以根据totalCount和pageSize计算出totalPage之后,就可以在前台显示出总页数

接下来是Action层的写法(中间的Service层只是起一个传递作用和事务控制作用,省略)

Java代码 
1.@Controller 
2.@Scope("prototype")  
3.public class BookAction extends ActionSupport {  
4. 
5.    private static final long serialVersionUID = -4400311497910666205L;  
6. 
7.    @Autowired 
8.    private IBookService bookService;  
9. 
10.    private Page page;// 分页组件  
11. 
12.    private List<Book> books;// 查询结果列表  
13. 
14.    public String list() {  
15.        if (null == page) {  
16.            page = new Page();// 如果page对象为空,说明不是通过点击页码跳转  
17.        }  
18.        books = bookService.getBooks(page);  
19.        return SUCCESS;  
20.    }  
21.} 
@Controller
@Scope("prototype")
public class BookAction extends ActionSupport {

private static final long serialVersionUID = -4400311497910666205L;

@Autowired
private IBookService bookService;

private Page page;// 分页组件

private List<Book> books;// 查询结果列表

public String list() {
if (null == page) {
page = new Page();// 如果page对象为空,说明不是通过点击页码跳转
}
books = bookService.getBooks(page);
return SUCCESS;
}
}

这里省略了与分页无关的方法和字段,以及getter、setter方法

这里重点是list()方法,如果是通过jsp页面点击“上一页”、“下一页”的链接进入此方法的,那么struts2框架就会自动初始化Page对象。如果这个时候page对象为空,那么说明是用户直接进入该页面,就需要初始化一个Page对象(currentPage=1,表示是第一页)

该Page对象此时的totalCount是0,在查询之后,才会根据查询到的记录总数,设置totalCount的值,由于Action持有此Page的引用,所以可以在前台显示出来

下面是前台页面的写法

Jsp代码 
1.<div id="page_area">  
2.                  
3.                <s:if test="page.hasPrevious()">  
4.                    <a href="list.action?page.currentPage=${page.currentPage-1}">上一页</a>  
5.                </s:if>  
6.                  
7.                <span>第${page.currentPage}页</span>  
8.                <span>共${page.getTotalPage()}页</span>  
9.                  
10.                <s:if test="page.hasNext()">  
11.                    <a href="list.action?page.currentPage=${page.currentPage+1}">下一页</a>  
12.                </s:if>  
13.                  
14.            </div> 
<div id="page_area">

<s:if test="page.hasPrevious()">
<a href="list.action?page.currentPage=${page.currentPage-1}">上一页</a>
</s:if>

<span>第${page.currentPage}页</span>
<span>共${page.getTotalPage()}页</span>

<s:if test="page.hasNext()">
<a href="list.action?page.currentPage=${page.currentPage+1}">下一页</a>
</s:if>

</div>

里面用到了struts2的<s:if>标签,还有${}表达式。我感觉${]表达式还是很方便的,struts2封装的各种<s:>标签我倒是不喜欢用,自己用html标签开发,我觉得更灵活一点

这个分页组件在各个页面都可以用到,但是如果每个jsp都要拷贝这段代码,就不好了,所以需要把它封装成标签

封装后的效果是这样的:

Jsp代码 
1.<wfm:page page="${page}" /> 
<wfm:page page="${page}" />

下面就介绍下这个自定义标签的开发

首先需要在WEB-INF目录下创建一个.tld文件,只要放在WEB-INF目录下就可以了,所以一般会单独建一个子目录tld来保存



这个tld内容如下

Xml代码 
1.<?xml version="1.0" encoding="UTF-8" ?> 
2. 
3.<taglib xmlns="http://java.sun.com/xml/ns/j2ee" 
4.    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
5.    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" 
6.    version="2.0"> 
7.      
8.    <description>Workforce Tag Lib</description> 
9.    <tlib-version>1.0</tlib-version> 
10.    <short-name>WorkforceTagLibrary</short-name> 
11.    <uri>/wfm-tags</uri> 
12.     
13.    <tag> 
14.        <description>generate page bar</description> 
15.        <name>page</name> 
16.        <tag-class>com.huawei.inoc.framework.tag.PageTag</tag-class> 
17.        <body-content>empty</body-content> 
18.        <attribute> 
19.            <name>page</name> 
20.            <required>true</required> 
21.            <rtexprvalue>true</rtexprvalue> 
22.        </attribute> 
23.    </tag> 
24.     
25.</taglib> 
<?xml version="1.0" encoding="UTF-8" ?>

<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
    version="2.0">
   
    <description>Workforce Tag Lib</description>
    <tlib-version>1.0</tlib-version>
    <short-name>WorkforceTagLibrary</short-name>
    <uri>/wfm-tags</uri>
  
    <tag>
        <description>generate page bar</description>
        <name>page</name>
        <tag-class>com.huawei.inoc.framework.tag.PageTag</tag-class>
        <body-content>empty</body-content>
        <attribute>
            <name>page</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
  
</taglib>

上面比较重要的元素是<uri>,后面在jsp页面里使用这个标签的时候会用到,然后可以定义多个<tag>元素,每个都是一个标签,命名都很清晰,就不用解释了。其中<rtexprvalue>这个标签,设置为true之后,则此属性支持变量

下面是标签的实现类

Java代码 
1.public class PageTag extends SimpleTagSupport {  
2. 
3.    private Page page;  
4. 
5.    @Override 
6.    public void doTag() throws JspException, IOException {  
7.        String content = generateContent();  
8.        getJspContext().getOut().write(content);  
9.    }  
10. 
11.    private String generateContent() {  
12. 
13.        StringBuilder response = new StringBuilder();  
14. 
15.        response.append("<div id=\"page_area\">");  
16. 
17.        if (page.hasPrevious()) {  
18.            response.append("<a href=\"list.action?page.currentPage=" 
19.                    + (page.getCurrentPage() - 1) + "\">上一页</a>");  
20.        }  
21. 
22.        response.append("<span>第" + page.getCurrentPage() + "页</span>");  
23.        response.append("<span>共" + page.getTotalPage() + "页</span>");  
24. 
25.        if (page.hasNext()) {  
26.            response.append("<a href=\"list.action?page.currentPage=" 
27.                    + (page.getCurrentPage() + 1) + "\">下一页</a>");  
28.        }  
29. 
30.        response.append("</div>");  
31. 
32.        return response.toString();  
33.    }  
34. 
35.    public Page getPage() {  
36.        return page;  
37.    }  
38. 
39.    public void setPage(Page page) {  
40.        this.page = page;  
41.    }  
42. 
43.} 
public class PageTag extends SimpleTagSupport {

private Page page;

@Override
public void doTag() throws JspException, IOException {
String content = generateContent();
getJspContext().getOut().write(content);
}

private String generateContent() {

StringBuilder response = new StringBuilder();

response.append("<div id=\"page_area\">");

if (page.hasPrevious()) {
response.append("<a href=\"list.action?page.currentPage="
+ (page.getCurrentPage() - 1) + "\">上一页</a>");
}

response.append("<span>第" + page.getCurrentPage() + "页</span>");
response.append("<span>共" + page.getTotalPage() + "页</span>");

if (page.hasNext()) {
response.append("<a href=\"list.action?page.currentPage="
+ (page.getCurrentPage() + 1) + "\">下一页</a>");
}

response.append("</div>");

return response.toString();
}

public Page getPage() {
return page;
}

public void setPage(Page page) {
this.page = page;
}

}

上面的代码很简单,jsp2.0之后,要实现自定义标签是很容易的,继承SimpleTagSupport类,实现doTag()方法即可,如果需要设置一些属性的话,声明为字段,并添加getter和setter方法

最后是jsp页面的引用方法

Jsp代码 
1.<%@ taglib prefix="wfm" uri="/wfm-tags"%> 
<%@ taglib prefix="wfm" uri="/wfm-tags"%>


Jsp代码 
1.<wfm:page page="${page}" /> 
<wfm:page page="${page}" />

在比较早的版本里,还需要在web.xml里增加tag-lib的配置,从某个版本的servlet规范之后(好像是3.0),就不需要了,容器会自动到WEB-INF目录下加载所有的.tld文件
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics