2012-12-05 14 views
5

Nel mio universo pazzo, una stanza può avere molte sedie e sedie che possono "appartenere" a molte stanze. In Grails sembra così. Le sedie non dovrebbero sapere a quale stanza appartengono.Ottenere "violazione del vincolo di integrità referenziale" durante l'eliminazione dell'oggetto dominio

class Room { 
    String name 
    static hasMany = [chairs: Chair] 
    static constraints = { 
    } 
} 
class Chair { 
    String name 
    static constraints = { 
    } 
} 

voglio eliminare una sedia e rimuovere tutti i riferimenti di sedie in tutti gli oggetti di dominio camera che hanno quel sedia automaticamente. Sono riuscito, ma con una correzione che non mi piace. Nel ChairController ho fatto la seguente

def deleleChair(){ 
    def chairToDelete = Chair.get(params.id) 
    Room.findAll().each {room-> 
     if(room.chairs.contains(chairToDelete)){ 
      room.removeFromChairs(chairToDelete) 
      room.save(failOnError:true) 
     } 
    } 
    chairToDelete.delete(params.chairId) 
} 

C'è una configurazione di ibernazione che ho bisogno di impostare in modo che lo fa automaticamente? Sembra una cosa molto comune negli scenari del mondo reale. Non vorrei implementare lo stesso pezzo di codice, quando probabilmente deciderò (esempio più pazzo) che un oggetto dominio Car può avere molte sedie.

Ho provato a utilizzare il plugin push dell'evento Grails. Ho avuto un ChairService con un'azione che ha ascoltato gli eventi beforeDelete in gorm.

class ChairService { 
    @grails.events.Listener(topic = 'beforeDelete', namespace = "gorm") 
    def handleDeletedChair(Chair chair){ 
     Room.findAll().each {room-> 
      if(room.chairs.contains(chairToDelete)){ 
       room.removeFromChairs(chairToDelete) 
       room.save(failOnError:true, flush:true) 
      } 
     } 
    } 
} 

Questa funzione viene chiamata ogni volta che una sedia eliminare è tentato, ma quando si tornare alla ChairController in modo da fare la cancellazione sedia reale, l'azione di eliminazione pensa ancora che il riferimento alla camera è ancora lì e getta una

Caused by JdbcSQLException: Referential integrity constraint violation:"FK4ACA6A6151428364: PUBLIC.ROOM_CHAIR FOREIGN KEY(CHAIR_ID) REFERENCES PUBLIC.CHAIR(ID)"; SQL statement: 

eliminare dalla sedia dove id =? e versione =? [23503-164]

Voglio che questa logica sia separata dalla sedia, la sedia non dovrebbe mai parlare delle stanze.

+1

Se non ti importa che la Sedia possa vedere Camera, tutto diventa molto più facile. – Gregg

risposta

1

Una soluzione semplice sarebbe quella di usare SQL standard:

import grails.events.Listener 
import groovy.sql.Sql 

class ChairService { 
    def dataSource // autowired 

    @Listener(topic='beforeDelete', namespace='gorm') 
     def handleDeletedChair(Chair chairToDelete) { 
     new Sql(dataSource).execute('DELETE FROM room_chair WHERE chair_id=?', [chairToDelete.id]) 
     return true 
    } 
} 

Poiché si tratta di SQL può essere dipendente dal database in uso (ho provato con H2).

+0

Ho provato questo e funziona perfettamente – user1879106

1

Poiché la relazione non esiste in modo bidirezionale, non sono sicuro che esista una soluzione molto più elegante.

Tuttavia, se hai a che fare con un sacco di stanze/sedie, ti consiglio di utilizzare una query di criteri con le proiezioni per caricare solo gli ID che desideri eliminare invece di caricare su tutte le stanze una sedia. Ancora meglio utilizzare il metodo executeUpdate per eliminare "in blocco". Fare questo dovrebbe anche aiutare con i tuoi errori.

grails criteria query docs...

deleting objects in grails docs...

0

ho preso l'errore nel ChairController.delete e questo è l'errore che viene generata. L'eliminazione viene eseguita e l'associazione viene rimossa ma viene generata l'eccetzione. La mia soluzione è abbastanza sgradevole, fondamentalmente ho colto l'eccezione e reso la vista rilevante in seguito.

Caused by HibernateOptimisticLockingFailureException: Object of class [test.cascade.Chair] with identifier [2]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [test.cascade.Chair#2] 
->> 10 | doCall    in test.cascade.ChairService$_handleDeletedChair_closure1 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|  7 | handleDeletedChair in test.cascade.ChairService 
| 238 | invoke . . . . . . in org.grails.plugin.platform.events.registry.DefaultEventsRegistry$ListenerHandler 
| 159 | invokeListeners in org.grails.plugin.platform.events.registry.DefaultEventsRegistry 
|  69 | event . . . . . . in org.grails.plugin.platform.events.publisher.DefaultEventsPublisher 
| 149 | event    in org.grails.plugin.platform.events.EventsImpl 
|  70 | onApplicationEvent in org.grails.plugin.platform.events.publisher.GormBridgePublisher 
| 1110 | runWorker   in java.util.concurrent.ThreadPoolExecutor 
| 603 | run . . . . . . . in java.util.concurrent.ThreadPoolExecutor$Worker 
^ 722 | run    in java.lang.Thread 

Caused by StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [test.cascade.Chair#2] 
->> 10 | doCall    in test.cascade.ChairService$_handleDeletedChair_closure1 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
|  7 | handleDeletedChair in test.cascade.ChairService 
| 238 | invoke . . . . . . in org.grails.plugin.platform.events.registry.DefaultEventsRegistry$ListenerHandler 
| 159 | invokeListeners in org.grails.plugin.platform.events.registry.DefaultEventsRegistry 
|  69 | event . . . . . . in org.grails.plugin.platform.events.publisher.DefaultEventsPublisher 
| 149 | event    in org.grails.plugin.platform.events.EventsImpl 
|  70 | onApplicationEvent in org.grails.plugin.platform.events.publisher.GormBridgePublisher 
| 1110 | runWorker   in java.util.concurrent.ThreadPoolExecutor 
| 603 | run . . . . . . . in java.util.concurrent.ThreadPoolExecutor$Worker 
^ 722 | run    in java.lang.Thread 
Problemi correlati