The official Fatica Labs Blog! RSS 2.0
# Wednesday, 29 September 2010

Note: For visitors of your site, this entry is only displayed for users with the preselected language Romanian/română (ro)

Continuare la partea a 4-a

În această parte vom vedea un alt tip de colecţie, şi cum, în general putem colecţiona obiecte ce nu sunt entităţi, adică obiecte care nu administrează pe cont propriu o identitate, şi a căror existenţă are sens doar dacă sunt conectate la entitatea cuprinzătoare. Pentru a face asta adăugăm entitatea Produs la modelul nostru, de care vom avea oricum nevoie pentru alte lucruri, şi presupunem că avem un simplu depozit, organizat pe celule, ce conţin stocul produsului în chestiune. Să mai spunem că pentru a identifica celula ne este de ajuns un singur şir. În realitate un depozit va fi mult mai complex, dar această simplificare este un pretext pentru a arăta strategia de colecţie MAP, iar într-o realitate foarte redusă ar putea chiar să aibă un sens aşa cum e făcut.

 

Să adăugăm deci mapping-ul entităţii Produs, cu un map al locaţiilor depozitului şi relativele cantităţi anexat:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHFromScratch.Entities" 
   3:                                                       assembly="NHFromScratch.Entities">
   4:   <class name="Product" table="Products">
   5:     <id name="Id" type="Int32">
   6:       <generator class="native"/>
   7:     </id>
   8:     <property name="Code" type="AnsiString" />
   9:     <property name="Description" type="AnsiString"/>
  10:     <map name="Warehouse">
  11:       <key column="Product"/>
  12:       <index column="Cell" type="AnsiString"/>
  13:       <element column="Quantity" type="Int32"></element>  
  14:     </map>
  15:     <!-- details comes here -->
  16:   </class>
  17:   
  18: </hibernate-mapping>
  19:  

Să vedem cum este declarat <map/>, sau mai bine spus, un mod pentru a declara un map, în acest caz un map indexat cu un şir ce identifică celula depozitului, şi cu un număr care indică cantitatea produsului prezent în această celulă. În tabela ce conţine map-ul, Tag Key este referinţa la entitatea mamă (în acest caz Product), apoi avem nevoie de un indice, în acest caz este „Cell”-ul depozitului, iar valoarea, în acest caz este Quantity a produsului. Lansând unit test-ul de creare a bazei de date obţinem, pentru partea care ne interesează, următoarea schemă:

 

 

image

 

Dacă mergem să examinăm mai bine DDL-ul observăm, în partea de creare a tabelei warehouse, următorul script:

 

clip_image001

Cheia primară a tabelei este deci constituită din FK-ul tabelei parent(Product) combinată cu componenta „Index” - în acest caz Cell -. Nu am putea avea, pentru un produs, două informaţii relative aceleiaşi celule, sau locaţii de depozit.

 

Să presupunem că pentru a localiza poziţia în depozit sunt necesare, însă, două informaţii, de exemplu rând şi coloană, şi că pe lângă cantitate ne interesează să ştim data la care această cantitate a fost stocată; putem modifica mapping-ul după cum urmează:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" namespace="NHFromScratch.Entities" 
   3:                                                       assembly="NHFromScratch.Entities">
   4:   <class name="Product" table="Products">
   5:     <id name="Id" type="Int32">
   6:       <generator class="native"/>
   7:     </id>
   8:     <property name="Code" type="AnsiString" />
   9:     <property name="Description" type="AnsiString"/>
  10:     <map name="Warehouse">
  11:       <key column="Product"/>
  12:       <composite-index class ="WarehouseLocation">
  13:         <key-property name="CellRow" type="AnsiString" />
  14:         <key-property name="CellCol" type="AnsiString" />
  15:       </composite-index>
  16:       <composite-element class="StockInformation">
  17:         <property name="StockDate" type="DateTime"/>
  18:         <property name="Quantity" type="Int32"/>
  19:       </composite-element>
  20:     </map>
  21:     <!-- details comes here -->
  22:   </class>
  23:   
  24: </hibernate-mapping>

Practic am înlocuit “<index>” cu “<composite-index>” şi “<element>” cu “<composite-element>”. Desigur putem avea şi scenarii în care doar indicele este compozit, sau doar elementul. Imediat ce lansăm compilarea hbm2net generează câteva fişiere în plus:

 

 

clip_image001[5]

 

Practic avem o clasă pentru a reprezenta „cheia” ( WarehouseLocation ) în map şi una pentru a reprezenta „elementul” ( StockInformation ). Clasa utilizată pentru cheie trebuie să poată acţiona ca atare, şi deci trebuie să efectueze oportune ovveride -uri ale metodelor Equals si  GetHashCode; suferă comportamente imprevizibile ale codului. Utilizând hbm2net acesta generează codul oportun:

   1: public override bool Equals(object other)
   2: {
   3:     if (this == other)
   4:         return true;
   5:     WarehouseLocation rhs = other as WarehouseLocation;
   6:     if (rhs == null) 
   7:         return false; // null or not a WarehouseLocation
   8:     return _cellRow.Equals(rhs._cellRow) && _cellCol.Equals(rhs._cellCol);
   9: }
  10:  
  11: public override int GetHashCode()
  12: {
  13:     return _cellRow.GetHashCode() ^ _cellCol.GetHashCode();
  14: }

