Hibernate3 提供了DetachedCriteria,使得我们可以在Web层构造detachedCriteria,然后调用业务层Bean,进行动态条件查询,根据这一功能,我设计了通用的抽象Bean基类和分页类支持,代码来自于Quake Wang的javaeye-core包的相应类,然后又做了很多修改。
分页支持类:
java代码: |
package com.
javaeye.
common.
util;
import java.
util.
List;
public
class PaginationSupport
{
public
final
static
int PAGESIZE =
30;
private
int pageSize = PAGESIZE;
private
List items;
private
int totalCount;
private
int
[
] indexes =
new
int
[
0
];
private
int startIndex =
0;
public PaginationSupport
(
List items,
int totalCount
)
{
setPageSize
(PAGESIZE
);
setTotalCount
(totalCount
);
setItems
(items
);
setStartIndex
(
0
);
}
public PaginationSupport
(
List items,
int totalCount,
int startIndex
)
{
setPageSize
(PAGESIZE
);
setTotalCount
(totalCount
);
setItems
(items
);
setStartIndex
(startIndex
);
}
public PaginationSupport
(
List items,
int totalCount,
int pageSize,
int startIndex
)
{
setPageSize
(pageSize
);
setTotalCount
(totalCount
);
setItems
(items
);
setStartIndex
(startIndex
);
}
public
List getItems
(
)
{
return items;
}
public
void setItems
(
List items
)
{
this.
items = items;
}
public
int getPageSize
(
)
{
return pageSize;
}
public
void setPageSize
(
int pageSize
)
{
this.
pageSize = pageSize;
}
public
int getTotalCount
(
)
{
return totalCount;
}
public
void setTotalCount
(
int totalCount
)
{
if
(totalCount >
0
)
{
this.
totalCount = totalCount;
int count = totalCount / pageSize;
if
(totalCount % pageSize >
0
)
count++;
indexes =
new
int
[count
];
for
(
int i =
0; i < count; i++
)
{
indexes
[i
] = pageSize * i;
}
}
else
{
this.
totalCount =
0;
}
}
public
int
[
] getIndexes
(
)
{
return indexes;
}
public
void setIndexes
(
int
[
] indexes
)
{
this.
indexes = indexes;
}
public
int getStartIndex
(
)
{
return startIndex;
}
public
void setStartIndex
(
int startIndex
)
{
if
(totalCount <=
0
)
this.
startIndex =
0;
else
if
(startIndex >= totalCount
)
this.
startIndex = indexes
[indexes.
length -
1
];
else
if
(startIndex <
0
)
this.
startIndex =
0;
else
{
this.
startIndex = indexes
[startIndex / pageSize
];
}
}
public
int getNextIndex
(
)
{
int nextIndex = getStartIndex
(
) + pageSize;
if
(nextIndex >= totalCount
)
return getStartIndex
(
);
else
return nextIndex;
}
public
int getPreviousIndex
(
)
{
int previousIndex = getStartIndex
(
) - pageSize;
if
(previousIndex <
0
)
return
0;
else
return previousIndex;
}
}
|
抽象业务类
java代码: |
/** * Created on 2005-7-12 */
package com.
javaeye.
common.
business;
import java.
io.
Serializable;
import java.
util.
List;
import org.
hibernate.
Criteria;
import org.
hibernate.
HibernateException;
import org.
hibernate.
Session;
import org.
hibernate.
criterion.
DetachedCriteria;
import org.
hibernate.
criterion.
Projections;
import org.
springframework.
orm.
hibernate3.
HibernateCallback;
import org.
springframework.
orm.
hibernate3.
support.
HibernateDaoSupport;
import com.
javaeye.
common.
util.
PaginationSupport;
public
abstract
class AbstractManager
extends HibernateDaoSupport
{
private
boolean cacheQueries =
false;
private
String queryCacheRegion;
public
void setCacheQueries
(
boolean cacheQueries
)
{
this.
cacheQueries = cacheQueries;
}
public
void setQueryCacheRegion
(
String queryCacheRegion
)
{
this.
queryCacheRegion = queryCacheRegion;
}
public
void save
(
final
Object entity
)
{
getHibernateTemplate
(
).
save
(entity
);
}
public
void persist
(
final
Object entity
)
{
getHibernateTemplate
(
).
save
(entity
);
}
public
void update
(
final
Object entity
)
{
getHibernateTemplate
(
).
update
(entity
);
}
public
void delete
(
final
Object entity
)
{
getHibernateTemplate
(
).
delete
(entity
);
}
public
Object load
(
final
Class entity,
final
Serializable id
)
{
return getHibernateTemplate
(
).
load
(entity, id
);
}
public
Object get
(
final
Class entity,
final
Serializable id
)
{
return getHibernateTemplate
(
).
get
(entity, id
);
}
public
List findAll
(
final
Class entity
)
{
return getHibernateTemplate
(
).
find
(
"from " + entity.
getName
(
)
);
}
public
List findByNamedQuery
(
final
String namedQuery
)
{
return getHibernateTemplate
(
).
findByNamedQuery
(namedQuery
);
}
public
List findByNamedQuery
(
final
String query,
final
Object parameter
)
{
return getHibernateTemplate
(
).
findByNamedQuery
(query, parameter
);
}
public
List findByNamedQuery
(
final
String query,
final
Object
[
] parameters
)
{
return getHibernateTemplate
(
).
findByNamedQuery
(query, parameters
);
}
public
List find
(
final
String query
)
{
return getHibernateTemplate
(
).
find
(query
);
}
public
List find
(
final
String query,
final
Object parameter
)
{
return getHibernateTemplate
(
).
find
(query, parameter
);
}
public PaginationSupport findPageByCriteria
(
final DetachedCriteria detachedCriteria
)
{
return findPageByCriteria
(detachedCriteria, PaginationSupport.
PAGESIZE,
0
);
}
public PaginationSupport findPageByCriteria
(
final DetachedCriteria detachedCriteria,
final
int startIndex
)
{
return findPageByCriteria
(detachedCriteria, PaginationSupport.
PAGESIZE, startIndex
);
}
public PaginationSupport findPageByCriteria
(
final DetachedCriteria detachedCriteria,
final
int pageSize,
final
int startIndex
)
{
return
(PaginationSupport
) getHibernateTemplate
(
).
execute
(
new HibernateCallback
(
)
{
public
Object doInHibernate
(Session session
)
throws HibernateException
{
Criteria criteria = detachedCriteria.
getExecutableCriteria
(session
);
int totalCount =
(
(
Integer
) criteria.
setProjection
(Projections.
rowCount
(
)
).
uniqueResult
(
)
).
intValue
(
);
criteria.
setProjection
(
null
);
List items = criteria.
setFirstResult
(startIndex
).
setMaxResults
(pageSize
).
list
(
);
PaginationSupport ps =
new PaginationSupport
(items, totalCount, pageSize, startIndex
);
return ps;
}
},
true
);
}
public
List findAllByCriteria
(
final DetachedCriteria detachedCriteria
)
{
return
(
List
) getHibernateTemplate
(
).
execute
(
new HibernateCallback
(
)
{
public
Object doInHibernate
(Session session
)
throws HibernateException
{
Criteria criteria = detachedCriteria.
getExecutableCriteria
(session
);
return criteria.
list
(
);
}
},
true
);
}
public
int getCountByCriteria
(
final DetachedCriteria detachedCriteria
)
{
Integer count =
(
Integer
) getHibernateTemplate
(
).
execute
(
new HibernateCallback
(
)
{
public
Object doInHibernate
(Session session
)
throws HibernateException
{
Criteria criteria = detachedCriteria.
getExecutableCriteria
(session
);
return criteria.
setProjection
(Projections.
rowCount
(
)
).
uniqueResult
(
);
}
},
true
);
return count.
intValue
(
);
}
}
|
用户在web层构造查询条件detachedCriteria,和可选的startIndex,调用业务bean的相应findByCriteria方法,返回一个PaginationSupport的实例ps。
ps.getItems()得到已分页好的结果集 ps.getIndexes()得到分页索引的数组 ps.getTotalCount()得到总结果数 ps.getStartIndex()当前分页索引 ps.getNextIndex()下一页索引 ps.getPreviousIndex()上一页索引
连续看了两篇robbin有关DetachedCriteria的介绍,感觉真的不错,尤其是上面的示例代码,让我着实觉得该对我原来的分页查询做一下代码重构了。
我把原本我的做法也提供出来供大家讨论吧:
首先,为了实现分页查询,我封装了一个Page类:
java代码: |
/*Created on 2005-4-14*/
package org.
flyware.
util.
page;
/** * @author Joa * */
public
class Page
{
/** imply if the page has previous page */
private
boolean hasPrePage;
/** imply if the page has next page */
private
boolean hasNextPage;
/** the number of every page */
private
int everyPage;
/** the total page number */
private
int totalPage;
/** the number of current page */
private
int currentPage;
/** the begin index of the records by the current query */
private
int beginIndex;
/** The default constructor */
public Page
(
)
{
}
/** construct the page by everyPage * @param everyPage * */
public Page
(
int everyPage
)
{
this.
everyPage = everyPage;
}
/** The whole constructor */
public Page
(
boolean hasPrePage,
boolean hasNextPage,
int everyPage,
int totalPage,
int currentPage,
int beginIndex
)
{
this.
hasPrePage = hasPrePage;
this.
hasNextPage = hasNextPage;
this.
everyPage = everyPage;
this.
totalPage = totalPage;
this.
currentPage = currentPage;
this.
beginIndex = beginIndex;
}
/** * @return * Returns the beginIndex. */
public
int getBeginIndex
(
)
{
return beginIndex;
}
/** * @param beginIndex * The beginIndex to set. */
public
void setBeginIndex
(
int beginIndex
)
{
this.
beginIndex = beginIndex;
}
/** * @return * Returns the currentPage. */
public
int getCurrentPage
(
)
{
return currentPage;
}
/** * @param currentPage * The currentPage to set. */
public
void setCurrentPage
(
int currentPage
)
{
this.
currentPage = currentPage;
}
/** * @return * Returns the everyPage. */
public
int getEveryPage
(
)
{
return everyPage;
}
/** * @param everyPage * The everyPage to set. */
public
void setEveryPage
(
int everyPage
)
{
this.
everyPage = everyPage;
}
/** * @return * Returns the hasNextPage. */
public
boolean getHasNextPage
(
)
{
return hasNextPage;
}
/** * @param hasNextPage * The hasNextPage to set. */
public
void setHasNextPage
(
boolean hasNextPage
)
{
this.
hasNextPage = hasNextPage;
}
/** * @return * Returns the hasPrePage. */
public
boolean getHasPrePage
(
)
{
return hasPrePage;
}
/** * @param hasPrePage * The hasPrePage to set. */
public
void setHasPrePage
(
boolean hasPrePage
)
{
this.
hasPrePage = hasPrePage;
}
/** * @return Returns the totalPage. * */
public
int getTotalPage
(
)
{
return totalPage;
}
/** * @param totalPage * The totalPage to set. */
public
void setTotalPage
(
int totalPage
)
{
this.
totalPage = totalPage;
}
}
|
上面的这个Page类对象只是一个完整的Page描述,接下来我写了一个PageUtil,负责对Page对象进行构造:
java代码: |
/*Created on 2005-4-14*/
package org.
flyware.
util.
page;
import org.
apache.
commons.
logging.
Log;
import org.
apache.
commons.
logging.
LogFactory;
/** * @author Joa * */
public
class PageUtil
{
private
static
final Log logger = LogFactory.
getLog
(PageUtil.
class
);
/** * Use the origin page to create a new page * @param page * @param totalRecords * @return */
public
static Page createPage
(Page page,
int totalRecords
)
{
return createPage
(page.
getEveryPage
(
), page.
getCurrentPage
(
), totalRecords
);
}
/** * the basic page utils not including exception handler * @param everyPage * @param currentPage * @param totalRecords * @return page */
public
static Page createPage
(
int everyPage,
int currentPage,
int totalRecords
)
{
everyPage = getEveryPage
(everyPage
);
currentPage = getCurrentPage
(currentPage
);
int beginIndex = getBeginIndex
(everyPage, currentPage
);
int totalPage = getTotalPage
(everyPage, totalRecords
);
boolean hasNextPage = hasNextPage
(currentPage, totalPage
);
boolean hasPrePage = hasPrePage
(currentPage
);
return
new Page
(hasPrePage, hasNextPage,
everyPage, totalPage,
currentPage, beginIndex
);
}
private
static
int getEveryPage
(
int everyPage
)
{
return everyPage ==
0 ?
10 : everyPage;
}
private
static
int getCurrentPage
(
int currentPage
)
{
return currentPage ==
0 ?
1 : currentPage;
}
private
static
int getBeginIndex
(
int everyPage,
int currentPage
)
{
return
(currentPage -
1
) * everyPage;
}
private
static
int getTotalPage
(
int everyPage,
int totalRecords
)
{
int totalPage =
0;
if
(totalRecords % everyPage ==
0
)
totalPage = totalRecords / everyPage;
else
totalPage = totalRecords / everyPage +
1 ;
return totalPage;
}
private
static
boolean hasPrePage
(
int currentPage
)
{
return currentPage ==
1 ?
false :
true;
}
private
static
boolean hasNextPage
(
int currentPage,
int totalPage
)
{
return currentPage == totalPage || totalPage ==
0 ?
false :
true;
}
}
|
上面的这两个对象与具体的业务逻辑无关,可以独立和抽象。
面对一个具体的业务逻辑:分页查询出User,每页10个结果。具体做法如下: 1. 编写一个通用的结果存储类Result,这个类包含一个Page对象的信息,和一个结果集List:
java代码: |
/*Created on 2005-6-13*/
package com.
adt.
bo;
import java.
util.
List;
import org.
flyware.
util.
page.
Page;
/** * @author Joa */
public
class Result
{
private Page page;
private
List content;
/** * The default constructor */
public Result
(
)
{
super
(
);
}
/** * The constructor using fields * * @param page * @param content */
public Result
(Page page,
List content
)
{
this.
page = page;
this.
content = content;
}
/** * @return Returns the content. */
public
List getContent
(
)
{
return content;
}
/** * @return Returns the page. */
public Page getPage
(
)
{
return page;
}
/** * @param content * The content to set. */
public
void setContent
(
List content
)
{
this.
content = content;
}
/** * @param page * The page to set. */
public
void setPage
(Page page
)
{
this.
page = page;
}
}
|
2. 编写业务逻辑接口,并实现它(UserManager, UserManagerImpl)
java代码: |
/*Created on 2005-7-15*/
package com.
adt.
service;
import net.
sf.
hibernate.
HibernateException;
import org.
flyware.
util.
page.
Page;
import com.
adt.
bo.
Result;
/** * @author Joa */
public
interface UserManager
{
public Result listUser
(Page page
)
throws HibernateException;
}
|
java代码: |
/*Created on 2005-7-15*/
package com.
adt.
service.
impl;
import java.
util.
List;
import net.
sf.
hibernate.
HibernateException;
import org.
flyware.
util.
page.
Page;
import org.
flyware.
util.
page.
PageUtil;
import com.
adt.
bo.
Result;
import com.
adt.
dao.
UserDAO;
import com.
adt.
exception.
ObjectNotFoundException;
import com.
adt.
service.
UserManager;
/** * @author Joa */
public
class UserManagerImpl
implements UserManager
{
private UserDAO userDAO;
/** * @param userDAO The userDAO to set. */
public
void setUserDAO
(UserDAO userDAO
)
{
this.
userDAO = userDAO;
}
/* (non-Javadoc) * @see com.adt.service.UserManager#listUser(org.flyware.util.page.Page) */
public Result listUser
(Page page
)
throws HibernateException, ObjectNotFoundException
{
int totalRecords = userDAO.
getUserCount
(
);
if
(totalRecords ==
0
)
throw
new ObjectNotFoundException
("userNotExist"
);
page = PageUtil.
createPage
(page, totalRecords
);
List users = userDAO.
getUserByPage
(page
);
return
new Result
(page, users
);
}
}
|
其中,UserManagerImpl中调用userDAO的方法实现对User的分页查询,接下来编写UserDAO的代码: 3. UserDAO 和 UserDAOImpl:
java代码: |
/*Created on 2005-7-15*/
package com.
adt.
dao;
import java.
util.
List;
import org.
flyware.
util.
page.
Page;
import net.
sf.
hibernate.
HibernateException;
/** * @author Joa */
public
interface UserDAO
extends BaseDAO
{
public
List getUserByName
(
String name
)
throws HibernateException;
public
int getUserCount
(
)
throws HibernateException;
public
List getUserByPage
(Page page
)
throws HibernateException;
}
|
java代码: |
/*Created on 2005-7-15*/
package com.
adt.
dao.
impl;
import java.
util.
List;
import org.
flyware.
util.
page.
Page;
import net.
sf.
hibernate.
HibernateException;
import net.
sf.
hibernate.
Query;
import com.
adt.
dao.
UserDAO;
/** * @author Joa */
public
class UserDAOImpl
extends BaseDAOHibernateImpl
implements UserDAO
{
/* (non-Javadoc) * @see com.adt.dao.UserDAO#getUserByName(java.lang.String) */
public
List getUserByName
(
String name
)
throws HibernateException
{
String querySentence = "FROM user in
class com.
adt.
po.
User WHERE user.
name=:name";
Query query = getSession
(
).
createQuery
(querySentence
);
query.
setParameter
("name", name
);
return query.
list
(
);
}
/* (non-Javadoc) * @see com.adt.dao.UserDAO#getUserCount() */
public
int getUserCount
(
)
throws HibernateException
{
int count =
0;
String querySentence = "SELECT count
(*
) FROM user in
class com.
adt.
po.
User";
Query query = getSession
(
).
createQuery
(querySentence
);
count =
(
(
Integer
)query.
iterate
(
).
next
(
)
).
intValue
(
);
return count;
}
/* (non-Javadoc) * @see com.adt.dao.UserDAO#getUserByPage(org.flyware.util.page.Page) */
public
List getUserByPage
(Page page
)
throws HibernateException
{
String querySentence = "FROM user in
class com.
adt.
po.
User";
Query query = getSession
(
).
createQuery
(querySentence
);
query.
setFirstResult
(page.
getBeginIndex
(
)
)
.
setMaxResults
(page.
getEveryPage
(
)
);
return query.
list
(
);
}
}
|
至此,一个完整的分页程序完成。前台的只需要调用userManager.listUser(page)即可得到一个Page对象和结果集对象的综合体,而传入的参数page对象则可以由前台传入,如果用webwork,甚至可以直接在配置文件中指定。
下面给出一个webwork调用示例:
java代码: |
/*Created on 2005-6-17*/
package com.
adt.
action.
user;
import java.
util.
List;
import org.
apache.
commons.
logging.
Log;
import org.
apache.
commons.
logging.
LogFactory;
import org.
flyware.
util.
page.
Page;
import com.
adt.
bo.
Result;
import com.
adt.
service.
UserService;
import com.
opensymphony.
xwork.
Action;
/** * @author Joa */
public
class ListUser
implements
Action
{
private
static
final Log logger = LogFactory.
getLog
(ListUser.
class
);
private UserService userService;
private Page page;
private
List users;
/* * (non-Javadoc) * * @see com.opensymphony.xwork.Action#execute() */
public
String execute
(
)
throws
Exception
{
Result result = userService.
listUser
(page
);
page = result.
getPage
(
);
users = result.
getContent
(
);
return SUCCESS;
}
/** * @return Returns the page. */
public Page getPage
(
)
{
return page;
}
/** * @return Returns the users. */
public
List getUsers
(
)
{
return users;
}
/** * @param page * The page to set. */
public
void setPage
(Page page
)
{
this.
page = page;
}
/** * @param users * The users to set. */
public
void setUsers
(
List users
)
{
this.
users = users;
}
/** * @param userService * The userService to set. */
public
void setUserService
(UserService userService
)
{
this.
userService = userService;
}
}
|
上面的代码似乎看不出什么地方设置了page的相关初值,事实上,可以通过配置文件来进行配置,例如,我想每页显示10条记录,那么只需要:
java代码: |
<?xml version="
1.
0"?>
<!DOCTYPE xwork
PUBLIC "-
//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.0.dtd">
<xwork>
<
package name="user"
extends="webwork-interceptors">
<!-- The default interceptor stack name -->
<default-interceptor-ref name="myDefaultWebStack"/>
<action name="listUser"
class="com.
adt.
action.
user.
ListUser">
<param name="page.
everyPage">
10</param>
<result name="success">/user/user_list.
jsp</result>
</action>
</
package>
</xwork>
|
这样就可以通过配置文件和OGNL的共同作用来对page对象设置初值了。并可以通过随意修改配置文件来修改每页需要显示的记录数。
注:上面的<param>的配置,还需要webwork和Spring整合的配合。
我写的一个用于分页的类,用了泛型了,hoho
java代码: |
package com.
intokr.
util;
import java.
util.
List;
/** * 用于分页的类<br> * 可以用于传递查询的结果也可以用于传送查询的参数<br> * * @version 0.01 * @author cheng */
public
class Paginator<E>
{
private
int count =
0;
// 总记录数
private
int p =
1;
// 页编号
private
int num =
20;
// 每页的记录数
private
List<E> results =
null;
// 结果
/** * 结果总数 */
public
int getCount
(
)
{
return count;
}
public
void setCount
(
int count
)
{
this.
count = count;
}
/** * 本结果所在的页码,从1开始 * * @return Returns the pageNo. */
public
int getP
(
)
{
return p;
}
/** * if(p<=0) p=1 * * @param p */
public
void setP
(
int p
)
{
if
(p <=
0
)
p =
1;
this.
p = p;
}
/** * 每页记录数量 */
public
int getNum
(
)
{
return num;
}
/** * if(num<1) num=1 */
public
void setNum
(
int num
)
{
if
(num <
1
)
num =
1;
this.
num = num;
}
/** * 获得总页数 */
public
int getPageNum
(
)
{
return
(count -
1
) / num +
1;
}
/** * 获得本页的开始编号,为 (p-1)*num+1 */
public
int getStart
(
)
{
return
(p -
1
) * num +
1;
}
/** * @return Returns the results. */
public
List<E> getResults
(
)
{
return results;
}
public
void setResults
(
List<E> results
)
{
this.
results = results;
}
public
String toString
(
)
{
StringBuilder buff =
new StringBuilder
(
);
buff.
append
("
{"
);
buff.
append
("count:"
).
append
(count
);
buff.
append
(",p:"
).
append
(p
);
buff.
append
(",nump:"
).
append
(num
);
buff.
append
(",results:"
).
append
(results
);
buff.
append
("
}"
);
return buff.
toString
(
);
}
}
|
|