-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 
Author Message
 Post subject: Why is the OnDelete annotation needed?
PostPosted: Mon Jul 11, 2005 11:50 am 
Newbie

Joined: Fri Jul 08, 2005 5:18 pm
Posts: 10
I have the following entities:

@Entity( access = AccessType.FIELD )
@Table( name = "TB_Keylist" )
class DBKeylist implements Keylist, Serializable
{
@Basic
@Column( name = "KL_UID" )
@Id( generate = GeneratorType.IDENTITY )
private int uid;

...

@OneToMany( targetEntity = DBUnitRecord.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "keylistUID" )
@MapKey
@OrderBy( clause="UR_UID ASC" )
@OnDelete( action = OnDeleteAction.CASCADE )
private Map< Long, UnitRecord > unitRecords;
}

@Entity( access = AccessType.FIELD )
@Table( name = "TB_UnitRecord" )
class DBUnitRecord implements UnitRecord, Serializable
{
@Basic
@Column( name = "UR_UID" )
@Id
private long unitID;

...

@Basic
@Column( name = "KL_UID", nullable = false )
private int keylistUID;
}


For the DBKeylist.unitRecords field, I am trying to get Hibernate to generate an "on delete cascade" to the foreign key constraint. I have to include the @OnDelete annotation to get it to work right. Shouldn't it work correctly when I included the cascade = CascadeType.ALL to the OneToMany annotation? Is this a feature that just hasn't been added to Hibernate yet? Or is the way that you intend it to always work?

(Note that I played the trick of making the keylistUID to be an int in DBUnitRecord to get the effect of unidirectional one-to-many association. I still have the same cascade delete problem if use a bi-directional association where DBUnitRecord.keylistUID would be a DBKeylist instead of an int.)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2005 12:45 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
This is on purpose, on delete deletions bypasses Hibernate's usual optimistic locking strategy for versioned data.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 11, 2005 11:40 pm 
Newbie

Joined: Fri Jul 08, 2005 5:18 pm
Posts: 10
What does the cascade attribute of the OneToMany annotation do in Hibernate then?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 12:39 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
?... it does not bypass optimistic locking, it basically does delete where version = ?

_________________
Emmanuel


Top
 Profile  
 
 Post subject: Counter to the specification
PostPosted: Tue Jul 12, 2005 1:25 pm 
Newbie

Joined: Tue Jul 12, 2005 1:17 pm
Posts: 7
This doesn't seem to comply with the EJB3 specification and I think you should fix it. In section 3.2.2 of the EJB Persistence public review draft you can find that remove operations should be cascaded if the cascade type includes either ALL or REMOVE. If you want some other behavior the user of Hibernate should have to set a non-conforming property within the persistence.xml or something like that.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 1:39 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
This most certainly complies perfectly with the spec and I think you guys need to go off and spend some time using your brains on this one instead of writing such silly stuff.

Hint: it is of course simply not possible to combine optimistic locking with ON DELETE CASCADE. Hence we provide this feature as an extension to ths spec. Of course, there is another way to perform cascade delete which does do the optimistic lock check, and this is the mechanism Hibernate uses to implement the spec behavior.


Top
 Profile  
 
 Post subject: Ok, I'll bite.
PostPosted: Tue Jul 12, 2005 1:57 pm 
Newbie

Joined: Tue Jul 12, 2005 1:17 pm
Posts: 7
How is specifying CascadeType.ALL (which includes CascadeType.REMOVE) not the same thing as specifying your Hibernate specific annotation?

I have entities. When the top level entity is removed I want all its children removed. Why do I have to say this twice?

The only reference in the specification at all to optimistic concurrency is with respect to Version columns which it doesn't appear this example uses.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 2:43 pm 
Newbie

Joined: Fri Jul 08, 2005 5:18 pm
Posts: 10
I am not trying to be silly. I'm just trying to understand.

Here is the quote from the spec, section 3.2.2 bullet 2:
"• If X is a managed entity, the remove operation causes it to become removed. The remove operation
is cascaded to entities referenced by X, if the relationships from X to these other entities
is annotated with the cascade=REMOVE or cascade=ALL annotation element value."


