NHibernate - Cascadas

En este capítulo, cubriremos cómo usar la función Cascade. Si tiene un conjunto o colección de artículos o una relación entre dos clases, como nuestro cliente y pedido, y tiene una relación de clave externa. Si borramos el cliente por defecto, NHibernate no hace nada a los objetos hijo, por lo que los que pertenecen a ese cliente y nosotros podríamos ser pedidos huérfanos.

  • También podríamos estar violando restricciones de clave externa, por lo que podemos usar la noción de cascadas.

  • Por defecto, NHibernate no conecta operaciones en cascada a objetos secundarios.

  • La razón de esto es que puede tener relaciones como un cliente que tiene una dirección de envío predeterminada y esa dirección de envío se comparte con muchos clientes diferentes.

  • Por lo tanto, no querrá crear una cascada de esa relación necesariamente porque otros clientes todavía se refieran a ella.

  • Entonces, toda la noción de cascadas es decirle a NHibernate cómo manejar sus entidades secundarias.

Hay diferentes opciones para la conexión en cascada, que son las siguientes:

  • none - que es el predeterminado y significa que no hay cascada.

  • all - que va a guardar, actualizar y eliminar en cascada.

  • save-update - Se pondrá en cascada, guardará y actualizará.

  • delete - Se eliminará en cascada.

  • all-delete-orphan - Es uno especial que se usa con bastante frecuencia y es el mismo que Todos excepto, si encuentra Eliminar filas huérfanas, las eliminará también.

Puede especificar el valor predeterminado en su hbm.xml archivo, para que pueda proporcionar una cascada predeterminada en ese elemento de mapeo de Hibernate o también puede especificarlo para colecciones y relaciones específicas, como la de muchos a uno.

Echemos un vistazo a cascadas de ejemplo simples, solucionemos el problema en el programa, donde tenemos que poner en cascada manualmente el guardado en los pedidos como se muestra en el siguiente código.

using(var session = sessionFactory.OpenSession()) 

using(var tx = session.BeginTransaction()) { 
   var newCustomer = CreateCustomer(); 
   Console.WriteLine("New Customer:"); 
   Console.WriteLine(newCustomer); 
   session.Save(newCustomer); 
	
   foreach (var order in newCustomer.Orders) { 
      session.Save(order); 
   } 
	
   id = newCustomer.Id; 
   tx.Commit(); 
}

En el fragmento de código anterior, puede ver que estamos guardando manualmente todos los pedidos para el cliente. Ahora eliminemos el código en cascada manual en el que se guardan todos los pedidos.

using(var session = sessionFactory.OpenSession())
 
using(var tx = session.BeginTransaction()) { 
   var newCustomer = CreateCustomer(); 
   Console.WriteLine("New Customer:"); 
   Console.WriteLine(newCustomer);
	
   session.Save(newCustomer); 
   id = newCustomer.Id; 
   tx.Commit(); 
}

Necesitamos especificar la opción de cascada en customer.hbm.xml.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
	
   <class name = "Customer"> 
   
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 
      
      <property name = "FirstName"/> 
      <property name = "LastName"/> 
      <property name = "AverageRating"/> 
      <property name = "Points"/> 
      <property name = "HasGoldStatus"/> 
      <property name = "MemberSince" type = "UtcDateTime"/> 
      <property name = "CreditRating" type = "CustomerCreditRatingType"/>
      
      <component name = "Address"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component>
      
      <set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"> 
         <key column = "CustomerId"/> 
         <one-to-many class = "Order"/> 
      </set> 
   
   </class> 
</hibernate-mapping>
  • Ahora, los pedidos pertenecen totalmente al cliente. Entonces, si los clientes fueran eliminados de la base de datos, nuestra aplicación aquí querría eliminar todos esos pedidos, incluidos los que podrían haber quedado huérfanos.

  • Terminará haciendo una eliminación. Con eso, dirá eliminar de la tabla de pedidos, donde el ID de cliente es igual al cliente que está eliminando.

  • Por lo tanto, puede poner en cascada estas eliminaciones. Entonces con elAll, guardará, actualizará y eliminará.

Ahora, cuando ejecute esta aplicación, verá el siguiente resultado.

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Order Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
		
Press <ENTER> to exit...

Como puede ver, hemos eliminado el código del programa que se conectó en cascada manualmente y nuestra aplicación sigue funcionando.

Entonces, dependiendo de su relación, es posible que desee combinarlos en cascada. Ahora, echemos un vistazo a una relación en cascada diferente. Vamos a ir a laOrder.hbm.xml archivo y podemos conectar en cascada esa relación de muchos a uno.

<?xml version = "1.0" encoding = "utf-8" ?> 
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
   namespace = "NHibernateDemo"> 
   
   <class name = "Order" table = "`Order`"> 
	
      <id name = "Id"> 
         <generator class = "guid.comb"/> 
      </id> 

      <property name = "Ordered"/> 
      <property name = "Shipped"/> 
      
      <component name = "ShipTo"> 
         <property name = "Street"/> 
         <property name = "City"/> 
         <property name = "Province"/> 
         <property name = "Country"/> 
      </component> 
      
      <many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/>
		
   </class> 
</hibernate-mapping>

Entonces, si creamos un nuevo pedido y hay un nuevo cliente adjunto al mismo y decimos, guarde ese pedido, es posible que deseemos ponerlo en cascada. Pero una cosa que probablemente no querríamos hacer es si se elimina un pedido para eliminar al cliente correspondiente.

Entonces, aquí, querríamos hacer una actualización guardada, por lo que al usar una actualización guardada, enviará en cascada cualquier guardado o actualización a ese cliente. Entonces, si obtenemos un nuevo cliente o si cambiamos de cliente, eso se producirá en cascada. Si es una eliminación, no la eliminará de la base de datos.

Entonces, al ejecutar nuestra aplicación nuevamente, todo sigue funcionando como se esperaba.

New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
   CreditRating: Good
   AverageRating: 42.42424242

   Orders:
      Id: 00000000-0000-0000-0000-000000000000
      Order Id: 00000000-0000-0000-0000-000000000000

Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134

The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
      John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
		
   Points: 100
   HasGoldStatus: True
   MemberSince: 1/1/2012 12:00:00 AM (Utc)
   CreditRating: Good
   AverageRating: 42.4242

   Orders:
      Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
      Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
		
Press <ENTER> to exit...

Ahora debe echar un vistazo a su aplicación, recuerde que el valor predeterminado es Ninguno y debe pensar en sus entidades y sus relaciones entre ellas para determinar las cascadas apropiadas para cada una de sus entidades, así como cada una de sus relaciones en esa base de datos.