[NHibernate] parent+childs saven via cascade lukt niet

Pagina: 1
Acties:
  • 528 views sinds 30-01-2008
  • Reageer

Onderwerpen


Acties:
  • 0 Henk 'm!

  • pjonk
  • Registratie: November 2000
  • Laatst online: 10-09 15:33
Ben wat aan het experimenteren met NHibernate om te kijken hoe ik een order inclusief orderregels kan saven in de database. Ik heb hiervoor in MS-SQL een ultiem simpel database-schema gemaakt:
Afbeeldingslocatie: http://home.quicknet.nl/qn/prive/jonkie.xl/gotimg/orders.png
Tabel Orders
- ID is PK en een identity veld
- Reference is een nvarchar(50) met de order beschrijving

Tabel OrderLines
- ID is PK en een identity veld
- OrderID is de FK die verwijst naar Orders.ID (Orders->OrderLines is een 1:N relatie)
- Linenumber bepaalt de volgorde van de orderregels
- Description is een nvarchar(50) met de beschrijving van de regel

Vervolgens heb ik de Order en OrderLine entity classes gemaakt met onderstaande hibernate mappings:

Order.hbm.xml
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
  <class name="NHibernateTest.Order, NHibernateTest" table="Orders">
    <id name="Id" column="ID" type="Int32" unsaved-value="0">
      <generator class="identity" />
    </id>
    <property name="Reference" column="Reference" type="String(50)" />
    <list name="Orderlines" inverse="true" cascade="all">
      <key column="OrderID" />
      <index column="Linenumber"/>
      <one-to-many class="NHibernateTest.Orderline, NHibernateTest" />
    </list>
  </class>
</hibernate-mapping>


Orderline.hbm.xml
XML:
1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.0">
  <class name="NHibernateTest.Orderline, NHibernateTest" table="Orderlines">
    <id name="Id" column="ID" type="Int32" unsaved-value="0">
      <generator class="identity" />
    </id>
    <property name="Linenumber" column="Linenumber" type="Int32" />
    <property name="Description" column="Description" type="String(50)" />
    <many-to-one name="Order" class="NHibernateTest.Order, NHibernateTest" 
         cascade="all" column="OrderID" not-null="true" />
  </class>
</hibernate-mapping>

Ik wil nu een order met 2 orderregels in de database toevoegen. Dit doe ik als volgt:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Maak order en 2 orderregels
Order o = new Order();
o.Reference = DateTime.Now.ToString();
Orderline line1 = new Orderline();
line1.Order = o;
line1.Linenumber = 0;
line1.Description = "Regel 1";
o.Orderlines.Add(line1);
Orderline line2 = new Orderline();
line1.Order = o;
line1.Linenumber = 1;
line1.Description = "Regel 1";
o.Orderlines.Add(line2);

// Save de order inclusief regels
ISession session = _factory.OpenSession())
session.Save(o);
session.Flush();
session.Close();

Ik zie nu dat een order wordt aangemaakt in de tabel Orders, maar de tabel OrderLines wordt niet gevuld. De cascade="all" in de mapping files zou hier echter wel voor moeten zorgen. In de log zie ik het volgende terug:
code:
1
2
3
4
5
6
7
8
9
10
11
12
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.SQL [(null)] - INSERT INTO Orders (Reference) VALUES (@p0); select SCOPE_IDENTITY()
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.SQL [(null)] - @p0 = '11-11-2006 22:26:01'
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Connection.DriverConnectionProvider [(null)] - Obtaining IDbConnection from Driver
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Opened Reader, open Readers :1
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Persister.AbstractEntityPersister [(null)] - Natively generated identity: 19
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Driver.NHybridDataReader [(null)] - running NHybridDataReader.Dispose()
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Closed Reader, open Readers :0
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Impl.BatcherImpl [(null)] - Closed IDbCommand, open IDbCommands :0
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Engine.Cascades [(null)] - processing cascades for: NHibernateTest.Order
2006-11-11 22:26:04,823 [3544] DEBUG NHibernate.Engine.Cascades [(null)] - cascading to collection: NHibernateTest.Order.Orderlines
2006-11-11 22:26:04,833 [3544] DEBUG NHibernate.Engine.Cascades [(null)] - done processing cascades for: NHibernateTest.Order
2006-11-11 22:26:04,833 [3544] DEBUG NHibernate.Impl.SessionImpl [(null)] - flushing session

In de laatste regels zie ik inderdaad Ik zie hier inderdaad "processing cascades for: NHibernateTest.Order", maar dan meteen een "done processing cascades for: NHibernateTest.Order" zonder dat er een INSERT is gedaan.
Iemand enig idee wat ik fout doe?

It’s nice to be important but it’s more important to be nice


Acties:
  • 0 Henk 'm!

  • Gert
  • Registratie: Juni 1999
  • Laatst online: 07-11-2024
En als je eerst de Order opslaat en vervolvens OrderLines toevoegt? Binnen dezelfde session hoef je dan alleen maar session.lush() aan te roepen.

Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 11:22
Is er een reden waarom je een list gebruikt in je Order hbm file ? Gebruik eens een bag:
Please note that NHibernate does not support bidirectional one-to-many associations with an indexed collection (list, map, or array) as the "many" end, you have to use a set or bag mapping.

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • pjonk
  • Registratie: November 2000
  • Laatst online: 10-09 15:33
Bedankt voor de antwoorden.
whoami schreef op zondag 12 november 2006 @ 10:57:
Is er een reden waarom je een list gebruikt in je Order hbm file ? Gebruik eens een bag:
[...]
De reden om een <list> te gebruiken was omdat ik een sortering op linenumber wilde hebben. Hiervoor kon ik de <index column> gebruiken. Ik zie echter dat ik met een bag en een order-by attribute hetzelfde kan bereiken, dus ik heb nu:
XML:
1
<bag name="Orderlines" inverse="true" order-by="Linenumber" cascade="all">