In my above example, when I don't have the OnDelete annotation, but I do have the cascade=CascadeType.ALL annotation, and I try a remove operation on a DBKeylist object. I get an exception from Sybase to the effect of "cannot delete keylist because a foreign key reference exists from a DBUnitRecord". The spec makes it sound like when I remove the DBKeylist object that all of the DBUnitRecord objects should go away with no complaining. In my example I am not using any versioning.

The Hibernate OnDelete annotation of course fixes the problem. I just prefer not to have any Hibernate specific references in my code so that could easily port to a new O/R mapping if I needed to in the future. But, if I have to, then fine.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 3:26 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Your interpretation of the spec is correct.

Your observation of Hibernate behavior is not correct. (I know, I just tested it.)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 12, 2005 3:45 pm 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 12:50 pm
Posts: 5130
Location: Melbourne, Australia
Actually, probably your interpretation of the spec is also wrong. "Cascade delete" in terms of Hibernate and EJB is a logical construct and is not the same thing as "ON DELETE CASCADE" foreign key constraints.


Top
 Profile  
 
 Post subject: I see my problem
PostPosted: Tue Jul 12, 2005 6:02 pm 
Newbie

Joined: Fri Jul 08, 2005 5:18 pm
Posts: 10
I figured out the issue. I was not doing a remove operation, I was doing a delete by query. I'm assuming that the cascade parameter does not extend to the delete by query operation.



Now the bad news...

I changed my example to do a remove instead of a delete by query. DBKeylist has about 10000 or so DBUnitRecords in its association. I got the following exception when trying to do a remove on the DBKeylist:

