Forzando a NHibernate a eliminar en cascada antes de inserciones


Tengo un objeto padre que tiene una relación de uno a muchos con un ISet de objetos hijos. Los objetos secundarios tienen una restricción única (PageNum y ContentID - la clave foránea para el padre).

<set name="Pages" inverse="true" cascade="all-delete-orphan" access="field.camelcase-underscore">
    <key column="ContentId" />
    <one-to-many class="DeveloperFusion.Domain.Entities.ContentPage, DeveloperFusion.Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
</set>

El problema que he encontrado es si elimino un elemento ContentPage de la colección principal, y luego añado uno nuevo con la misma clave única dentro de la misma transacción... Se obtiene una violación de restricción única porque NHibernate intenta realizar el insert antes de el eliminar.

¿Hay alguna manera de forzar a NHibernate a realizar la eliminación primero?

Author: Peter Mortensen, 2009-04-01

3 answers

No hay opción para especificar el orden de operaciones en una transacción, ya que está codificada de la siguiente manera (de la documentación):

Las sentencias SQL se emiten en el siguiente orden

  • todas las inserciones de entidades, en el mismo orden los objetos correspondientes se guardaron usando ISession.Save ()
  • todas las actualizaciones de la entidad
  • todas las eliminaciones de colecciones
  • todas las eliminaciones, actualizaciones e inserciones de elementos de la colección
  • toda la colección inserciones
  • todas las eliminaciones de entidades, en el mismo orden, los objetos correspondientes se eliminaron utilizando ISession.Eliminar()

(Una excepción es que los objetos que usan la generación nativa de ID se insertan cuando se guardan.)

Como tal, ¿puedo desafiarle a responder por qué está agregando una nueva entidad con un identificador existente? Se supone que un identificador es único para una "entidad" específica."Si esa entidad se ha ido, también debería serlo su identificador.

Otra opción sería actualizar ese registro en lugar de eliminar/insertar. Esto mantiene el ID igual, por lo que no hay una violación de restricción única (al menos en la clave) y puede cambiar todos los demás datos para que sea un registro "nuevo".

EDITAR: Así que aparentemente no estaba prestando atención a la pregunta cuando respondí, ya que este es un problema con una restricción única en una columna de clave no primaria.

Creo que tienes dos soluciones para elegir de:

  1. Llame a Session.Flush() después de su eliminación que ejecutará todos los cambios en la sesión hasta ese punto, después de lo cual puede continuar con el resto (insertando su nuevo objeto). Esto también funciona dentro de una transacción, por lo que no necesita preocuparse por la atomicidad.
  2. Cree una función ReplacePage que actualice la entidad existente con nuevos datos pero mantenga la clave primaria y la columna única iguales.
 30
Author: Stuart Childs,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-02 17:43:57

Estoy experimentando el mismo problema ... Tengo una entidad que tiene una colección que se asigna a una tabla que contiene una restricción única.

Lo que no entiendo es que, según la respuesta de Stuarts, las eliminaciones de colecciones deben ocurrir antes de las inserciones de colecciones? Cuando me sumerjo en el código fuente de NHibernate, encuentro la clase CollectionUpdateAction, que contiene este código en su método Execute:

persister.DeleteRows(collection, id, session);
persister.UpdateRows(collection, id, session);
persister.InsertRows(collection, id, session);

Entonces, asumiría que las eliminaciones se ejecutan antes de las inserciones, pero aparentemente esto no es el caso. ¿No se utiliza la acción CollectionUpdateAction en este escenario? ¿Cuándo se utiliza la acción CollectionUpdateAction?

Respuesta:

He trabajado alrededor de esto de la siguiente manera:

  • En mi asignación, he establecido la opción de cascada en ' delete-orphan 'en lugar de'all-delete-orphan'
  • Todo el acceso a mi base de datos va a través de un repositorio; en el método save de mi repositorio, tengo este código para mi entidad:

    public void Save( Order orderObj )
    {
        // Only starts a transaction when there is no transaction
        // associated yet with the session
       With.Transaction(session, delegate()
       {
           session.SaveOrUpdate (orderObj);
           session.Flush();
    
           foreach( OrderLine line in orderObj.Lines )
           {
                session.SaveOrUpdate (line);
           }
        };
     }
    

Así que, guardo el orderObj, y porque la cascada se establece en delete-orphan, los objetos que se van a eliminar, se eliminarán de la base de datos.

Después de llamar a SaveOrUpdate, debo asegurarme de vaciar los cambios en la base de datos.

Dado que la configuración de cascada delete-orphan se asegura de que no se inserten ni actualicen líneas de orden, tengo que recorrer mi colección y llamar a 'saveOrUpdate' para cada línea de orden. Esto asegurará que se inserten nuevas líneas de orden y que se actualicen las modificadas. No se realizará ninguna acción para OrderLines que no han cambiado.

Aunque no es la solución ideal (es un truco feo en mi humilde opinión), funciona y se abstrae detrás del repositorio, así que así es como lidio con este problema por ahora...

 4
Author: Frederik Gheysels,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-21 11:02:29

Si se deshace de la clave primaria secundaria (ContentPage.Id) y hacer la clave compuesta la clave primaria (es decir, pageNum y contentId), entonces creo que puede funcionar. Esta es una solución ofrecida al mismo problema que ocurre en hibernación.

 0
Author: Iain,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-11-03 13:35:15