今天在调试hibernate的Dao层单例的时候,在一个查询用户的概览页面,第一次刷新页面的时候,用户列表能正确展示。但是当用F5刷新页面的时候,发现用户列表不能正确的展示出来,报出一个hibernate异常,Session is Closed!由于我采用的是one-session-per-request模式,按道理session不应该会被关闭,除非该session会被commit或者close掉。重新读了一遍代码,没有发现有这样的代码,在无法确定问题的时候,我只能把当前报错的位置的session的hashcode打印出来,结果报错位置的session的hashCode永远与第一次进入页面的session的hashCode一致,相当于永远使用了一个UserMgr(Dao层)对象。仔细看下UserMgr对象,果不其然,我是用的是单例模式,而单例模式只set了一次session,所以以后每次从UserMgr中取的session对象都是被关闭掉的session对象。修改一下代码,把每次使用的session都从当前线程中获取,错误解决。
主要注意事项:
(1)hibernate.cfg.xml中增加以下配置,设置session的在当前线程中有效。
<!-- 设置 上下文运行条件,value可以为:jta/thread/managed -->
<property name="hibernate.current_session_context_class">thread</property>
(2)通过HibernateSessionRequestFilter的一个filter来保证“one-session-per-request模式”
(3)所有Mgr对象(Dao)都要继承自BaseHibernateDAO类,这个类中提供了一些cud的一些基本操作
1.HibernateSessionRequestFilter的代码如下:
/**
* 提供所有的jsp访问的时候,执行hibernate的 Transaction的开始和提交
*
* @author rey
*
*/
public class HibernateSessionRequestFilter implements Filter {
/**
* 记录日志
*/
private static Logger logger = Logger
.getLogger(HibernateSessionRequestFilter.class);
private SessionFactory sf;
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#destroy()
*/
@Override
public void destroy() {
sf.close();
}
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest _request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
sf.getCurrentSession().beginTransaction();
if (logger.isDebugEnabled()) {
logger
.debug("Starting a database transaction,session.hashCode::"
+ sf.getCurrentSession().hashCode());
}
// Call the next filter (continue request processing)
chain.doFilter(_request, response);
// Commit and cleanup
if (logger.isDebugEnabled()) {
logger
.debug("Committing the database transaction,session.hashCode::"
+ sf.getCurrentSession().hashCode());
}
sf.getCurrentSession().getTransaction().commit();
} catch (StaleObjectStateException staleEx) {
logger
.error("This interceptor does not implement optimistic concurrency control!");
logger
.error("Your application will not work until you add compensation actions!");
// Rollback, close everything, possibly compensate for any permanent
// changes
// during the conversation, and finally restart business
// conversation. Maybe
// give the user of the application a chance to merge some of his
// work with
// fresh data... what you do here depends on your applications
// design.
throw staleEx;
} catch (Throwable ex) {
// Rollback only
ex.printStackTrace();
try {
if (sf.getCurrentSession().getTransaction().isActive()) {
logger
.debug("Trying to rollback database transaction after exception");
sf.getCurrentSession().getTransaction().rollback();
}
} catch (Throwable rbEx) {
logger.error("Could not rollback transaction after exception!",
rbEx);
}
// Let others handle it... maybe another interceptor for exceptions?
throw new ServletException(ex);
}
}
/*
* (non-Javadoc)
*
* @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
*/
@Override
public void init(FilterConfig arg0) throws ServletException {
logger.debug("Initializing filter...");
logger
.debug("Obtaining SessionFactory from static HibernateUtil singleton");
sf = HibernateUtil.getSessionFactory();
}
}
2.BaseHibernateDAO代码如下:
/**
* 所有Mgr类都要继承的抽象类
*
* @author rey
*
*/
public class BaseHibernateDAO<T, ID extends Serializable> implements
IBaseHibernateDAO<T, ID> {
private Class<T> persistentClass;
public BaseHibernateDAO() {
// 根据泛型中传递的参数T来初始化当前需要持久化的对象,取第一个参数,跟泛型的参数位置有关系
this.persistentClass = (Class<T>) ((ParameterizedType) getClass()
.getGenericSuperclass()).getActualTypeArguments()[0];
}
/**
* 获取当前mgr中的session,由于Mgr大部分都被设计成单例,所以每次都需要从当前thread中获取
*
* @return
*/
protected Session getSession() {
// 由于是单例模式,所以每次session都要重新获取,从当前线程中获取
return HibernateUtil.getCurrentSession();
}
public Class<T> getPersistentClass() {
return persistentClass;
}
@SuppressWarnings("unchecked")
public T findById(ID id, boolean lock) {
T entity;
if (lock)
entity = (T) getSession().load(getPersistentClass(), id,
LockMode.UPGRADE);
else
entity = (T) getSession().load(getPersistentClass(), id);
return entity;
}
@SuppressWarnings("unchecked")
public List<T> findAll() {
return findByCriteria();
}
/**
* 根据实例的条件来查询集合<BR>
* Example(继承了Criteria对象)过滤条件来查询指定对象集合
*
* @param exampleInstance
* 需要查询的类的对象实例,不是xx.class
* @param excludeProperty
* 不提取的字段名称
* @return
*/
@SuppressWarnings("unchecked")
public List<T> findByExample(T exampleInstance, String[] excludeProperty) {
Criteria crit = getSession().createCriteria(getPersistentClass());
Example example = Example.create(exampleInstance);
for (String exclude : excludeProperty) {
example.excludeProperty(exclude);
}
crit.add(example);
return crit.list();
}
@SuppressWarnings("unchecked")
public T makePersistent(T entity) {
getSession().saveOrUpdate(entity);
return entity;
}
public void makeTransient(T entity) {
getSession().delete(entity);
}
public void flush() {
getSession().flush();
}
public void clear() {
getSession().clear();
}
/**
* 根据过滤条件,查询满足条件的集合 <BR>
* Criterion:为过滤条件<BR>
* Criteria:过滤条件的执行者 <BR>
* Use this inside subclasses as a convenience method.
*/
@SuppressWarnings("unchecked")
protected List<T> findByCriteria(Criterion... criterion) {
Criteria crit = getSession().createCriteria(getPersistentClass());
for (Criterion c : criterion) {
crit.add(c);
}
return crit.list();
}
/**
* 查询一个对象的个数,按照这个字段和条件来统计这个字段
*
* @param _sCountSelect
* 需要统计的字段,一般为一个持久化对象的主键id
* @param criterion
* 需要按照某种条件来过滤和统计
* @return
* @throws MyException
*/
public int getTotalSize(String _sCountSelect, Criterion... criterion) throws MyException {
Criteria crit = getSession().createCriteria(getPersistentClass());
crit.setProjection(Projections.count(_sCountSelect));
for (Criterion c : criterion) {
crit.add(c);
}
Object oTotalNumber = crit.uniqueResult();
if (oTotalNumber == null) {
throw new MyException("查询总条数时发生错误[CountSelect::" + _sCountSelect
+ "]");
}
return Integer.parseInt(oTotalNumber.toString());
}
/**
*
* @param _nPageIndex
* 查询页面索引,第一页从1开始
* @param _nPageSize
* 页面显示的记录数
* @param _nItemCoun
* 记录总数
* @param _criterion
* 查询条件
* @return
*/
protected List<T> getPageItems(int _nPageIndex, int _nPageSize,
int _nItemCoun) {
Criteria crit = getSession().createCriteria(getPersistentClass());
// 构造分页参数
MyPage CurrPage = new MyPage(_nPageSize);
CurrPage.setItemCount(_nItemCoun);
CurrPage.setCurrPageIndex(_nPageIndex);
// 这里的起始值是0,MyPage的起始值为1
crit.setFirstResult(CurrPage.getFirstItemIndex() - 1);
crit.setMaxResults(CurrPage.getCurrPageSize()); // 获取当前页大小
return crit.list();
}
@Override
public List<T> findByExample(T exampleInstance) {
// TODO Auto-generated method stub
return null;
}
}
3.继承自BaseHibernateDAO类的一个Mgr对象,UserMgr代码:
/**
* 用户对象业务逻辑管理类,只负责业务逻辑的处理
*
* @author rey
*
*/
public class UserMgr extends BaseHibernateDAO<User, Long> {
/**
* 单例对象
*/
private static UserMgr m_SingleMgr = null;
/**
* 单例方法
*
* @return
*/
public static UserMgr getInstance() {
if (m_SingleMgr == null) {
m_SingleMgr = new UserMgr();
}
return m_SingleMgr;
}
// 构造方法
public UserMgr() {
super();
}
/**
* 根据用户id获取用户对象
*
* @param _lUserId
* 用户id
* @return
* @throws MyException
*/
public User findById(long _lUserId) {
return (User) super.findById(_lUserId, false);
}
// 提供外接的查询函数================
/**
* 保存用户到数据库中,包括新增和修改操作
*
* @param _saveUser
* 要保存的用户
* @return
* @throws MyException
*/
public User saveOrUpdate(User _saveUser) throws MyException {
if (_saveUser == null)
throw new MyException("保存的用户对象为null!");
return (User) this.makePersistent(_saveUser);
}
/**
* 删除一个用户
*
* @param _deleteUser
* 要删除的用户对象
* @throws MyException
*/
public void delete(User _deleteUser) throws MyException {
if (_deleteUser == null)
throw new MyException("要删除的用户对象为null!");
this.makeTransient(_deleteUser);
}
/**
* 删除指定ids的用户信息,返回删除的用户个数
*
* @param _sDeleteUserIds
* @throws MyException
*/
public int delete(String _sDeleteUserIds) throws MyException {
// 1.如果用sql删除,则可能缓存会有问题
// 2.如果一条一条删除则会有性能问题,综合考虑采用批处理来删除
if (_sDeleteUserIds == null) {
return 0;
}
StringBuilder sbHQL = new StringBuilder(
"delete from User where UserId in(?");
String[] aUserIds = _sDeleteUserIds.split(",");
for (int i = 1; i < aUserIds.length; i++) {
sbHQL.append(",?");
}
sbHQL.append(")");
// 构造查询
Query deleteQuery = getSession().createQuery(sbHQL.toString());
// 添加参数
for (int i = 0; i < aUserIds.length; i++) {
deleteQuery.setString(i, aUserIds[i]);
}
return deleteQuery.executeUpdate();
}
/**
*
* @param _nPageIndex
* 查询页面索引,第一页从1开始
* @param _nPageSize
* 页面记录条数
*
* @param _nItemCount
* 页面记录总条数
* @return
*/
public List<User> getPageUsers(int _nPageIndex, int _nPageSize,
int _nItemCount) {
return this.getPageItems(_nPageIndex, _nPageSize, _nItemCount);
}
}