-->
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.  [ 4 posts ] 
Author Message
 Post subject: Issue with parent child unidirectional one-to-many mapping
PostPosted: Thu Jan 22, 2009 3:46 pm 
Newbie

Joined: Thu Jan 22, 2009 12:26 pm
Posts: 3
Hibernate version: v2.0.1GA

Hello all. I am in the process of converting a simple web service over
to use nHibernate and so far I have been having major problems.

The first issue is trying to map (what I would think is) a simple
parent/child relationship.

The tables (MS SQL 2005, btw) are:

Code:
Entity
------
ID (PK, autoincrement)

EntityAlias
--------
ID (PK, autoincrement)
EntityID (FK pointing to Entity ID)


The class Entity has a collection of Aliases. So this seems like a
straight forward one-to-many, unidirectional relationship to me, so I
tried first with a mapping like the following:

Code:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="MyNS" assembly="MyAssembly">
  <class name="Entity" dynamic-update="false" dynamic-insert="false">
    <id name="ID" type="Int32">
      <generator class="native"/>
    </id>
    <bag name="Aliases" table="EntityAlias" cascade="all-delete-orphan" lazy="false">
      <key column="EntityID"/>
      <one-to-many class="EntityAlias"/>
    </bag>
  </class>
</hibernate-mapping>

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="MyNS" assembly="MyAssembly">
  <class name="EntityAlias" dynamic-update="false" dynamic-insert="false">
    <id name="ID" type="Int32">
      <generator class="native"/>
    </id>
  </class>
</hibernate-mapping>


Unfortunately, this requires (1) the FK to be nullable, and (2) I can't see a difference between cascade set to "all" or "all-delete-orphan" in Entity. I've tried both and it seems to do exactly the same thing: just set the EntityID to null in the EntityAlias table when an alias is deleted from the collection in Entity (otherwise everything seems to work well). I looked in the log file and I see for EntityAlias an entry that says (from memory):

Update: update EntityAlias set EntityID = ? where ID = ?
Delete: update EntityAlias set EntityID = null where ID = ?
On shot delete: update EntityAlias set EntityID = null where EntityID = ?

Shouldn't there be something with a delete statement?

Next I tried adding inverse=true to the bag mapping, and a many-to-one tag in EntityAlias as I saw suggested from various sources. The problem with this is it makes the relationship be bidirectional. That means I must also have an Entity reference in the child object. This wouldn't be a problem (though it is more work) except in my use case: I return the Entity object to a client, they delete an EntityAlias from that Entity's collection and then pass the Entity back using the UpdateEntity web method. This method tries to do a simple session.Update(Entity) but this doesn't cause the deleted EntityAlias to be deleted from the database because (since it's bidirectional) I would actually have to set the Entity reference in the EntityAlias to null to cause the deletion. Of course I can't because I don't have it anymore. I would have to pull the old object and do a by-hand comparison of the new object and the old one, but I expected nHibernate Update method to do this already.

I feel like I must be missing something fundamental because such a common, obvious pattern can't be so difficult to do.

I appreciate any help someone can give me.
Thanks,
Jason


Last edited by jbjohns on Fri Jan 23, 2009 4:14 am, edited 2 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 23, 2009 4:08 am 
Newbie

Joined: Thu Jan 22, 2009 12:26 pm
Posts: 3
Now that I'm back at my desk, here is more of the requested info:

Hibernate version: v2.0.1GA

Mapping documents: (provided above)

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

Code:
using(ISession session = OpenSession())
using (ITransaction tx = session.BeginTransaction())
{
     session.Update(entity);
     session.Flush();  // thought this might delete the orphans
     tx.Commit();
}


Full stack trace of any exception that occurs:

No exception, expected behavior just doesn't happen.

Name and version of the database you are using:

MS SQL 2005

The generated SQL (show_sql=true):

ICollectionPersister Static SQL for collection: MyAssembly.Entity.Aliases
ICollectionPersister Row insert: UPDATE EntityAlias SET EntityID = ? WHERE ID = ?
ICollectionPersister Row delete: UPDATE EntityAlias SET EntityID = null WHERE EntityID = ? AND ID = ?
ICollectionPersister One-shot delete: UPDATE EntityAlias SET EntityID = null WHERE EntityID = ?
AbstractEntityPersister Version select: SELECT ID FROM Entity WHERE ID = ?
AbstractEntityPersister Snapshot select: SELECT entity_.ID, entity_.Name as Name1_ FROM Entity entity_ WHERE entity_.ID=?

Debug level Hibernate log excerpt:

] NHibernate.SQL UPDATE Entity SET Name = @p1 WHERE ID = @p2; @p1 = 'Test Entity', @p2 = '992'
2009-01-22 17:08:09,977 DEBUG [8 ] Transaction.AdoTransaction Enlist Command
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher Closed IDbCommand, open IDbCommands: 0
2009-01-22 17:08:09,977 DEBUG [8 ] Collection.ICollectionPersister Deleting collection: [MyAssembly.Entity.Aliases#992]
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher Opened new IDbCommand, open IDbCommands: 1
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher Building an IDbCommand object for the SqlString: UPDATE EntityAlias SET EntityID = null WHERE EntityID = ?
2009-01-22 17:08:09,977 DEBUG [8 ] Type.Int32Type binding '992' to parameter: 0
2009-01-22 17:08:09,977 DEBUG [8 ] NHibernate.SQL UPDATE EntityAlias SET EntityID = null WHERE EntityID = @p0; @p0 = '992'
2009-01-22 17:08:09,977 DEBUG [8 ] Transaction.AdoTransaction Enlist Command
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher Closed IDbCommand, open IDbCommands: 0
2009-01-22 17:08:09,977 DEBUG [8 ] Collection.ICollectionPersister done deleting collection
2009-01-22 17:08:09,977 DEBUG [8 ] Collection.ICollectionPersister Inserting collection: [MyAssembly.Entity.Aliases#992]
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher Opened new IDbCommand, open IDbCommands: 1
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher Building an IDbCommand object for the SqlString: UPDATE EntityAlias SET EntityID = ? WHERE ID = ?
2009-01-22 17:08:09,977 DEBUG [8 ] Type.Int32Type binding '992' to parameter: 0
2009-01-22 17:08:09,977 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:09,977 DEBUG [8 ] Type.Int32Type binding '41' to parameter: 1
2009-01-22 17:08:09,977 DEBUG [8 ] NHibernate.SQL UPDATE EntityAlias SET EntityID = @p0 WHERE ID = @p1; @p0 = '992', @p1 = '41'
2009-01-22 17:08:09,977 DEBUG [8 ] Transaction.AdoTransaction Enlist Command
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher reusing command UPDATE EntityAlias SET EntityID = @p0 WHERE ID = @p1
2009-01-22 17:08:09,977 DEBUG [8 ] Type.Int32Type binding '992' to parameter: 0
2009-01-22 17:08:09,977 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:09,977 DEBUG [8 ] Type.Int32Type binding '47' to parameter: 1
2009-01-22 17:08:09,977 DEBUG [8 ] NHibernate.SQL UPDATE EntityAlias SET EntityID = @p0 WHERE ID = @p1; @p0 = '992', @p1 = '47'
2009-01-22 17:08:09,977 DEBUG [8 ] Transaction.AdoTransaction Enlist Command
2009-01-22 17:08:09,977 DEBUG [8 ] Collection.ICollectionPersister done inserting collection: 2 rows inserted
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.AbstractBatcher Closed IDbCommand, open IDbCommands: 0
2009-01-22 17:08:09,977 DEBUG [8 ] AdoNet.ConnectionManager registering flush end
2009-01-22 17:08:09,992 DEBUG [8 ] Default.AbstractFlushingEventListener post flush
2009-01-22 17:08:09,992 DEBUG [8 ] Transaction.AdoTransaction Start Commit
2009-01-22 17:08:09,992 DEBUG [8 ] Default.AbstractFlushingEventListener flushing session
2009-01-22 17:08:09,992 DEBUG [8 ] Default.AbstractFlushingEventListener processing flush-time cascades
2009-01-22 17:08:09,992 INFO [8 ] Engine.Cascade processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: MyAssembly.Entity
2009-01-22 17:08:09,992 INFO [8 ] Engine.Cascade deleting orphans for collection: MyAssembly.Entity.Aliases
2009-01-22 17:08:10,008 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:10,008 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:10,008 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:10,008 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:10,008 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:10,008 DEBUG [8 ] Engine.IdentifierValue unsaved-value: 0
2009-01-22 17:08:10,008 INFO [8 ] Engine.Cascade done deleting orphans for collection: MyAssembly.Entity.Aliases
2009-01-22 17:08:10,008 INFO [8 ] Engine.Cascade done processing cascade NHibernate.Engine.CascadingAction+SaveUpdateCascadingAction for: MyAssembly.Entity
2009-01-22 17:08:10,008 DEBUG [8 ] Default.AbstractFlushingEventListener dirty checking collections
2009-01-22 17:08:10,008 DEBUG [8 ] Default.AbstractFlushingEventListener Flushing entities and processing referenced collections
2009-01-22 17:08:10,024 DEBUG [8 ] Engine.Collections Collection found: [MyAssembly.Entity.Aliases#992], was: [MyAssembly.Entity.Aliases#992] (initialized)
2009-01-22 17:08:10,024 DEBUG [8 ] Default.AbstractFlushingEventListener Processing unreferenced collections
2009-01-22 17:08:10,024 DEBUG [8 ] Default.AbstractFlushingEventListener Scheduling collection removes/(re)creates/updates
2009-01-22 17:08:10,024 DEBUG [8 ] Default.AbstractFlushingEventListener Flushed: 0 insertions, 0 updates, 0 deletions to 2 objects
2009-01-22 17:08:10,024 DEBUG [8 ] Default.AbstractFlushingEventListener Flushed: 0 (re)creations, 0 updates, 0 removals to 1 collections
2009-01-22 17:08:10,024 DEBUG [8 ] Impl.Printer listing entities:
2009-01-22 17:08:10,024 DEBUG [8 ] Impl.Printer MyAssembly.Entity{Names=[MyAssembly.EntityAlias#41, MyAssembly.EntityAlias#47], Name=Test Entity, ID=992}
2009-01-22 17:08:10,024 DEBUG [8 ] Default.AbstractFlushingEventListener executing flush
2009-01-22 17:08:10,024 DEBUG [8 ] AdoNet.ConnectionManager registering flush begin
2009-01-22 17:08:10,024 DEBUG [8 ] AdoNet.ConnectionManager registering flush end
2009-01-22 17:08:10,024 DEBUG [8 ] Default.AbstractFlushingEventListener post flush
2009-01-22 17:08:10,024 DEBUG [8 ] Impl.SessionImpl before transaction completion
2009-01-22 17:08:10,055 DEBUG [8 ] Transaction.AdoTransaction IDbTransaction Committed
2009-01-22 17:08:10,055 DEBUG [8 ] Impl.SessionImpl transaction completion
2009-01-22 17:08:10,055 DEBUG [8 ] AdoNet.ConnectionManager aggressively releasing database connection
2009-01-22 17:08:10,055 DEBUG [8 ] Connection.ConnectionProvider Closing connection
2009-01-22 17:08:10,055 DEBUG [8 ] Transaction.AdoTransaction IDbTransaction disposed.
2009-01-22 17:08:10,055 DEBUG [8 ] Impl.SessionImpl running ISession.Dispose()
2009-01-22 17:08:10,055 DEBUG [8 ] Impl.SessionImpl closing session
2009-01-22 17:08:10,055 DEBUG [8 ] AdoNet.AbstractBatcher running BatcherImpl.Dispose(true)
[Session Ended]


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 23, 2009 11:53 am 
Newbie

Joined: Thu Jan 22, 2009 12:26 pm
Posts: 3
Update: I found out about the Merge method (this doesn't seem to be in the nHibernate documentation, but it does work) and tried it and it seems to do the right thing.

I'm a little surprised by this, but at least I seem to have a solution.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jan 26, 2009 8:08 am 
Beginner
Beginner

Joined: Fri Sep 26, 2008 9:19 am
Posts: 20
Location: Belgium
what is the merge method doing then? i see it standing there, but i didn't see any docs on it.


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