-->
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.  [ 3 posts ] 
Author Message
 Post subject: delete-orphan and ConstraintViolationException
PostPosted: Sun Jul 08, 2007 3:40 pm 
Regular
Regular

Joined: Fri Dec 17, 2004 10:38 am
Posts: 54
Hibernate version:
3.2.4.sp1

Mapping documents:
Code:
<class name="Component" table="COMPONENTS">
   <id name="id" column="ID" type="long" unsaved-value="0">
      <generator class="native" />
   </id>

   <property name="businessKey" column="BUSINESS_KEY" type="long" not-null="true" unique="true" />
   <many-to-one name="container" column="CONTAINER_ID" class="Container" not-null="true" />
</class>

<!-- element holder -->
<class name="Container" table="CONTAINERS">
   <id name="id" column="ID" type="long" unsaved-value="0">
      <generator class="native" />
   </id>

   <set name="components" cascade="all-delete-orphan" inverse="true">
      <cache usage="read-write" />
      <key column="CONTAINER_ID" on-delete="cascade" not-null="true" />
      <one-to-many class="Component" />
   </set>
</class>

Code between sessionFactory.openSession() and session.close():
Code:
      Transaction tx = session.beginTransaction();

      Container cont = new Container();
      cont.setComponents(new HashSet<Component>());
      
      Component c1 = new Component();
      c1.setBusinessKey(1);
      c1.setContainer(cont);

      cont.getComponents().add(c1);
      
      session.save(cont);
      
      tx.commit();
      
      tx = session.beginTransaction();

      cont.getComponents().clear();
      
      Component c2 = new Component();
      c2.setBusinessKey(1);
      c2.setContainer(cont);

      cont.getComponents().add(c2);
      
      tx.commit();


Full stack trace of any exception that occurs:
Code:
org.hibernate.exception.ConstraintViolationException: could not insert: [org.foo.Component]
   at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71)
   at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
   at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:40)
   at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2158)
   at org.hibernate.persister.entity.AbstractEntityPersister.insert(AbstractEntityPersister.java:2638)
   at org.hibernate.action.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:48)
   at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
   at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:298)
   at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:181)
   at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:107)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:187)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:172)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:94)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
   at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
   at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
   at org.hibernate.engine.CascadingAction$1.cascade(CascadingAction.java:218)
   at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
   at org.hibernate.engine.Cascade.cascadeCollectionElements(Cascade.java:296)
   at org.hibernate.engine.Cascade.cascadeCollection(Cascade.java:242)
   at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:219)
   at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
   at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
   at org.hibernate.event.def.AbstractFlushingEventListener.cascadeOnFlush(AbstractFlushingEventListener.java:131)
   at org.hibernate.event.def.AbstractFlushingEventListener.prepareEntityFlushes(AbstractFlushingEventListener.java:122)
   at org.hibernate.event.def.AbstractFlushingEventListener.flushEverythingToExecutions(AbstractFlushingEventListener.java:65)
   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.foo.test.CollectionTest.test1(CollectionTest.java:59)
   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)
   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.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: org.h2.jdbc.JdbcSQLException: Unique index or primary key violation: CONSTRAINT_INDEX_0  ON PUBLIC.COMPONENTS(BUSINESS_KEY) [23001-46]
   at org.h2.message.Message.getSQLException(Message.java:65)
   at org.h2.message.Message.getSQLException(Message.java:47)
   at org.h2.index.Index.getDuplicateKeyException(Index.java:61)
   at org.h2.index.TreeIndex.add(TreeIndex.java:53)
   at org.h2.table.TableData.addRow(TableData.java:77)
   at org.h2.command.dml.Insert.update(Insert.java:88)
   at org.h2.command.CommandContainer.update(CommandContainer.java:64)
   at org.h2.command.Command.executeUpdate(Command.java:120)
   at org.h2.jdbc.JdbcPreparedStatement.executeUpdateInternal(JdbcPreparedStatement.java:127)
   at org.h2.jdbc.JdbcPreparedStatement.executeUpdate(JdbcPreparedStatement.java:116)
   at org.hibernate.id.IdentityGenerator$GetGeneratedKeysDelegate.executeAndExtract(IdentityGenerator.java:73)
   at org.hibernate.id.insert.AbstractReturningDelegate.performInsert(AbstractReturningDelegate.java:33)
   ... 48 more

The generated SQL (show_sql=true):
Code:
Hibernate: insert into CONTAINERS (ID) values (null)
Hibernate: insert into COMPONENTS (ID, BUSINESS_KEY, CONTAINER_ID) values (null, ?, ?)
Hibernate: insert into COMPONENTS (ID, BUSINESS_KEY, CONTAINER_ID) values (null, ?, ?)
23:20:12,578 WARN  [org.hibernate.util.JDBCExceptionReporter.logExceptions:77] SQL Error: 23001, SQLState: 23001
23:20:12,578 ERROR [org.hibernate.util.JDBCExceptionReporter.logExceptions:78] Unique index or primary key violation: CONSTRAINT_INDEX_0  ON PUBLIC.COMPONENTS(BUSINESS_KEY) [23001-46]


This problem I see for several years since v. 2.1.
There are several workarounds to overcome this insert-before-delete problem including
- removing unique constrant(not very good idea from consistency point of view),
- flusing session between deleting elements and adding new one (alsa clumsy thing since collection manipulation occurs in service layer, not persistence one, have to add special dao method to handle collection mutation),
- making collection of components instead of entity (not work in cases of component entity inheritance mapping)
...
not great solutions but help to solve problem, all impose some restrictions and break unintrusive persistence idea of hibernate.

Why do hibernate have to insert delete-orphan cascaded association before removing orphaned children anyway?


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 09, 2007 5:32 pm 
Expert
Expert

Joined: Tue Dec 28, 2004 7:02 am
Posts: 573
Location: Toulouse, France
I'm surprised by the (null) that seems to be inserted. I don't have any hibernate project settled at the moment, but couldn't it be a problem?

In your case, the (null) column seems to be the parent id. So as Hibernate doesn't have the parent id, it can't insert. What is the underlying DB and the strategy corresponding to the native one with your DBMS ?

Well, I've got to check 'cause this is an interesting problem, but I can't remember having to watch out flushing the parent insertion before adding children (though it's maybe not a common usecase for us).

_________________
Baptiste
PS : please don't forget to give credits below if you found this answer useful :)


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 10, 2007 4:32 pm 
Regular
Regular

Joined: Fri Dec 17, 2004 10:38 am
Posts: 54
batmat wrote:
In your case, the (null) column seems to be the parent id. So as Hibernate doesn't have the parent id, it can't insert. What is the underlying DB and the strategy corresponding to the native one with your DBMS ?

No it is not, after insertion of parent (1st line of show_sql) id is generated and obtained by Hibernate.
2nd line insert c1 object using this id just fine
3rd line shows try to insert c2 object which fails due to unique index on my business key CONSTRAINT_INDEX_0 ON PUBLIC.COMPONENTS(BUSINESS_KEY)

Test uses H2 database, id generator strategy is identity, i encounter this particular problem with other dbs - oracle, mysql, mssql. It is not a generator problem.
If before 3rd statement hibernate issued
Code:
delete from COMPONENTS where id=? --ID of c1

all updates completes just fine.
(removing uniq business key constraint shows this delete issued AFTER insert, but having constraint hibernate unable to reach it)


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 3 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.