views:

233

answers:

3

Hi guys. How to force NHibernate not to generate UPDATE for each element in collection if I change only one element of that collection. Here it is:

internal class Master
{
    private int _id;

    public string Name { get; set; }
    public ISet<Slave> Slaves { get; set; }

    public Master()
    {
        Slaves = new HashedSet<Slave>();
    }
}

internal class Slave
{
    private int _id;

    public string Name { get; set; }
    public Master Master { get; set; }
}

Mappings:

<class name="nHibernateTutorials.Slave, nHibernateTutorials" lazy="false">
    <id access="field" name="_id" column="id">
        <generator class="native"/>
    </id>
    <property access="property" name="Name" column="name"/>
    <many-to-one access="property" name="Master" column="master"/>
</class>

<class name="nHibernateTutorials.Master, nHibernateTutorials" lazy="false">
    <id access="field" name="_id" column="id">
        <generator class="native"/>
    </id>
    <property access="property" name="Name" column="name"/>
    <set access="property" name="Slaves" cascade="save-update">
        <key column="master"/>
        <one-to-many class="nHibernateTutorials.Slave, nHibernateTutorials"/>
    </set>
</class>

Code that updates collection element:

Master m = new Master {Name = "Kenny"};

Slave s1 = new Slave { Name = "Cartman", Master = m};
m.Slaves.Add(s1);
Slave s2 = new Slave {Name = "Kyle", Master = m};
m.Slaves.Add(s2);
Slave s3 = new Slave {Name = "Stan", Master = m};
m.Slaves.Add(s3);

DbManager.SaveObject(m);

s1.Name = "Daisy";
DbManager.SaveObject(m);

Code in DbManager.SaveObject simply opens new session and uses SaveOrUpdate to update object.

If I change one of the elements from the Slaves collection of master and then try to update master, NHibernate generates SQL for updating all elements in Slaves collection. But I need to update only one element.

Thank you.

A: 

Try using access="backfield" instead of access="property".

Tavo
nop, it doesn't work
spkenny
check this:http://nhforge.org/blogs/nhibernate/archive/2008/10/20/how-test-your-mappings-the-ghostbuster.aspxand this:http://jfromaniello.blogspot.com/2010/02/nhibernate-ghostbuster-version-11.html
Tavo
+1  A: 

You're probably seeing superfluous many-to-one updates. When you're mapping one-to-many collections, you must choose which side is the owner of the relationship. In your example, both sides of the relationship think they own it!

You need to add an attribute to your set element, with inverse="true". That directive tells NHibernate not to update the collection from that end of the relationship.

James L
In this case I should explicitly save changed collection element, but instead I want to save only collection that contains it and get single UPDATE statement for only changed element.
spkenny
Can you show the code where you load the entity, make the change and save it? It sounds like you're maybe reattaching a detached collection. In that case, NHibernate has no way to track changes and it updates all objects (by default).
James L
I've updated initial message
spkenny
A: 

You are resaving a transient collection, because Master.Slaves is still the original collection.

This would not happen in a new session.

If you want to update an entity in the same session after saving, you should session.Refresh() that entity. Your DBManager.SaveObject method could do a refresh after save.

DbManager.SaveObject<T>( T entity )
{
    session.Save( entity );
    session.Refresh( entity );
}
Lachlan Roche