Anterior Post
Siguiente Post
En el anterior ejemplo tenía un problema a resolver: por cada capítulo de un libro nuevo se ejecutaba un INSERT Y un UPDATE, cuando bien podría bastar uno. En este post, uso una manera alternativa de mapear una lista uno-a-varios, usando el atribute inverse. Como ya es usual, el código del ejemplo está en mi AjCodeKata Code Project, en el directorio trunk/NHibernate/BookChapters. Pueden bajarse una versión “congelada a hoy” desde NHibernate3BookChaptersInverse.zip.
Modifiqué la anterior solución, agregando un nuevo proyecto de consola Books.Inverse, copiando el proyecto Books.Console original. Entonces, agregué un nuevo atributo en el mapeo de Book:
<list name="Chapters" cascade="all-delete-orphan"
inverse="true">
<key column="BookId"/>
<index column="ChapterIndex"/>
<one-to-many class="Chapter"/>
</list>
El nuevo atributo es inverse=”true”. Con esta pista, NHibernate conoce que el manejo de la lista y sus elementos está bajo el control del programador. ¿Qué implica esto? Ejecuté el nuevo programa de consola: cada capítulo es grabado con un solo INSERT, pero el resultado en la base de datos fue:
No hay BookId, no hay ChapterIndex con valores, son todos null! El problema está en el código original:
cookbook.Chapters.Add(new Chapter() { Title = "Models and Mappings" });
cookbook.Chapters.Add(new Chapter() { Title = "Configuration and Schema" });
cookbook.Chapters.Add(new Chapter() { Title = "Sessions and Transactions" });
Cuando creo un nuevo Chapter, no pongo valores en Book o en su Chapter Index. Esos datos los ponía el NHiberante. Pero ahora con inverse=”true”, esos valores caen bajo mi responsabilidad: NHibernate no interviene. Entonces, para solucionar el problema, cambié la clase Chapter, agregando una nueva propiedad:
// Used in Inverse example
public virtual int ChapterIndex { get; set; }
Y cambié el código a:
cookbook.Chapters.Add(new Chapter() { Title = "Models and Mappings", Book = cookbook, ChapterIndex = 0 });
cookbook.Chapters.Add(new Chapter() { Title = "Configuration and Schema", Book = cookbook, ChapterIndex = 1 });
cookbook.Chapters.Add(new Chapter() { Title = "Sessions and Transactions", Book = cookbook, ChapterIndex = 2 });
Ahora, la salida muestra los insert correctos:
NHibernate: INSERT INTO Books (Title, Author, Id) VALUES (@p0, @p1, @p2);@p0 =
'NHibernate Cookbook' [Type: String (4000)], @p1 = 'Jason Dentler'
[Type: String (4000)], @p2 = b643aa0e-1917-4066-96a4-64a09562a58a [Type: Guid (0)]
NHibernate: INSERT INTO Chapters (Title, Notes, BookId, ChapterIndex, Id) VALUES
(@p0, @p1, @p2, @p3, @p4);@p0 = 'Models and Mappings' [Type: String (4000)], @p1
= NULL [Type: String (4000)], @p2 = b643aa0e-1917-4066-96a4-64a09562a58a
[Type : Guid (0)], @p3 = 0 [Type: Int32 (0)], @p4 =
df9114af-7183-4b1b-9826-d12982a898a5 [Type: Guid (0)]
NHibernate: INSERT INTO Chapters (Title, Notes, BookId, ChapterIndex, Id) VALUES
(@p0, @p1, @p2, @p3, @p4);@p0 = 'Configuration and Schema'
[Type: String (4000)], @p1 = NULL [Type: String (4000)], @p2 =
b643aa0e-1917-4066-96a4-64a09562a58a [Type: Guid (0)], @p3 = 1 [Type: Int32 (0)],
@p4 = 3fc1df36-c7de-4ff8-add2-8f843b627115 [Type: Guid (0)]
NHibernate: INSERT INTO Chapters (Title, Notes, BookId, ChapterIndex, Id) VALUES
(@p0, @p1, @p2, @p3, @p4);@p0 = 'Sessions and Transactions'
[Type: String (4000)], @p1 = NULL [Type: String (4000)], @p2 =
b643aa0e-1917-4066-96a4-64a09562a58a [Type: Guid (0)], @p3 = 2 [Type: Int32 (0)],
@p4 = 9c8891e4-6419-4469-aa3a-154f2c908985 [Type: Guid (0)]
Los datos grabados están bien:
Todo bien! Noten que no tuve que agregar los capítulos a la sesión de NHibernate: son grabados como parte del libro nuevo, igual que en el anterior ejemplo. Pero con inverse=true, tuve que poner los valores de la relación inversa (de Chapter a Book) y la propiedad de orden que había elegido.
Próximos pasos: explorar borrado y actualización, mapeos más complejos, otras opciones de logging, otras formas de hacer mapeos (como Fluen NHibernate, ConfORM, y el nuevo mapeo por código de NHibernate 3.2).
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez