[NHibernate] item verwijderd uit IDictionary->geen delete

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Nu online
Ik heb een probleempje; om met de deur in huis te vallen.
Ik heb 2 classes die er ongeveer zo uit zien:
code:
1
2
3
4
5
6
public class Class1
{
    private int _id;

    private IDictionary<string, Class2> _class2Collection;
}

code:
1
2
3
4
5
6
public class Class2
{
    private int _id;
    private Class1 _owner;
    private IDictionary<string, string> _dictionary;
}

Zoals je dus kan zien, bevat class1 een dictionary die instances van class2 bevat. Class2 bevat op zijn beurt opnieuw een dictionary.

Nu is het zo dat, als ik een item uit de dictionary van class1 verwijderd:
code:
1
_class2Collection.Remove (someKey);

Dat dit niet resulteert in een delete uit de DB, en dat vind ik een beetje vreemd.
Als ik nl. iets verwijder uit de dictionary uit class2:
code:
1
_dictionary.Remove (someKey);

Dan genereert NHibernate hier wel een delete statement voor. Het verschil zit 'm er natuurlijk in dat die eerste dictionary (uit class1) dus 'complexe' items bevat (instances van class2), en die andere dictionary bevat gewoon strings.

M'n mapping files zien er ongeveer als volgt uit (ik doe gewoon ff de relevante dingen)
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Domain.Class1, Domain" table="Table1" lazy="false">

    <id name="Id" access="field.camelcase-underscore" column="ProjectId" unsaved-value="-1">
      <generator class="identity" />
    </id>
    
    <map name="_class2Collection" access="field" table="Table2" lazy="false" cascade="all" inverse="true" >
      <key column="Id" />
      <index column="Language" type="string" />      
      <one-to-many class="Domain.Class2, Domain" />
    </map>
    
  </class>
</hibernate-mapping>

en voor class2
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class name="Domain.Class2, Domain" table="Table2" lazy="false">
    
    <id name="_id" access="field" column="Id" unsaved-value="-1">
      <generator class="identity" />
    </id>

    <many-to-one name="_owner" column="ownerid" access="field" class="Domain.Class1, Domain" cascade="all" />

    <map name="_dictionary" access="field" lazy="false" table="Table3" cascade="all" >
      <key column="Class2Id"   />
      <index column="key" type="string"  />
      <element column="Value" type="string" not-null="false"  />
    </map>

  </class>
</hibernate-mapping>

Wat ik ook vreemd vind is, dat als ik 'inverse=true' weghaal in de mapping van Class1 (daar waar ik die idictionary mapping specifieer), dat NHibernate dan wel een UPDATE statement genereert, en m'n 'key' en 'index' column op NULL wil zetten, ipv de hele reutemeut te verwijderen.

WTF doe ik hier verkeerd ? :?

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • DutchCommando
  • Registratie: November 2000
  • Laatst online: 09:21
Bron: http://www.hibernate.org/.../en/html/collections.html

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<class name="NHibernate.Auction.Category, NHibernate.Auction">
    <id name="Id" column="ID"/>
    ...
    <bag name="Items" table="CATEGORY_ITEM" lazy="true">
        <key column="CATEGORY_ID"/>
        <many-to-many class="NHibernate.Auction.Item, NHibernate.Auction" column="ITEM_ID"/>
    </bag>
</class>

<class name="NHibernate.Auction.Item, NHibernate.Auction">
    <id name="id" column="ID"/>
    ...

    <!-- inverse end -->
    <bag name="categories" table="CATEGORY_ITEM" inverse="true" lazy="true">
        <key column="ITEM_ID"/>
        <many-to-many class="NHibernate.Auction.Category, NHibernate.Auction" column="CATEGORY_ID"/>
    </bag>
</class>


"Changes made only to the inverse end of the association are not persisted. This means that NHibernate has two representations in memory for every bidirectional association, one link from A to B and another link from B to A. This is easier to understand if you think about the .NET object model and how we create a many-to-many relationship in C#:"

C#:
1
2
3
4
5
category.Items.Add(item);          // The category now "knows" about the relationship
item.Categories.Add(category);     // The item now "knows" about the relationship

session.Update(item);                     // No effect, nothing will be saved!
session.Update(category);                 // The relationship will be saved


"The non-inverse side is used to save the in-memory representation to the database. We would get an unneccessary INSERT/UPDATE and probably even a foreign key violation if both would trigger changes! The same is of course also true for bidirectional one-to-many associations."
Oftewel, roep de Update/Delete methode aan met het non-inverse object.

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Nu online
Als ik 'cascade=all' heb staan, op m'n collectie, worden die changes dan ook niet gepersisted ?

Als ik cascade=all-delete-orphan zet, dan wordt trouwens alles verwijderd ( m'n volledige class1 en alle gerelateerde records dus)

Zowiezo kan ik niet over m'n 'non-inverse' objecten loopen, want die zijn verwijderd. :P

[ Voor 59% gewijzigd door whoami op 12-07-2007 23:08 ]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Nu online
Hmm, gewoon 'cascade' op 'all-delete-orphan' zetten is dan toch de oplossing.
Ik gebruikte SaveOrUpdate, en dan werd m'n 'main' object ook uit de DB verwijderd. Als ik gewoon 'Save' gebruik, dan werkt het wel zoals het moet.
will not remove c from the database; it will only remove the link to p (and cause a NOT NULL constraint violation, in this case). You need to explicitly Delete() the Child.
code:
1
2
3
4
5
6
7
8
9
Parent p = (Parent) session.Load(typeof(Parent), pid);
// Get one child out of the set
IEnumerator childEnumerator = p.Children.GetEnumerator();
childEnumerator.MoveNext();
Child c = (Child) childEnumerator.Current;

p.Children.Remove(c);
session.Delete(c);
session.Flush();


Now, in our case, a Child can't really exist without its parent. So if we remove a Child from the collection, we really do want it to be deleted. For this, we must use cascade="all-delete-orphan".
code:
1
2
3
4
<set name="Children" inverse="true" cascade="all-delete-orphan">
    <key column="parent_id"/>
    <one-to-many class="Child"/>
</set>

Note: even though the collection mapping specifies inverse="true", cascades are still processed by iterating the collection elements. So if you require that an object be saved, deleted or updated by cascade, you must add it to the collection. It is not enough to simply set its parent.

https://fgheysels.github.io/