問題描述
Hibernate 單向父/子關係 - delete() 對子表執行更新而不是刪除 (Hibernate Unidirectional Parent/Child relationship - delete() performs update on child table instead of delete)
If I delete a record from the Parent table I want the corresponding records in the child table to be deleted. How can I make Hibernate delete from the Child table rather than attempt to update with a null?
I'm using Hibernate 3 but cannot use annotations at this time. I've attached copies of HBM, DAO etc below. -- Thank you in Advance
When attempting to delete data from tables in Parent/Child relationship I get the following error:
Testcase: testDelete(com.dressbarn.imbo.model.data.hibernate.dao.CharityTransferDAOTest): Caused an ERROR
Hibernate flushing: Could not execute JDBC batch update; uncategorized SQLException for SQL [update RMS12.DRS_CHARITY_TRANSFER_ITEM set TSF_NO=null, TSF_SEQ_NO=null where TSF_NO=?]; SQL state [72000]; error code [1407]; ORA-01407: cannot update ("RMS12"."DRS_CHARITY_TRANSFER_ITEM"."TSF_NO") to NULL
; nested exception is java.sql.BatchUpdateException: ORA-01407: cannot update ("RMS12"."DRS_CHARITY_TRANSFER_ITEM"."TSF_NO") to NULL
org.springframework.jdbc.UncategorizedSQLException: Hibernate flushing: Could not execute JDBC batch update; uncategorized SQLException for SQL [update RMS12.DRS_CHARITY_TRANSFER_ITEM set TSF_NO=null, TSF_SEQ_NO=null where TSF_NO=?]; SQL state [72000]; error code [1407]; ORA-01407: cannot update ("RMS12"."DRS_CHARITY_TRANSFER_ITEM"."TSF_NO") to NULL
; nested exception is java.sql.BatchUpdateException: ORA-01407: cannot update ("RMS12"."DRS_CHARITY_TRANSFER_ITEM"."TSF_NO") to NULL
Caused by: java.sql.BatchUpdateException: ORA-01407: cannot update ("RMS12"."DRS_CHARITY_TRANSFER_ITEM"."TSF_NO") to NULL
at oracle.jdbc.driver.DatabaseError.throwBatchUpdateException(DatabaseError.java:498)
at oracle.jdbc.driver.OraclePreparedStatement.executeBatch(OraclePreparedStatement.java:12368)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:48)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:246)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:237)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:143)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
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:578)
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:116)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:171)
at org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:629)
at com.dressbarn.imbo.model.data.hibernate.dao.CharityTransferDAO$$EnhancerByCGLIB$$6a21cd58.delete(<generated>)
at com.dressbarn.imbo.model.data.hibernate.dao.CharityTransferDAOTest.testDelete(CharityTransferDAOTest.java:112)
at org.springframework.test.ConditionalTestCase.runBare(ConditionalTestCase.java:69)
My tables are:
Parent:
CREATE TABLE DRS_CHARITY_TRANSFER
(
TSF_NO NUMBER(10) NOT NULL Primary Key,
FROM_LOC NUMBER(10),
CHARITY_LOC_ID NUMBER(10),
STATUS VARCHAR2(1 CHAR),
CREATE_DATE DATE,
EXT_REF_NO VARCHAR2(30 CHAR),
COMMENT_DESC VARCHAR2(2000 CHAR),
USER_ID VARCHAR2(30 CHAR)
)
Child:
CREATE TABLE DRS_CHARITY_TRANSFER_ITEM
(
TSF_NO NUMBER(10) NOT NULL PRIMARY KEY,
ITEM VARCHAR2(25 BYTE) NOT NULL PRIMARY KEY,
TSF_SEQ_NO INTEGER,
TSF_QTY INTEGER
)
HBM XML
<hibernate-mapping package="com.dressbarn.imbo.model.data.hibernate.transfer" schema="RMS12">
<class name="CharityTransfer" table="DRS_CHARITY_TRANSFER">
<id name="transferNumber" column="TSF_NO" unsaved-value="undefined">
</id>
<property column="FROM_LOC" length="10" name="fromLocation" type="java.lang.Long"/>
<property column="CHARITY_LOC_ID" length="10" name="toCharityLocId" type="java.lang.Long"/>
<property column="STATUS" name="status" type="string"/>
<property column="EXT_REF_NO" name="documentNumber" type="string"/>
<property column="COMMENT_DESC" name="comment" type="string"/>
<property column="CREATE_DATE" name="createDate" type="string"/>
<property column="USER_ID" name="userId" type="string"/>
<list name="charityTransferItemList" cascade="all-delete-orphan" lazy="false">
<key column="TSF_NO" />
<list-index column="TSF_SEQ_NO"/>
<one-to-many class="CharityTransferItem" />
</list>
</class>
<class name="CharityTransferItem" table="DRS_CHARITY_TRANSFER_ITEM">
<id name="item" column="TSF_NO" unsaved-value="undefined">
</id>
<property column="ITEM" name="item" type="string"/>
<property column="TSF_SEQ_NO" length="10" name="sequence" type="integer"/>
<property column="TSF_QTY" length="12" name="quantity" type="long"/>
</class>
DAO
public class CharityTransferDAO extends HibernateDaoSupport implements ICharityTransfer {
public void delete(CharityTransfer charityTransfer) throws IMADataException {
try {
getSessionFactory()
.getCurrentSession()
.delete(charityTransfer);
}
catch (HibernateException e) {
throw new IMADataException("failed to delete charity shipping information", e);
}
}
參考解法
方法 1:
I encountered this error all the time.
Just put an inverse="true" on the relationship and your problem will go away!
<list name="charityTransferItemList" inverse="true" cascade="all-delete-orphan" lazy="false" >
<key column="TSF_NO" />
<list-index column="TSF_SEQ_NO"/>
<one-to-many class="CharityTransferItem" />
</list>
Basically the inverse will tell hibernate the child cannot exist without a parent, thereby causing hibernate to delete the child.
Having said that, you'll also need to remove the charityTransfer object from the collection in the parent as well.
方法 2:
(Note: This has been raised as an incorrect answer by two users).
Hibernate docs say you can just mark the key column as not-null="true"
:
<list name="charityTransferItemList" cascade="all,delete-orphan" lazy="false">
<key column="TSF_NO" not-null="true" />
<list-index column="TSF_SEQ_NO"/>
<one-to-many class="CharityTransferItem" />
</list>
From Hibernate doc on collections:
If the foreign key column of a association is declared NOT NULL, you must declare the mapping not-null="true" or use a bidirectional association with the collection mapping marked inverse="true". See the discussion of bidirectional associations later in this chapter for more information.
I also think there is a typo in your cascade style (all-delete-orphan
should be all,delete-orphan
).
(by Mark Glass、Michael Wiles、jjmontes)