2012-01-23 12 views
6

In un'applicazione Grails, il comportamento predefinito dei metodi di servizio è che sono transazionali e che la transazione viene automaticamente ripristinata se viene generata un'eccezione non controllata. Tuttavia, in Groovy uno non è obbligato a gestire (o ripensare) le eccezioni controllate, quindi c'è il rischio che se un metodo di servizio genera un'eccezione controllata, la transazione non verrà ripristinata. A causa di questo, sembra opportuno annotare ogni Grails classe di servizioComportamento transazione servizio Grails

@Transactional(rollbackFor = Throwable.class) 
class MyService { 

    void writeSomething() { 
    } 
} 

Si supponga che ho altri metodi in MyService, uno dei quali legge solo il DB, e l'altro non toccare il DB, sono i seguenti annotazioni corrette?

@Transactional(readOnly = true) 
void readSomething() {} 

// Maybe this should be propagation = Propagation.NOT_SUPPORTED instead? 
@Transactional(propagation = Propagation.SUPPORTS) 
void dontReadOrWrite() {} 

Al fine di rispondere a questa domanda, credo che avrete bisogno di sapere che cosa la mia intenzione è:

  • Se viene generata un'eccezione da qualsiasi metodo e c'è una transazione in corso, sarà essere tirato indietro. Ad esempio, se writeSomething() chiama dontReadOrWrite() e un'eccezione viene generata da quest'ultimo, la transazione avviata dal primo verrà ripristinata. Suppongo che l'attributo a livello di classe rollbackFor sia ereditato dai singoli metodi a meno che non lo sovrascrivano esplicitamente.
  • Se c'è nessuna transazione in corso, non viene avviata per metodi come dontReadOrWrite
  • Se nessuna transazione è in corso quando readSomething() si chiama, sarà avviata una transazione di sola lettura. Se è in corso una transazione in lettura/scrittura, parteciperà a questa transazione.

risposta

1

Penso che quello che stai cercando sia una gestione delle transazioni più granulare, e usare l'annotazione @Transactional è la direzione giusta per quello. Detto questo, c'è un Grails Transaction Handling Plugin che può darti il ​​comportamento che stai cercando. L'avvertenza è che sarà necessario avvolgere le chiamate del metodo di servizio in una chiusura DomainClass.withTransaction e fornire il comportamento non standard che si sta cercando come una mappatura dei parametri al metodo withTransaction().

Come nota, sul backend questo sta facendo esattamente ciò di cui si sta parlando sopra utilizzando l'annotazione @Transactional per modificare il comportamento della transazione in fase di esecuzione. La documentazione del plugin è eccellente, quindi non penso che ti troverai senza una guida sufficiente.

Spero che questo sia quello che stai cercando.

4

Il tuo codice è corretto: vuoi utilizzare l'annotazione Spring @Transactional su singoli metodi nella classe di servizio per ottenere la granularità che stai cercando, hai ragione che desideri SUPPORTI per dontReadOrWrite (NOT_SUPPORTED sospenderà una transazione esistente, che non ti comprerà nulla in base a ciò che hai descritto e richiederà che il tuo software passi i cicli, quindi c'è dolore senza alcun guadagno), e hai ragione che vuoi il default comportamento di propagazione (OBBLIGATORIO) per readSomething.

Ma una cosa importante da tenere a mente con il comportamento delle transazioni Spring è che Spring implementa la gestione delle transazioni inserendo la classe in un proxy che esegue l'impostazione della transazione appropriata, invoca il metodo e quindi esegue la transazione appropriata quando controlla i ritorni. E (in modo cruciale), questo codice di gestione delle transazioni è chiamato solo invocato quando si chiama il metodo sul proxy, cosa che non accade se writeSomething() chiama direttamente dontReadOrWrite() come nel primo punto elenco.

Se avete bisogno di un comportamento transazionale diverso su un metodo che si chiama con un altro metodo, hai due scelte, che io sappia, se si desidera continuare a utilizzare le annotazioni @Transactional di primavera per la gestione delle transazioni:

  1. Sposta il metodo viene chiamato dall'altro in una classe di servizio diversa, a cui si accede dalla classe di servizio originale tramite il proxy Spring.
  2. Lascia il metodo dov'è. Dichiarare una variabile membro nella classe di servizio dello stesso tipo dell'interfaccia della classe di servizio e renderla @Autowired, che fornirà un riferimento all'oggetto proxy Spring della classe di servizio. Quindi, quando si desidera richiamare il metodo con il diverso comportamento transazionale, farlo su quella variabile membro anziché direttamente, e il codice transazione Spring verrà generato come desiderato.

Approccio # 1 è grande se i due metodi in realtà non sono correlati in ogni caso, perché risolve il problema senza confondere chi finisce per mantenere il vostro codice, e non c'è modo di dimenticare accidentalmente per richiamare il metodo di transazione-enabled .

L'approccio n. 2 è in genere l'opzione migliore, presupponendo che i propri metodi siano tutti nello stesso servizio per un motivo e che non si voglia veramente dividerli. Ma è confuso con un manutentore che non capisce questa ruga delle transazioni di Spring, e devi ricordarti di invocarlo in quel modo in ogni posto che chiami, quindi c'è un prezzo per questo. Di solito sono disposto a pagare quel prezzo per non spezzare le mie classi di servizio in modo innaturale, ma come sempre, dipenderà dalla tua situazione.

Problemi correlati