...
2005-07-12 14:54:49.027 [FINE...] (main) AbstractBatcher.log: delete from TB_UnitRecord where UR_UID=?
2005-07-12 14:54:49.027 [FINEST.] (main) NullableType.nullSafeSet: binding '298876794' to parameter: 1
2005-07-12 14:54:49.027 [FINE...] (main) AbstractBatcher.log: delete from TB_UnitRecord where UR_UID=?
2005-07-12 14:54:49.027 [FINEST.] (main) NullableType.nullSafeSet: binding '298876795' to parameter: 1
2005-07-12 14:54:49.027 [FINE...] (main) AbstractBatcher.log: delete from TB_UnitRecord where UR_UID=?
2005-07-12 14:54:49.027 [FINEST.] (main) NullableType.nullSafeSet: binding '298876796' to parameter: 1
2005-07-12 14:54:49.027 [FINE...] (main) AbstractBatcher.log: delete from TB_UnitRecord where UR_UID=?
2005-07-12 14:54:49.043 [FINEST.] (main) NullableType.nullSafeSet: binding '298876797' to parameter: 1
2005-07-12 14:54:49.043 [FINE...] (main) AbstractBatcher.log: delete from TB_UnitRecord where UR_UID=?
2005-07-12 14:54:49.043 [FINEST.] (main) NullableType.nullSafeSet: binding '298876798' to parameter: 1
2005-07-12 14:54:49.043 [FINE...] (main) AbstractBatcher.log: delete from TB_UnitRecord where UR_UID=?
2005-07-12 14:54:49.043 [FINEST.] (main) NullableType.nullSafeSet: binding '298876799' to parameter: 1
2005-07-12 14:54:49.043 [FINE...] (main) AbstractBatcher.log: delete from TB_Keylist where KL_UID=?
2005-07-12 14:54:49.043 [FINEST.] (main) NullableType.nullSafeSet: binding '1' to parameter: 1
2005-07-12 14:54:49.043 ------------------------------------------------
[SEVERE.] (main) JDBCExceptionReporter.logExceptions: ASA Error -198: Primary key for row in table 'TB_Keylist' is referenced by foreign key 'FK76CCF5865F8146F7' in table 'TB_UnitRecord'
2005-07-12 14:54:49.074 ------------------------------------------------
[SEVERE.] (main) AbstractFlushingEventListener.performExecutions: Could not synchronize database state with sessionorg.hibernate.exception.ConstraintViolationException: could not delete: [com.ccad.common.keylist.db.DBKeylist#1]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:63)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.persister.entity.BasicEntityPersister.delete(BasicEntityPersister.java:2091)
at org.hibernate.persister.entity.BasicEntityPersister.delete(BasicEntityPersister.java:2218)
at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:66)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:239)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:223)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:141)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:284)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:736)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:330)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:36)
at com.ccad.common.keylist.test.DBKeylistTest.testDBKeylist(DBKeylistTest.java:206)
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 junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: com.sybase.jdbc2.jdbc.SybSQLException: ASA Error -198: Primary key for row in table 'TB_Keylist' is referenced by foreign key 'FK76CCF5865F8146F7' in table 'TB_UnitRecord'
at com.sybase.jdbc2.tds.Tds.processEed(Tds.java:2538)
at com.sybase.jdbc2.tds.Tds.nextResult(Tds.java:1922)
at com.sybase.jdbc2.jdbc.ResultGetter.nextResult(ResultGetter.java:69)
at com.sybase.jdbc2.jdbc.SybStatement.nextResult(SybStatement.java:201)
at com.sybase.jdbc2.jdbc.SybStatement.nextResult(SybStatement.java:182)
at com.sybase.jdbc2.jdbc.SybStatement.updateLoop(SybStatement.java:1611)
at com.sybase.jdbc2.jdbc.SybStatement.executeUpdate(SybStatement.java:1594)
at com.sybase.jdbc2.jdbc.SybPreparedStatement.executeUpdate(SybPreparedStatement.java:89)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:22)
at org.hibernate.persister.entity.BasicEntityPersister.delete(BasicEntityPersister.java:2074)
... 27 more
org.hibernate.exception.ConstraintViolationException: could not delete: [com.ccad.common.keylist.db.DBKeylist#1]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:63)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.persister.entity.BasicEntityPersister.delete(BasicEntityPersister.java:2091)
at org.hibernate.persister.entity.BasicEntityPersister.delete(BasicEntityPersister.java:2218)
at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:66)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:239)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:223)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:141)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:284)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:736)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:330)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
at org.hibernate.ejb.TransactionImpl.commit(TransactionImpl.java:36)
at com.ccad.common.keylist.test.DBKeylistTest.testDBKeylist(DBKeylistTest.java:206)
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 junit.framework.TestCase.runTest(TestCase.java:154)
at junit.framework.TestCase.runBare(TestCase.java:127)
at junit.framework.TestResult$1.protect(TestResult.java:106)
at junit.framework.TestResult.runProtected(TestResult.java:124)
at junit.framework.TestResult.run(TestResult.java:109)
at junit.framework.TestCase.run(TestCase.java:118)
at junit.framework.TestSuite.runTest(TestSuite.java:208)
at junit.framework.TestSuite.run(TestSuite.java:203)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:478)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:344)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: com.sybase.jdbc2.jdbc.SybSQLException: ASA Error -198: Primary key for row in table 'TB_Keylist' is referenced by foreign key 'FK76CCF5865F8146F7' in table 'TB_UnitRecord'
at com.sybase.jdbc2.tds.Tds.processEed(Tds.java:2538)
at com.sybase.jdbc2.tds.Tds.nextResult(Tds.java:1922)
at com.sybase.jdbc2.jdbc.ResultGetter.nextResult(ResultGetter.java:69)
at com.sybase.jdbc2.jdbc.SybStatement.nextResult(SybStatement.java:201)
at com.sybase.jdbc2.jdbc.SybStatement.nextResult(SybStatement.java:182)
at com.sybase.jdbc2.jdbc.SybStatement.updateLoop(SybStatement.java:1611)
at com.sybase.jdbc2.jdbc.SybStatement.executeUpdate(SybStatement.java:1594)
at com.sybase.jdbc2.jdbc.SybPreparedStatement.executeUpdate(SybPreparedStatement.java:89)
at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:22)
at org.hibernate.persister.entity.BasicEntityPersister.delete(BasicEntityPersister.java:2074)
... 27 more


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 11 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.