Helaas lost dit mijn probleem met het opslaan nog niet op.
Gert schreef op zondag 12 november 2006 @ 10:49:
En als je eerst de Order opslaat en vervolvens OrderLines toevoegt? Binnen dezelfde session hoef je dan alleen maar session.lush() aan te roepen.
Ja en dit werkt inderdaad wel. Wat ik heel vreemd vind is dat de Orderlines collectie leeg wordt gemaakt na het saven van een collectie:
C#:
1
2
3
// Hier is o.Orderlines.Count 2
session.Save(o);
// Hier is o.Orderlines.Count 0 geworden

Waarom gebeurt dit?

Edit:
Nog even gekeken in de Hibernate FAQ http://www.hibernate.org/116.html#A7
I saved a parent object but its associated objects weren't saved to the database.
Associated objects must be saved explicitly by calling Session.save() (or Session.persist()) or the association must be mapped with cascade="all" or cascade="save-update" (or cascade="persist").

Die cascade="all" zou toch gewoon moeten werken :?

[ Voor 15% gewijzigd door pjonk op 12-11-2006 15:40 ]

It’s nice to be important but it’s more important to be nice


Acties:
  • 0 Henk 'm!

  • Zinno1973
  • Registratie: November 2006
  • Laatst online: 25-04-2022
Volgens mij moet het attribuut inverse="true" niet bij Order staan maar bij Orderline.
Het attribuut inverse betekent dat hibernate deze kant van de relatie niet meeneent
in het geval van cascades en dirty checking. Zet je dit dus bij Orderline, dan zal het
door save-en van een Order ook de Orderlines gesave-ed worden.

Acties:
  • 0 Henk 'm!

  • pjonk
  • Registratie: November 2000
  • Laatst online: 10-09 15:33
Zinno1973 schreef op maandag 13 november 2006 @ 07:54:
Volgens mij moet het attribuut inverse="true" niet bij Order staan maar bij Orderline.
Het attribuut inverse betekent dat hibernate deze kant van de relatie niet meeneent
in het geval van cascades en dirty checking. Zet je dit dus bij Orderline, dan zal het
door save-en van een Order ook de Orderlines gesave-ed worden.
Bedankt denk dat ik het nu begin te vatten. Ik heb nu ook wat uitleg en in een sample in de NHibernate documentatie gevonden. Jij bedoelt dus hetzelfde als hieronder?
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
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

It’s nice to be important but it’s more important to be nice


Acties:
  • 0 Henk 'm!

  • Zinno1973
  • Registratie: November 2006
  • Laatst online: 25-04-2022
klopt in het voorbeeld heeft een item een categorie en categorie weer een verwijzing naar item. Inverse is aangegeven bij Category, dit betekent dat hibernate deze kant van de relatie "niet meeneemt" in bijvoorbeeld dirty checking. Zou je dit weg laten dan zou hibernate in een loop komen.

Je moet je dus van te voren bedenken welke kant van de relatie je in je applicatie gaat gebruiken.

Acties:
  • 0 Henk 'm!

  • momania
  • Registratie: Mei 2000
  • Laatst online: 11-09 12:30

momania

iPhone 30! Bam!

Type collectie dat je gebruikt maakt niets uit hier voor de cascading save.

Zorg dat je op je parent class een method hebt om childs toe te voegen in deze vorm (even in java):

Java:
1
2
3
4
5
6
7
8
9
10
11
class Parent {
  Set childs;

  public void addChild(Child child) {
    if (childs == null) { 
      childs = new HashSet();
    }
    child.setParent(this);
    childs.add(child);
  }
}


Belangrijkste hier is dus dat je niet alleen de childs toevoegd aan de collectie, maar ook bij iedere child de referentie terug maakt naar de parent. :)

Neem je whisky mee, is het te weinig... *zucht*


Acties:
  • 0 Henk 'm!

  • pjonk
  • Registratie: November 2000
  • Laatst online: 10-09 15:33
momania schreef op maandag 13 november 2006 @ 16:47:
Belangrijkste hier is dus dat je niet alleen de childs toevoegd aan de collectie, maar ook bij iedere child de referentie terug maakt naar de parent. :)
Dat klopt, maar dat doe ik ook, zie hieronder:
C#:
1
2
3
4
Orderline line1 = new Orderline();
line1.Order = o; // Deze zet dus de parent ;)
line1.Linenumber = 0;
line1.Description = "Regel 1"; 

De methode van Gert om de orderlines tijdens Hibernate sessie toe te voegen na het opslaan van de Order is de enige manier die voor mij werkt. Blijkbaar moet je de childs toevoegen in een sessie anders snapt NHibernate het niet?
Overigens na mijn session.Save(o) verdwijnen met Orderlines uit de collectie van de order. Misschien toch iets met dirty checking?

It’s nice to be important but it’s more important to be nice


Acties:
  • 0 Henk 'm!

  • Zinno1973
  • Registratie: November 2006
  • Laatst online: 25-04-2022
Je zet wel de parent van de Orderline, maar voeg je de Orderline ook toe als child aan de Order?

Dus iets als:
Order order = new Order();
.
.
.
Orderline line1 = new Orderline();
line1.Order = order;
line1.Linenumber = 0;
line1.Description = "Regel 1";
order.addOrderline(line1 ); //of order.getOrderlines().add(line1 );

Als je dit doet en je het attribuut 'reverse' bij orderline staan dan wordt een orderlien opgeslagen zodra je een order opslaat.
Pagina: 1