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.  [ 2 posts ] 
Author Message
 Post subject: Problem with ordering of inserts
PostPosted: Wed Nov 28, 2007 8:45 am 
Newbie

Joined: Tue Apr 17, 2007 7:15 am
Posts: 1
I've put together a small test-case that replicates a problem one of our devs is having.

We have a Root object which has a collection of Nodes. Some of these may be members of a directed graph, so each Node has a collection of a its outgoing edges and its incoming edges: (apologies for the ascii art)

Code:
+--------+          +--------+ 1   from   * +--------+
|        | 1      * |        |--------------|        |
|  Root  |----------|  Node  | 1    to    * |  Edge  |
|        |          |        |--------------|        |
+--------+          +--------+              +--------+


Ideally, we would like to save the whole lot by just saving the Root instance, but this is failing. NH is trying to do this:

  1. insert Root
  2. insert Node1
  3. insert Edge1
  4. insert Node2
  5. update Edge1 with FK to Node2

Unfortunately, the Edge-Node FKs have not-null constraints, so the initial insert of Edge2 fails.

Obviously, there are various work-arounds, but none of them feel right:

  • get rid of not null constraints
  • flush the session before creating the Edge object
  • disable the cascades and save the instances explicitly
  • (on Oracle) make the not-null constraint deferred so it is only checked a the end of the transaction. Unfortunately, this project is using SQL Server, so this isn't an option.

Is there any way of getting NH to insert both Nodes before the Edge that links them?

Hibernate version: 1.2.0

Mapping documents:

Code:
<?xml version="1.0" encoding="utf-8"?>
<!--Generated from NHibernate.Mapping.Attributes on 2007-11-28 12:06:20Z.-->
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="WireTest.Root, WireTest" proxy="WireTest.IRoot, WireTest" table="Root">
    <id column="Id" type="Int32">
      <generator class="native" />
    </id>
    <version name="VersionNumber" />
    <property name="Name" length="20" not-null="true" unique="true" />
    <bag name="Nodes" cascade="save-update" inverse="true">
      <key column="RootId" />
      <one-to-many class="WireTest.Node, WireTest" />
    </bag>
  </class>
  <class name="WireTest.Node, WireTest" proxy="WireTest.INode, WireTest">
    <id column="Id" type="Int32">
      <generator class="native" />
    </id>
    <version name="VersionNumber" />
    <property name="Label" length="20" not-null="true" />
    <many-to-one name="Root" class="WireTest.Root, WireTest" column="RootId" not-null="true" />
    <bag name="FromEdges" cascade="save-update" inverse="true" optimistic-lock="false">
      <key column="FromNodeId" />
      <one-to-many class="WireTest.Edge, WireTest" />
    </bag>
    <bag name="ToEdges" cascade="save-update" inverse="true">
      <key column="ToNodeId" />
      <one-to-many class="WireTest.Edge, WireTest" />
    </bag>
  </class>
  <class name="WireTest.Edge, WireTest" proxy="WireTest.IEdge, WireTest">
    <id column="Id" type="Int32">
      <generator class="native" />
    </id>
    <version name="VersionNumber" />
    <property name="Label" length="20" not-null="true" />
    <many-to-one name="FromNode" class="WireTest.Node, WireTest" column="FromNodeId" not-null="true" />
    <many-to-one name="ToNode" class="WireTest.Node, WireTest" column="ToNodeId" not-null="true" />
  </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():

Code:
using (ISession session = _sessionFactory.OpenSession())
using(ITransaction transaction = session.BeginTransaction())
{
    Root r = new Root();
    r.Name = "a root";

    INode n1 = r.AddNode("Node1");
    INode n2 = r.AddNode("Node2");

    IEdge edge = r.AddLink(n1, n2, "Edge 1");

    try
    {
        session.Save(r);
        transaction.Commit();   
    }
    catch(HibernateException)
    {
        transaction.Rollback();
    }
}



Full stack trace of any exception that occurs:

