产品提交测试小组,返回如下异常bug
Hibernate:
update
MAILBOX_VIEW
set
name=?,
homedir=?,
domainid=?,
groupid=?,
max_msg_count=?,
quota=?,
usagespace=?,
valid_msg_count=?,
status=?,
zone=?,
scope=?
where
id=?
[class/org.hibernate.util.JDBCExceptionReporter] Thread:pool3_thread5 WARN : 2007-10-29 11:31:10 SQL Error: 17008, SQLState: null
[class/org.hibernate.util.JDBCExceptionReporter] Thread:pool3_thread5 ERROR: 2007-10-29 11:31:10 ?????
[class/org.hibernate.event.def.AbstractFlushingEventListener] Thread:pool3_thread5 ERROR: 2007-10-29 11:31:10 Could not synchronize database state with session
org.hibernate.exception.GenericJDBCException: could not update: [com.ceno.mail.mailbox.persistent.MailBoxPO#2000060]
at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:103)
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:91)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2360)
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2242)
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2542)
at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:92)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:248)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:232)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultAutoFlushEventListener.onAutoFlush(DefaultAutoFlushEventListener.java:41)
at org.hibernate.impl.SessionImpl.autoFlushIfRequired(SessionImpl.java:969)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1114)
at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
at com.ceno.orm.hibernate.HibernateDAOSupport$HibernateTemplate.find(HibernateDAOSupport.java:1291)
at com.ceno.mail.user.persistent.MailUserDAO.findMailUser(MailUserDAO.java:313)
at com.ceno.mail.user.persistent.MailUserDAO.getUser(MailUserDAO.java:350)
at com.ceno.mail.user.implement.MailUserProxyManager.getUserByName(MailUserProxyManager.java:289)
at com.ceno.mail.user.persistent.MailUserDAO.getUserByName(MailUserDAO.java:400)
at com.ceno.mail.user.implement.MailUserProxyManager.getUserByName(MailUserProxyManager.java:88)
at com.ceno.mail.user.MailUserUtil.getUserByName(MailUserUtil.java:70)
at com.ceno.mail.login.MailUserAuthModule.login(MailUserAuthModule.java:49)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:585)
我们在产品试运行期间从没有发现该问题,该异常是在做Query操作抛出的,经分析后做出如下判断:
1、该段代码本身没有对MailBoxViewPO做任何操作,脏数据是之前遗留的。
2、该段代码前面没有任何Hibernate操作调用,脏数据不会是在同一个thread调用引起的。
3、该段代码对HibernateSession的open/close操作是成对出现的,全部hibernate操作都通过HibernateTemplate来调用,不会出现session遗留问题。
那么问题会在什么地方,难道close的时候数据没有清除掉?再看HibernateSessionFactory的实现,每次OpenSession的时候都是new HibernateSessionImpl对象,不存在这个问题。
灵光一现,Session对象不会被复用,但我们的Thread是复用的,而在获取session的时候会把session捆在ThreadLocal里面,getSession的代码如下:
/**
* Retrieves the current Session local to the thread.
* <p/>
* If no Session is open, opens a new Session for the running thread.
*
* @return Session
*/
public static Session getSession()
{
CountSession s = (CountSession) threadSession.get();
if (s == null) {
if (log.isDebug()) {
log.debug("Opening new Session for this thread.");
}
s = new CountSession();
if (getInterceptor() != null) {
if (log.isDebug()) {
log.debug("Using interceptor: " + getInterceptor().getClass());
}
s.setSession(getSessionFactory().openSession(getInterceptor()));
}
else {
s.setSession(getSessionFactory().openSession());
}
s.setCount(1);
threadSession.set(s);
}
else {
s.setCount(s.getCount() + 1);
}
return s.getSession();
}
MailUserUtil所在代码模块的Hibernate调用都通过HibernateTemplate来进行,否认该模块会出现没有调用close操作把session从threadLocal里面移走的低级错误。怀疑的重点到业务和管理系统模块的老代码上,查找老代码的HibernateUtil.getSession(),发现所有代码在调用hibernate操作后都会在finally处调用HibernateUtil.closeSession()。
再次陷入无线索状态中,再次仔细检查调用Hibernate操作的老代码,发现如下一些代码:
Zone[] zones = new BusinessZonePO[0];
Session session = HibernateUtil.getSession();
try {
HibernateUtil.beginTransaction();
Query query = session.createQuery("from BusinessZonePO where pid=?");
query.setInteger(0, pid);
List list = query.list();
HibernateUtil.commitTransaction();
if (list != null && list.size() > 0) {
zones = (Zone[]) list.toArray(zones);
}
}
catch (HibernateException e) {
HibernateUtil.rollbackTransaction();
throw e;
}
finally {
HibernateUtil.closeSession();
}
return zones;
有一部份类似的调用都是在getSession后,再beginTransaction,这里代码写法明显有问题,如果在begin和commit之间抛出Unchecked Exception,那么commit和rollback都不会执行。把怀疑重点转到这部分代码上来了,调来HibernateUtil.beginTransaction的实现:
Transaction tx = (Transaction) threadTransaction.get();
if (tx == null) {
if (log.isDebug()) {
log.debug("Starting new database transaction in this thread.");
}
tx = getSession().beginTransaction();
threadTransaction.set(tx);
}
一看代码基本上有些眉目了,在这个实现里面,再次调用了getSession,在beginTransaction之前已经调用了一次getSession,这意味着这一次getSession调用会从ThreadLocal对旬里面取到CountSession,并把count置为2。
这样如果没有调用commitTransaction,只会调finally一次closeSession,closeSession的代码如下:
CountSession s = (CountSession) threadSession.get();
if (s != null) {
if (s.getCount() <= 1) {
threadSession.set(null);
threadTransaction.set(null);
s.close();
}
else {
s.setCount(s.getCount() - 1);
}
}
如果只调用一次closeSession,那么CountSession对象只会把count减1,并不会从ThreadLocal里面移走。这时候思路渐渐清晰了,再检查HibernateUtil.commitTransaction的实现,问题马上就找到了:
Transaction tx = (Transaction) threadTransaction.get();
try {
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
if (log.isDebug()) {
log.debug("Committing database transaction of this thread.");
}
tx.commit();
}
threadTransaction.set(null);
}
catch (HibernateException ex) {
rollbackTransaction();
throw ex;
}
原是是怀疑在begin和commit之间抛出Unchecked Exception,导致commit没调用,检查这段代码才发现commit里面根本没有调用closeSession,导致session残留在ThreadLocal对象里面,在thread被回收到thread pool并下一次被取出调用,该session第二次使用,而这时候session里面会残留大量po对象,有些可能为dirty data。
在commitTransaction里面加入finally{closeSession();},问题解决。
总结:
1、同时使用ThreadLocal和Thread Pool需十分谨慎检查thread回到pool前,确保local对象一定被释放。
2、对于Session或Connection之类的有限资源,必须通过模板机制保证close/open成对出现,原则上不允许调用者自已进行显示的open/close,一旦代码出现问题,会对系统造成十分严重的影响。
3、深入理解catch/finally,Unchecked Exception。
分享到:
相关推荐
博文链接虽然没有提供,但通常这类博客会包含作者在模仿Hibernate过程中的代码示例和实现思路,帮助读者理解如何构建类似功能的框架。 【标签】"源码"和"工具"表明我们将会关注源代码级别的实现,以及这个自定义...
本文将针对“高板撒三框架”(假设此处指的是Spring、Hibernate、MyBatis三大框架)中的常见异常进行总结,并提供相应的解决思路,旨在帮助初学者快速定位并解决问题。 #### 标题解析 标题“高板撒三框架的常见异常...
2. **需求分析与抽象**:将业务需求中的实体和行为抽象为Hibernate中的实体和相关服务接口及方法。 3. **面向对象思考**:在设计映射时不直接考虑SQL语句,而是侧重于面向对象的设计思路。完成后可再考虑SQL语句优化...
11. 异常处理:学习Hibernate常见的异常,如ConstraintViolationException、ObjectRetrievalFailureException等,以及如何进行相应的错误处理。 12. 实战演练:结合提供的"代码"和"思路图",通过实际编写和运行代码...
其中,可能包括了Action类(Struts2的核心组件,处理用户请求),DAO(数据访问对象,与Hibernate交互)类,实体类(与数据库表对应的Java对象),配置文件(如Hibernate的配置文件hibernate.cfg.xml,Struts2的...
标签“源码”和“工具”提示我们,解决问题可能需要查看Hibernate的相关源码,理解其内部工作机制,同时也可能需要借助一些开发工具如IDE的调试功能来辅助排查。 在“Hibernate_OneToOne_bi”这个压缩文件中,很...
2. **Spring配置**:在Spring的配置文件(applicationContext.xml)中,我们需要配置数据源、Hibernate的SessionFactory以及Service和DAO层的Bean。使用@Autowired注解实现依赖注入,使得Struts2 Action可以直接使用...
Java EE数据持久层解决方案的设计与实现是企业级应用开发中的关键环节,主要...同时,这种设计思想也符合“学中做,做中学,学会做,独立做”的教学改革思路,让学生在实践中掌握更实用的技术,提高解决问题的能力。
此外,"程序设计与框架I课程设计报告"进一步详细阐述了系统的设计思路、架构选择及实现过程,包括需求分析、系统设计、模块划分、数据库设计、各层之间的交互以及具体的技术实现细节,对于理解整个系统有极大的帮助...
同时,良好的异常处理和日志记录也是系统不可或缺的部分,能够帮助开发者调试和排查问题。 总的来说,这个用户管理系统展示了如何将JSP、Struts2和Hibernate结合使用,以实现一个功能完善的用户登录和管理应用。...
本项目采用了Struts2和Hibernate这两个强大的开源框架,旨在提供高效、稳定且易于维护的解决方案。Struts2作为MVC(Model-View-Controller)架构的一部分,负责处理前端请求并控制业务流程,而Hibernate则作为ORM...
【酒店管理系统】是一款基于Struts和Hibernate框架的典型企业级应用,它主要用于管理酒店的日常运营,提升工作效率。Struts是Java EE平台上的一个MVC(Model-View-Controller)框架,而Hibernate则是一个强大的对象...
- **异常处理**:使用try-catch块捕获并处理可能出现的异常,确保事务回滚及Session关闭。 - **删除操作**:`public void delete(Object obj) throws Exception {`,此方法接收一个泛型对象作为参数,并从数据库...
- **文档编写**: 详细记录系统架构及关键组件设计思路。 通过以上内容的学习,开发者可以全面了解 Hibernate 3.2 的核心特性和使用方法,进而提高应用系统的数据访问效率和稳定性。在实际开发过程中,还需要根据...
"Hibernate+Struts分页的实现"这个话题关注的就是如何在大型数据集的场景下,结合这两种技术来有效地实施分页策略。 首先,让我们了解一下Struts框架。Struts是一个基于MVC(Model-View-Controller)设计模式的Java...
在SSH整合中,Hibernate作为数据访问层,处理数据库的CRUD操作。 3. **Struts框架** Struts是基于MVC设计模式的Web应用程序框架,主要用于控制层。它通过Action和ActionForm类来处理用户请求,并将结果转发到相应...
- **持久层**:采用Hibernate框架进行数据存取操作。 #### 四、表示层:Struts框架的应用 Struts是一个成熟的MVC框架,用于构建动态Web应用。其主要职责包括: 1. **请求处理**:管理用户请求并做出相应响应。 2....
在Java企业级应用开发中,经常需要处理大量数据的展示和查询问题。为了提高用户体验并优化服务器资源利用,分页查询是一种非常实用的技术手段。本文将详细介绍如何在Hibernate与Spring框架集成的环境下实现分页功能...