NHibernate 3 (Parte 5) Primer mapeo Uno-a-Varios

Published on Author lopezLeave a comment

Anterior post
Próximo post

Esta vez, tengo este modelo de datos para mapear a clases:

Cada Book (libro) tiene 0, 1  o más Chapter (Capítulos). Este es el diagrama de clases de mi dominio:

 

El código para Book.cs:

public class Book { public virtual Guid Id { get; set; } public virtual string Title { get; set; } public virtual string Author { get; set; } public virtual IList<Chapter> Chapters { get; set; } }



Como antes, las propiedades son virtuales (tengo que escribir por qué son virtuales, en un post de esta serie). Y noten, Chapter es un  IList<Chapter>. El IList será armado por NHibernate. El Chapter.cs:

public class Chapter { public virtual Guid Id { get; set; } public virtual string Title { get; set; } public virtual string Notes { get; set; } }



El mapeo de Book tiene un nuevo elemento:

<?xml version=”1.0″ encoding=”utf-8″ ?> <hibernatemapping xmlns=“urn:nhibernate-mapping-2.2” assembly=“Books” namespace=“Books”> <class name=“Book” table=“Books”> <id name=“Id”> <generator class=“guid.comb” /> </id> <property name=“Title” notnull=“true” /> <property name=“Author” notnull=“true”/> <list name=“Chapters” cascade=“all-delete-orphan”> <key column=“BookId”/> <index column=“ChapterIndex”/> <onetomany class=“Chapter”/> </list> </class> </hibernate-mapping>



El elemento <list> apunta a la propiedad de Book llamada Chapters (la IList). Los elementos serán ordenados basado en la columna de table ChapterIndex. BookId y ChapterIndex no son propiedades de la clase Chapter. Son columnas en la tabla Chapter. El mapeo de la clase Chapter no tiene ninguna referencia a Book:

<?xml version=”1.0″ encoding=”utf-8″ ?> <hibernatemapping xmlns=“urn:nhibernate-mapping-2.2” assembly=“Books” namespace=“Books”> <class name=“Chapter” table=“Chapters”> <id name=“Id”> <generator class=“guid.comb” /> </id> <property name=“Title” notnull=“true” /> <property name=“Notes” /> </class> </hibernate-mapping>



 

¿Qué es eso de “delete-all-orphan”? Vean

http://www.nhforge.org/doc/nh/en/index.html#collections-onetomany

El programador puede agregar y remover capítulos de un objeto libro, y entonces, NHibernate se encargará de su persistencia. Si un objeto se remueve de la lista por código, NHibernate eliminará el capítulo removido SI no ha sido agregado mientras tanto a OTRO libro (es decir, quedó como objeto, pero huérfano).

Este es el código para agregar y recuperar un libro con capítulos:

ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory(); using (ISession session = sessionFactory.OpenSession()) { using (ITransaction tx = session.BeginTransaction()) { Book cookbook = new Book() { Title = “NHibernate Cookbook“, Author = “Jason Dentler“, Chapters = new List<Chapter>() { new Chapter() { Title = “Models and Mappings” }, new Chapter() { Title = “Configuration and Schema” }, new Chapter() { Title = “Sessions and Transactions” } } }; session.Save(cookbook); foreach (Book book in session.Query<Book>()) { System.Console.WriteLine(string.Format(“Book {0}“, book.Title)); int nchapter = 0; foreach (Chapter chapter in book.Chapters) System.Console.WriteLine(string.Format(“Chapter {0}:{1}“, ++nchapter, chapter.Title)); } tx.Commit(); session.Close(); } }



Tuve un problema al escribir el ejemplo: inicialmente había definido ChapterIndex y BookId como columnas que no admitían null, en la tabla Chapters. Pero, notablemente, NHibernate requiere que esos campos puedan aceptar valores null. Así que en mis primeras pruebas, me daba un error al grabar un nuevo libro con capítulos. Ahí me di cuenta de esta nota (en la documentación de NHForge, en el enlace que puse más arriba):

Very Important Note: If the <key> column of a <one-to-many> association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse=”true”. See the discussion of bidirectional associations later in this chapter.

Cambié las columnas a nullable, y todo funcionó. Me asombra este requirimiento: yo esperaba que NHibernate llene las columnas BookId y ChapterIndex DURANTE la grabación de cada capítulo. Pero nones. Graba primero el capítulo con los campos en null, y luego los actualiza.

El código de este post, como es usual, está en mi AjCodeKatas Google Project, en trunk/NHibernate/BooksOneToMany. Pueden bajar una versión “actual congelada” desde NHibernate3BooksOneToMany.zip.

Próximos pasos: explorar otras opciones uno-a-varios, mapeos bidireccionales, y más.

Fuentes consultadas: Jason Dentler’s NHibernate 3.0 Cookbook
http://ayende.com/Blog/archive/2006/12/02/nhibernatecascadesthedifferentbetweenallalldeleteorphansandsaveupdate.aspx
http://www.nhforge.org/doc/nh/en/index.html#collections-onetomany

Nos leemos!

Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez

Leave a Reply

Your email address will not be published. Required fields are marked *