Adăugând aceste două clase la proiect putem regenera baza de date, care rezultă modificată astfel:

 

clip_image001[7]

Diagrama clase devine, pentru partea product, următoarea:

clip_image001[9] 

 

Este interesant să observăm că din punctul de vedere OR/M StockInformation si WarehouseInformation *nu* sunt entităţi. În această parte a cursului s-a dezminţit deci presupusa corespondenţă 1:1 între entităţi şi tabele.

 

Acum să scriem unit test-ul obişnuit pentru a vedea cum se comportă NH-ul în timpul CRUD-ului în această collection. Nu ne întoarcem asupra subiectului „lazy” întrucât analog partii a 4-a pentru <bag/>.

 

 

Codul unit test-ului pe care-l vom lansa este următorul:

   1: [Test]
   2: public void TestCrud()
   3: {
   4:    Console.WriteLine("Creating a product");
   5:    using (ISession session = sessionFactory.OpenSession())
   6:    using (ITransaction trns = session.BeginTransaction())
   7:    {
   8:        Product p = new Product();
   9:        p.Code = "P00000";
  10:        p.Description = " a sample product";
  11:        Console.WriteLine("Adding some Warehouse information");
  12:        var l1 = new WarehouseLocation() { CellCol = "A", CellRow = "1" };
  13:        p.Warehouse.Add(new KeyValuePair<WarehouseLocation, StockInformation>(l1, new StockInformation()));
  14:        p.Warehouse[l1].Quantity = 12;
  15:        p.Warehouse[l1].StockDate = DateTime.Now;
  16:        var l2 = new WarehouseLocation() { CellCol = "A", CellRow = "2" };
  17:        p.Warehouse.Add(new KeyValuePair<WarehouseLocation, StockInformation>(l2, new StockInformation()));
  18:        p.Warehouse[l2].Quantity = 42;
  19:        p.Warehouse[l2].StockDate = DateTime.Now.AddDays(-7);
  20:        Console.WriteLine("Persisting the Product");
  21:        session.SaveOrUpdate(p);
  22:        trns.Commit();
  23:    }
  24:    Console.WriteLine("Modifying warehouse information of a product");
  25:    using (ISession session = sessionFactory.OpenSession())
  26:    using (ITransaction trns = session.BeginTransaction())
  27:    {
  28:        Console.WriteLine("Retrieving product by code");
  29:        var p = session.CreateCriteria<Product>()
  30:            .Add(Expression.Eq("Code", "P00000"))
  31:            .UniqueResult<Product>();
  32:        
  33:        Console.WriteLine("Modify stored quantity on cell A1");
  34:        var l1 = new WarehouseLocation() { CellCol = "A", CellRow = "1" };
  35:        
  36:        p.Warehouse[l1].Quantity -=3;
  37:        p.Warehouse[l1].StockDate = DateTime.Now;
  38:        
  39:        Console.WriteLine("Persisting the Product");
  40:        session.SaveOrUpdate(p);
  41:        trns.Commit();
  42:    }
  43:    Console.WriteLine("deleting warehouse information of a product");
  44:    using (ISession session = sessionFactory.OpenSession())
  45:    using (ITransaction trns = session.BeginTransaction())
  46:    {
  47:        Console.WriteLine("Retrieving product by code");
  48:        var p = session.CreateCriteria<Product>()
  49:            .Add(Expression.Eq("Code", "P00000"))
  50:            .UniqueResult<Product>();
  51:  
  52:        Console.WriteLine("Modify stored quantity on cell A1");
  53:        var l1 = new WarehouseLocation() { CellCol = "A", CellRow = "1" };
  54:  
  55:        p.Warehouse.Remove(l1);
  56:  
  57:        Console.WriteLine("Persisting the Product");
  58:        session.SaveOrUpdate(p);
  59:        trns.Commit();
  60:    }
  61: }

Care produce următorul rezultat. S-au evidenţiat punctele în care se vede cum NH accesează la elementele colecţiei:

 

image

După cum se vede, map este foarte eficient şi accesează în update/delete elemente individuale ale colecţiei. Contrar acestei afirmaţii, având în vedere faptul că structura este reprezentată în memorie printr-un dictionary, nu este posibilă ordonarea sa (chiar făcând query-ul cu un order-by, funcţionarea internă a dicţionarului ar putea schimba această ordine).

Derscarca sursa
Partea a 4-a
Partea a 6-a
Wednesday, 29 September 2010 21:37:34 (GMT Daylight Time, UTC+01:00)  #    Comments [0] - Trackback
hbm2net | NHibernate | NHibernate Tutorial

All comments require the approval of the site owner before being displayed.
OpenID
Please login with either your OpenID above, or your details below.
Name
E-mail
(will show your gravatar icon)
Home page

Comment (Some html is allowed: a@href@title, b, strike, strong) where the @ means "attribute." For example, you can use <a href="" title=""> or <blockquote cite="Scott">.  

Enter the code shown (prevents robots):

Live Comment Preview
My Stack Overflow
Contacts

Send mail to the author(s) E-mail

Tags
profile for Felice Pollano at Stack Overflow, Q&A for professional and enthusiast programmers
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2019
Felice Pollano
Sign In
Statistics
Total Posts: 157
This Year: 0
This Month: 0
This Week: 0
Comments: 127
This blog visits
All Content © 2019, Felice Pollano
DasBlog theme 'Business' created by Christoph De Baene (delarou) and modified by Felice Pollano