求助Hibernate的单向Many-to-one关系问题。
一、问题特征如下:
????? 我在使用单向Many-to-one关系时,主要是想用作数据字典链接,1的一端是基本不变的数据,所以希望仅在many的这边进行引用。引用的方式有:
1.创建的时候新建引用;
2.修改的时候可以更新引用;
我是用saveupdate(instance)来保存数据的,但是发现刚创建的时候保存对象没有问题,保存后马上接着修改也没有问题。
但是,我发现如果指定一个修改对象进行修改,则出错。
错误如下:
org.hibernate.HibernateException: identifier of an instance of test.Parent was altered from 2 to 1
使用merge,saveupdate,save,update等方法,都出现几乎同样的错误。让我头都大了,网上查资料,说这类的错误出现的几率少,没搜到更详细的错误报告。我一一排查,把关联映射、生产环境等去掉重来,排查到最后才发现,新建并修改不出问题,但是事后指定一个对象修改,却发生上面的错误。估计耗时两三天了吧,我估计是对象状态的问题,但又不知何从下手。。我能力有限,希望各位给些意见。
二、下面给出我的一下代码和测试报告:
1.保存对象含单向多对一关系。
被测试对象test.Child的hbm:
<hibernate-mapping>
??? <class name="test.Child" table="app_child" schema="dbo">
?????? <id name="id" column="ChildId" type="integer">
?????????? <generator type="string">
?????????? <column name="name" length="50" not-null="true">column>
?????? property>
?????? <many-to-one name="parent" column="ParentID"
?????????? outer-join="true">
?????? many-to-one>
??? </class>
</hibernate-mapping>
?
2.被关联对象test.Parent如下hbm:
<hibernate-mapping>
<class name="test.Parent" table="app_parent" schema="dbo">
??? <id name="id" column="ParentId" type="integer"><generator type="string">
??????? <column name="name" length="50" not-null="true" unique="true">column>
??? property>
</class>
</hibernate-mapping>
3.操作页面jsf。Backingbean如下:
package test;
import net.xerllent.system.web.BasePage;
import org.springframework.dao.DataAccessException;
import java.util.Map;
public class TestForm extends BasePage {
?private Child child;
?private ChildDAO childDAO;
?
?public Child getChild() {
?
? if(child==null){
//?? child=(Child)childDAO.getChild(new Integer(3));
?? child=(Child)childDAO.loadChild(new Integer(3));
?? if(child==null) {
??? child=new Child();
?? }
? }
? return child;
?}
?public void setChild(Child child) {
? this.child = child;
?}
?public void setChildDAO(ChildDAO childDAO) {
? this.childDAO = childDAO;
?}
?
?
?public String merge() {
? try {
?? childDAO.merge(child);
?? return "success";
? } catch (RuntimeException e) {
?? return "success";
? }
?}
?public String persist() {
? try {
?? childDAO.persist(child);
?? return "success";
? } catch (RuntimeException e) {
?? return "success";
? }
?}
?public String refresh() {
? try {
?? childDAO.refresh(child);
?? return "success";
? } catch (RuntimeException e) {
?? return "success";
? }
?}
?public String replicate() {
? try {
?? childDAO.replicate(child);
?? return "success";
? } catch (RuntimeException e) {
?? return "success";
? }
?}
?public String save() {
? try {
?? childDAO.save(child);
?? return "success";
? } catch (RuntimeException e) {
?? return "success";
? }
?}
?public String saveOrUpdate() {
? try {
?? childDAO.saveOrUpdate(child);
?? return "success";
? } catch (RuntimeException e) {
?? return "success";
? }
?}
?public String update() {
? try {
?? childDAO.update(child);
?? return "success";
? } catch (RuntimeException e) {
?? return "success";
? }
?}
?public Map getDictParents(){
? return childDAO.getParentsMapList();
?}
?public Map getDictParentbs(){
? return childDAO.getParentbsMapList();
?}
}
4.Spring2-Hibernate3的merge,persist,refresh,replicate,save,saveOrUpdate,update方法测试表
方法测试方案 merge persist refresh Replicate<hibernate-mapping><class name="test.Parent" table="app_parent" schema="dbo"><id name="id" column="ParentId" type="integer"><generator type="string"><column name="name" length="50" not-null="true" unique="true"></column></property><!-- <set name="children" inverse="false"><key column="ParentID"/><one-to-many name="code">package test;import net.xerllent.system.web.BasePage;import org.springframework.dao.DataAccessException;import java.util.Map;public class TestForm extends BasePage {private Child child;private ChildDAO childDAO;public Child getChild() {if(child==null){//child=(Child)childDAO.getChild(new Integer(3));child=(Child)childDAO.loadChild(new Integer(3));if(child==null) {child=new Child();}}return child;}public void setChild(Child child) {this.child = child;}public void setChildDAO(ChildDAO childDAO) {this.childDAO = childDAO;}public String merge() {try {childDAO.merge(child);return "success";} catch (RuntimeException e) {return "success";}}public String persist() {try {childDAO.persist(child);return "success";} catch (RuntimeException e) {return "success";}}public String refresh() {try {childDAO.refresh(child);return "success";} catch (RuntimeException e) {return "success";}}public String replicate() {try {childDAO.replicate(child);return "success";} catch (RuntimeException e) {return "success";}}public String save() {try {childDAO.save(child);return "success";} catch (RuntimeException e) {return "success";}}public String saveOrUpdate() {try {childDAO.saveOrUpdate(child);return "success";} catch (RuntimeException e) {return "success";}}public String update() {try {childDAO.update(child);return "success";} catch (RuntimeException e) {return "success";}}public Map getDictParents(){return childDAO.getParentsMapList();}public Map getDictParentbs(){return childDAO.getParentbsMapList();}}<%@taglib uri="http://www.xerllent.cn/donuts" prefix="xer"%><%@ page language="java" pageEncoding="GB18030"%><%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %><%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %><%@taglib uri="http://myfaces.apache.org/tomahawk" prefix="t"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";//FacesContext ctx=FacesContext.getCurrentInstance();//ChildDAO cdao=(ChildDAO)ctx.getApplication().getVariableResolver().resolveVariable(ctx,"ChildDAO");//cdao.addRole();//cdao.testdelete();%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><base href="<%=basePath%>"><title>My JSF 'testParentChild.jsp' starting page</title><meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0"> <meta http-equiv="keywords" content="keyword1,keyword2,keyword3"><meta http-equiv="description" content="This is my page"><!--<link rel="stylesheet" type="text/css" href="styles.css">--></head> <body><f:view>This is my JSF JSP page. <br><h:form><h:messages showSummary="true"></h:messages><h:inputHidden value="#{TestForm.child.id}"></h:inputHidden><h:panelGrid columns="3"><t:outputLabel for="name" value="名称"></t:outputLabel><h:inputText id="name" value="#{TestForm.child.name}" required="true"></h:inputText><h:message for="name"></h:message><t:outputLabel for="parent" value="父系统:"></t:outputLabel><t:selectOneMenu id="parent" value="#{TestForm.child.parent.id}" required="true"><f:selectItems value="#{TestForm.dictParents}"/></t:selectOneMenu><h:message for="parent"></h:message><t:outputLabel for="parentb" value="父系统b:"></t:outputLabel><t:selectOneMenu id="parentb" value="#{TestForm.child.parentb.code}" required="true"><f:selectItems value="#{TestForm.dictParentbs}"/></t:selectOneMenu><h:message for="parentb"></h:message><h:commandButton action="#{TestForm.merge}" value="merge" type="submit"/><h:commandButton action="#{TestForm.persist}" value="persist" type="submit"/><h:commandButton action="#{TestForm.refresh}" value="refresh" type="submit"/><h:commandButton action="#{TestForm.replicate}" value="replicate" type="submit"/><h:commandButton action="#{TestForm.save}" value="save" type="submit"/><h:commandButton action="#{TestForm.saveOrUpdate}" value="saveOrUpdate" type="submit"/><h:commandButton action="#{TestForm.update}" value="update" type="submit"/></h:panelGrid></h:form></f:view></body></html>package test;import java.sql.SQLException;import java.util.Iterator;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.hibernate.HibernateException;import org.hibernate.Session;import org.springframework.orm.hibernate3.HibernateCallback;import org.springframework.orm.hibernate3.support.HibernateDaoSupport;public class ChildDAOImp extends HibernateDaoSupport implements ChildDAO {private static final Log log = LogFactory.getLog(Child.class);/* (non-Javadoc) * @see test.ChildDAO#getChild(java.lang.Integer) */public Child getChild(Integer id){return (Child)getHibernateTemplate().get(test.Child.class, id);}/* (non-Javadoc) * @see test.ChildDAO#loadChild(java.lang.Integer) */public Child loadChild(Integer id){return (Child)getHibernateTemplate().load(test.Child.class, id);}/* (non-Javadoc) * @see test.ChildDAO#getAllParents() */public List getAllParents(){try {String queryString = "from Parent";return getHibernateTemplate().find(queryString);} catch (RuntimeException re) {log.error("find all failed", re);throw re;}}/* (non-Javadoc) * @see test.ChildDAO#getAllParents() */public List getAllParentbs(){try {String queryString = "from Parentb";return getHibernateTemplate().find(queryString);} catch (RuntimeException re) {log.error("find all failed", re);throw re;}}/* (non-Javadoc) * @see test.ChildDAO#getParentsMapList() */public Map getParentsMapList(){Map options=new LinkedHashMap();options.put("请选择..", "");List parents= getAllParents(); for (Iterator it = parents.iterator(); it.hasNext();) { Parent option = (Parent) it.next(); options.put(option.getName(), option.getId().toString()); }return options;}/* (non-Javadoc) * @see test.ChildDAO#getParentsMapList() */public Map getParentbsMapList(){Map options=new LinkedHashMap();options.put("请选择..", "");List parents= getAllParentbs(); for (Iterator it = parents.iterator(); it.hasNext();) { Parentb option = (Parentb) it.next(); options.put(option.getName(), option.getCode()); }return options;}public void saveOrUpdate(Child chd){getHibernateTemplate().saveOrUpdate(chd);}public void merge(Child chd){getHibernateTemplate().merge(chd);}public void refresh(Child chd){getHibernateTemplate().refresh(chd);}public void save(Child chd){getHibernateTemplate().save(chd);}public void persist(Child chd){getHibernateTemplate().persist(chd);}public void update(Child chd){getHibernateTemplate().update(chd);}public void replicate(Child chd){getHibernateTemplate().replicate(chd, org.hibernate.ReplicationMode.OVERWRITE);}}[donuts] DEBUG [http-8080-2] HibernateTransactionManager.handleExistingTransaction(444) | Participating in existing transaction[donuts] DEBUG [http-8080-2] TransactionInterceptor.prepareTransactionInfo(282) | Getting transaction for [test.ChildDAO.update][donuts] DEBUG [http-8080-2] TransactionSynchronizationManager.getResource(140) | Retrieved value [org.springframework.orm.hibernate3.SessionHolder@204443] for key [org.hibernate.impl.SessionFactoryImpl@f61227] bound to thread [http-8080-2][donuts] DEBUG [http-8080-2] TransactionSynchronizationManager.getResource(140) | Retrieved value [org.springframework.orm.hibernate3.SessionHolder@204443] for key [org.hibernate.impl.SessionFactoryImpl@f61227] bound to thread [http-8080-2][donuts] DEBUG [http-8080-2] HibernateTemplate.execute(364) | Found thread-bound Session for HibernateTemplate[donuts] DEBUG [http-8080-2] HibernateTemplate.execute(388) | Not closing pre-bound Hibernate Session after HibernateTemplate[donuts] DEBUG [http-8080-2] TransactionInterceptor.commitTransactionAfterReturning(312) | Completing transaction for [test.ChildDAO.update][donuts] DEBUG [http-8080-2] TransactionInterceptor.commitTransactionAfterReturning(312) | Completing transaction for [test.ChildDAO.update][donuts] DEBUG [http-8080-2] HibernateTransactionManager.triggerBeforeCommit(833) | Triggering beforeCommit synchronization[donuts] DEBUG [http-8080-2] HibernateTransactionManager.triggerBeforeCompletion(846) | Triggering beforeCompletion synchronization[donuts] DEBUG [http-8080-2] HibernateTransactionManager.processCommit(660) | Initiating transaction commit[donuts] DEBUG [http-8080-2] HibernateTransactionManager.doCommit(571) | Committing Hibernate transaction on Session [org.hibernate.impl.SessionImpl@1077c9d][donuts] DEBUG [http-8080-2] JDBCTransaction.commit(103) | commit[donuts] DEBUG [http-8080-2] AbstractFlushingEventListener.prepareEntityFlushes(111) | processing flush-time cascades[donuts] DEBUG [http-8080-2] AbstractFlushingEventListener.prepareCollectionFlushes(154) | dirty checking collections[donuts] DEBUG [http-8080-2] HibernateTransactionManager.doRollbackOnCommitException(801) | Initiating transaction rollback after commit exceptionorg.springframework.orm.hibernate3.HibernateSystemException: identifier of an instance of test.Parent was altered from 1 to 2; nested exception is org.hibernate.HibernateException: identifier of an instance of test.Parent was altered from 1 to 2Caused by: org.hibernate.HibernateException: identifier of an instance of test.Parent was altered from 1 to 2at org.hibernate.event.def.DefaultFlushEntityEventListener.checkId(DefaultFlushEntityEventListener.java:58)at org.hibernate.event.def.DefaultFlushEntityEventListener.getValues(DefaultFlushEntityEventListener.java:157)at org.hibernate.event.def.DefaultFlushEntityEventListener.onFlushEntity(DefaultFlushEntityEventListener.java:113)at org.hibernate.event.def.AbstractFlushingEventListener.flushEntities(AbstractFlushingEventListener.java:196)at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:76)at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:26)at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:575)at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:314)at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:117)at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:161)at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)at $Proxy10.update(Unknown Source)at test.TestForm.update(TestForm.java:89)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)at java.lang.reflect.Method.invoke(Unknown Source)at org.apache.el.parser.AstValue.invoke(AstValue.java:131)at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:276)at org.apache.jasper.el.JspMethodExpression.invoke(JspMethodExpression.java:68)at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:77)at org.apache.myfaces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:54)at javax.faces.component.UICommand.broadcast(UICommand.java:383)at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:447)at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:752)at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:97)at com.sun.faces.lifecycle.LifecycleImpl.phase(LifecycleImpl.java:251)at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:117)at javax.faces.webapp.FacesServlet.service(FacesServlet.java:244)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:198)at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:75)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)at org.apache.myfaces.webapp.filter.ExtensionsFilter.doFilter(ExtensionsFilter.java:147)at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:230)at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:104)at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:261)at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:852)at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:584)at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1508)at java.lang.Thread.run(Unknown Source)[donuts] DEBUG [http-8080-2] HibernateTransactionManager.doRollback(590) | Rolling back Hibernate transaction on Session [org.hibernate.impl.SessionImpl@1077c9d][donuts] DEBUG [http-8080-2] JDBCTransaction.rollback(152) | rollback[donuts] DEBUG [http-8080-2] JDBCTransaction.toggleAutoCommit(193) | re-enabling autocommit[donuts] DEBUG [http-8080-2] JDBCTransaction.rollback(163) | rolled back JDBC Connection[donuts] DEBUG [http-8080-2] ConnectionManager.afterTransaction(302) | transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources![donuts] DEBUG [http-8080-2] HibernateTransactionManager.triggerAfterCompletion(875) | Triggering afterCompletion synchronization[donuts] DEBUG [http-8080-2] TransactionSynchronizationManager.clearSynchronization(276) | Clearing transaction synchronization[donuts] DEBUG [http-8080-2] TransactionSynchronizationManager.unbindResource(193) | Removed value [org.springframework.jdbc.datasource.ConnectionHolder@19747c9] for key [org.apache.commons.dbcp.BasicDataSource@190690e] from thread [http-8080-2][donuts] DEBUG [http-8080-2] HibernateTransactionManager.doCleanupAfterCompletion(657) | Not closing pre-bound Hibernate Session [org.hibernate.impl.SessionImpl@1077c9d] after transaction[donuts] DEBUG [http-8080-2] SessionImpl.disconnect(374) | disconnecting session[donuts] DEBUG [http-8080-2] ConnectionManager.closeConnection(441) | releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)][donuts] DEBUG [http-8080-2] ConnectionManager.afterTransaction(302) | transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources![donuts] DEBUG [http-8080-2] DelegatingVariableResolver.resolveVariable(106) | Attempting to resolve variable 'TestForm' in via original VariableResolver/* (non-Javadoc) * @see net.xerllent.system.dao.imp.BaseDao#attachClean(java.lang.Object) */public void attachClean(Object instance) {log.debug("attaching clean Object instance");try {getHibernateTemplate().lock(instance, LockMode.NONE);log.debug("attach successful");} catch (RuntimeException re) {log.error("attach failed", re);throw re;}}public Child getChild() {if(child==null){child=(Child)childDAO.getChild(new Integer(4));//child=(Child)childDAO.loadChild(new Integer(3));if(child==null) {child=new Child();}childDAO.attachClean(child);childDAO.attachClean(child.getParent());childDAO.attachClean(child.getParentb());}return child;}public String update() {try {//得到新的Id及其持久化对象p,并重新设置child的parentInteger pid=child.getParent().getId();Parent p=childDAO.getParentById(pid);child.setParent(p);childDAO.update(child);//此步依然出现错误!!!!return "success";} catch (RuntimeException e) {return "success";}}