2012-10-04 12 views
7

Ho un utente con un numero di ruoli. L'utente è collegato ai ruoli utilizzando una tabella di entità di collegamento. Ho impostato il file di configurazione per eliminare in cascata le entità di collegamento dei ruoli utente quando un utente viene eliminato.Perché Session.Evict in OnPostUpdate causa l'eccezione "Possibile accesso non protetto alla sessione"?

Attualmente stiamo utilizzando la cancellazione per eliminare entità. Abbiamo aggiunto un listener di eventi di eliminazione soft attivato da un'eliminazione. Quando un'entità viene eliminata, attiva l'evento DeleteEntity che contrassegna l'entità come eliminata.

Abbiamo anche annullato l'evento OnPostUpdate per rimuovere le entità dalla cache chiamando Evict sull'entità.

Se creo un utente senza ruoli, quindi cancellarlo, tutto funziona correttamente (funziona anche se la cascata è disabilitata). Tuttavia se ho un utente con almeno un ruolo assegnato e cancello l'utente, dopo la chiamata a Evict in OnPostUpdate, ottengo un'eccezione NHibernate "NHibernate.AssertionFailure: Possibile accesso nonthread safe alla sessione".

Ho provato, in OnPostUpdate, a utilizzare la sessione secondaria per eliminare l'entità, l'eccezione non viene generata, tuttavia, l'entità non viene eliminata.

public void UserDelete(.....) 
{ 
    var user = repository.Fetch<User>(id); 

    repository.Remove(user); 
    repository.Connection.Commit(); 
} 


// soft delete event listener 
protected override void DeleteEntity(NHibernate.Event.IEventSource session, object entity, ..) 
{    
    var repositoryEntity = entity as deletableentity; 
    if (repositoryEntity != null) 
    { 
     if (!repositoryEntity.IsDeleted) 
     { 
      // this marks the entity as deleted 
      repositoryEntity.isDeleted = true; 

      // cascade delete 
      this.CascadeBeforeDelete(session, persister, repositoryEntity, entityEntry, transientEntities); 
      this.CascadeAfterDelete(session, persister, repositoryEntity, transientEntities);   
     } 
    } 
} 

public void OnPostUpdate(PostUpdateEvent @event) 
{ 
    if (@event == null) throw new ArgumentNullException("event"); 

    var entity = @event.Entity as deletableentity; 

    // Evict any entities that have been set as deleted from first level cache. 
    if (entity != null && entity.IsDeleted) 
    { 
     @event.Session.Evict(entity); 
    } 
} 

Qualche idea su come risolverlo?

risposta

3

Trovato il problema. L'uso di soft delete attiverà effettivamente l'aggiornamento per impostare isDeleted flag. A causa di questa linea in una mappatura

cascade="all" 

la cascata viene applicato sia per Update e sfrattare azioni. Il mio postUpdate verrà licenziato 2 volte, ma allo stesso tempo Evict tenterà di sfrattare sulle entità figlio.

La soluzione era rimuovere Evict da cascata nel file di mapping. Ora è:

cascade="persist, merge, save-update, delete, lock, refresh" 
+0

grazie mille! – KeatsPeeks

1

Ho incontrato lo stesso problema, ma dal momento che sto utilizzando mappature Fluent non c'è alcuna opzione per escludere Evict dalla cascata. La mia soluzione era quella di evitare di chiamare Evict e basta rimuovere l'entità della cache di sessione:

public void OnPostUpdate(PostUpdateEvent @event) 
{ 
    var entity = @event.Entity as ISoftDeletable; 
    if (entity != null && entity.Deleted) 
    { 
     IEventSource session = @event.Session; 
     IEntityPersister persister = @event.Persister; 

     var key = new EntityKey(@event.Id, persister, session.EntityMode); 
     session.PersistenceContext.RemoveEntity(key); 
     session.PersistenceContext.RemoveProxy(key); 
    } 
} 
+0

wow non riesco a credere che l'ibernazione richieda shenanigans ... – rogerdpack

11

Secondo https://forum.hibernate.org/viewtopic.php?p=2424890 un altro modo per evitare questo è quello di chiamare fondamentalmente

  session.save(s); 
      session.flush(); // allow evict to work 
      session.evict(s); 

e che la radice del problema è che "se ho sfrattato l'entità dalla cache il commit() non lo troverà lì" (cioè non è un problema di sicurezza del thread, è un problema di cache è stato modificato).

+0

Questo era anche il mio problema, anche se stavo usando Hibernate tramite l'interfaccia JPA e mi occupavo di em.persist (...), em.flush() ed em.detach (...). – kilo

+3

Quindi il caso è "L'entità appena creata è stata rimossa (sfrattata) dalla sessione prima di essere salvata nel DB" – Lu55

Problemi correlati