Code:
   at NHibernate.Impl.SessionImpl.CheckNullability(Object[] values, IEntityPersister persister, Boolean isUpdate)
   at NHibernate.Impl.SessionImpl.DoSave(Object theObj, EntityKey key, IEntityPersister persister, Boolean replicate, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
   at NHibernate.Impl.SessionImpl.DoSave(Object obj, Object id, IEntityPersister persister, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
   at NHibernate.Impl.SessionImpl.SaveWithGeneratedIdentifier(Object obj, CascadingAction action, Object anything)
   at NHibernate.Impl.SessionImpl.Save(Object obj)
   at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
   at NHibernate.Engine.Cascades.CascadingAction.ActionSaveUpdateClass.Cascade(ISessionImplementor session, Object child, Object anything)
   at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, Object child, IType type, CascadingAction action, CascadeStyle style, CascadePoint cascadeTo, Object anything)
   at NHibernate.Engine.Cascades.CascadeCollection(CascadingAction action, CascadeStyle style, CollectionType collectionType, IType elemType, Object child, CascadePoint cascadeVia, ISessionImplementor
session, Object anything)
   at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, Object child, IType type, CascadingAction action, CascadeStyle style, CascadePoint cascadeTo, Object anything)
   at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, IEntityPersister persister, Object parent, CascadingAction action, CascadePoint cascadeTo, Object anything)
   at NHibernate.Impl.SessionImpl.DoSave(Object theObj, EntityKey key, IEntityPersister persister, Boolean replicate, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
   at NHibernate.Impl.SessionImpl.DoSave(Object obj, Object id, IEntityPersister persister, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
   at NHibernate.Impl.SessionImpl.SaveWithGeneratedIdentifier(Object obj, CascadingAction action, Object anything)
   at NHibernate.Impl.SessionImpl.Save(Object obj)
   at NHibernate.Impl.SessionImpl.SaveOrUpdate(Object obj)
   at NHibernate.Engine.Cascades.CascadingAction.ActionSaveUpdateClass.Cascade(ISessionImplementor session, Object child, Object anything)
   at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, Object child, IType type, CascadingAction action, CascadeStyle style, CascadePoint cascadeTo, Object anything)
   at NHibernate.Engine.Cascades.CascadeCollection(CascadingAction action, CascadeStyle style, CollectionType collectionType, IType elemType, Object child, CascadePoint cascadeVia, ISessionImplementor
session, Object anything)
   at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, Object child, IType type, CascadingAction action, CascadeStyle style, CascadePoint cascadeTo, Object anything)
   at NHibernate.Engine.Cascades.Cascade(ISessionImplementor session, IEntityPersister persister, Object parent, CascadingAction action, CascadePoint cascadeTo, Object anything)
   at NHibernate.Impl.SessionImpl.DoSave(Object theObj, EntityKey key, IEntityPersister persister, Boolean replicate, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
   at NHibernate.Impl.SessionImpl.DoSave(Object obj, Object id, IEntityPersister persister, Boolean useIdentityColumn, CascadingAction cascadeAction, Object anything)
   at NHibernate.Impl.SessionImpl.SaveWithGeneratedIdentifier(Object obj, CascadingAction action, Object anything)
   at NHibernate.Impl.SessionImpl.Save(Object obj)


Name and version of the database you are using:

SQL Server 2005


Top
 Profile  
 
 Post subject:
PostPosted: Wed Nov 28, 2007 11:58 am 
Expert
Expert

Joined: Fri May 13, 2005 11:13 am
Posts: 292
Location: Rochester, NY
This seems like a candidate for a JIRA issue. Non-null fk violation is a known issue for unidirectional one-to-many(http://jira.nhibernate.org/browse/NH-941 and http://jira.nhibernate.org/browse/NH-1050) and I have always used Parent-Child relationships to *avoid* this problem, and have recommended it to others many times. The fact that it still breaks in this not-unreasonable usage is disconcerting.

However, out of all the workarounds, I would go with allowing null FKs. It will allow you to keep the smoothest usage in your data access code. OTOH, if you are working with a legacy DB, you are pretty much forced into clumsy access.


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