Dove appartiene l'annotazione transazionale?


528

Dovresti inserire il @Transactionalnelle DAOclassi e / o i loro metodi o è meglio annotare le classi di servizio che stanno chiamando usando gli oggetti DAO? Oppure ha senso annotare entrambi i "livelli"?

Risposte:


537

Penso che le transazioni appartengano al livello di servizio. È quello che conosce le unità di lavoro e i casi d'uso. È la risposta giusta se sono stati iniettati diversi DAO in un servizio che deve collaborare in un'unica transazione.


Sono d'accordo. A volte non importa, ma a volte puoi trarne vantaggio, ad esempio la sessione Hibernate viene estesa per la transazione while, quindi tutti gli oggetti caricati si trovano nella cache di 1 ° livello e non è necessario ricollegare gli oggetti alla sessione, più caricati pigramente le proprietà funzionano senza fuzz.
dma_k,

5
Una transazione globale può consistere in più di uno di questi @Transactional (propagation = Propagation.REQUIRED)? O @Transactional è sempre un limite per una transazione? Non sono sicuro di averlo preso dalla documentazione, ma sembra che tu possa creare anche transazioni che consistono in metodi @Transactional e tutto ciò che scorre all'interno
lisak

1
Sì, penso che @Transactional esterno sia quello che diventa il limite per la transazione se Propagation.REQUIRED è attivato.
duffymo,


309

In generale, sono d'accordo con gli altri affermando che le transazioni vengono generalmente avviate a livello di servizio (a seconda della granularità richiesta ovviamente).

Tuttavia, nel frattempo ho anche iniziato ad aggiungere @Transactional(propagation = Propagation.MANDATORY)al mio livello DAO (e altri livelli che non sono autorizzati ad avviare transazioni ma richiedono quelli esistenti) perché è molto più facile rilevare errori in cui hai dimenticato di avviare una transazione nel chiamante ( ad es. il servizio). Se il tuo DAO è annotato con propagazione obbligatoria, otterrai un'eccezione che afferma che non vi è alcuna transazione attiva quando viene invocato il metodo.

Ho anche un test di integrazione in cui controllo tutti i bean (bean post processor) per questa annotazione e fallisco se c'è @Transactionalun'annotazione con propagazione diversa da Obbligatoria in un bean che non appartiene al livello servizi. In questo modo mi assicuro che non iniziamo le transazioni sul livello sbagliato.


3
Questo dovrebbe essere fatto nel livello dao (interfacce), nel dao impl. strato o entrambi?
Johan

3
Non so se si adatta a questa discussione, ma un altro suggerimento potrebbe essere quello di aggiungere @Transactional (readOnly = true) nell'impianto dao nelle operazioni di non scrittura.
maxivis

10
@Johan Spring consiglia di inserire le annotazioni Transaction sulle classi di implementazione anziché sulle interfacce.
Mathias G.,

Mi piace davvero questa idea, provandola anche per i miei progetti
Thomas Einwaller,

1
Fammi vedere se capisco ... stai dicendo che dovrei inserire @Transactionalla classe di implementazione del servizio e dovrei mettere @Transactional(propagation = MANDATORY)l'implementazione della classe DAO (repository)?
John Henckel,

93

Le annotazioni transazionali dovrebbero essere posizionate attorno a tutte le operazioni che sono inseparabili.

Ad esempio, la chiamata è "cambia password". Ciò consiste in due operazioni

  1. Cambia la password
  2. Controlla la modifica.
  3. Invia per email al client che la password è stata modificata.

Quindi, in quanto sopra, se il controllo fallisce, anche la modifica della password dovrebbe fallire? In tal caso, la transazione dovrebbe essere intorno a 1 e 2 (quindi a livello di servizio). Se l'e-mail fallisce (probabilmente dovrebbe esserci una sorta di fail-safe su questo in modo che non fallisca), allora dovrebbe ripristinare la password di modifica e l'audit?

Questi sono il tipo di domande che devi porre quando decidi dove mettere il @Transactional.


41

La risposta corretta per le architetture Spring tradizionali è quella di collocare la semantica transazionale sulle classi di servizio, per i motivi che altri hanno già descritto.

Una tendenza emergente in primavera è verso la progettazione guidata dal dominio (DDD). Spring Roo esemplifica bene la tendenza. L'idea è di rendere i POJO dell'oggetto di dominio molto più ricchi di quanto non siano nelle tipiche architetture Spring (di solito sono anemici ), e in particolare di inserire la semantica di transazione e persistenza sugli oggetti di dominio stessi. Nei casi in cui tutto ciò che serve sono semplici operazioni CRUD, i controller Web operano direttamente sui POJO dell'oggetto di dominio (funzionano come entità in questo contesto) e non esiste un livello di servizio. Nei casi in cui è necessario un qualche tipo di coordinamento tra gli oggetti di dominio, è possibile avere un bean di servizio che lo gestisca con@Transactioncome da tradizione. È possibile impostare la propagazione della transazione sugli oggetti del dominio in REQUIREDmodo che gli oggetti del dominio utilizzino tutte le transazioni esistenti, come le transazioni avviate nel bean di servizio.

Tecnicamente questa tecnica utilizza AspectJ e <context:spring-configured />. Roo utilizza le definizioni tra i tipi di AspectJ per separare la semantica dell'entità (transazioni e persistenza) dall'oggetto oggetto del dominio (fondamentalmente campi e metodi aziendali).


38

Il caso normale sarebbe quello di annotare a livello di livello di servizio, ma ciò dipende davvero dalle vostre esigenze.

Le annotazioni su un livello di servizio comporteranno transazioni più lunghe rispetto all'annotazione a livello DAO. A seconda del livello di isolamento della transazione che può causare problemi, poiché le transazioni simultanee non vedranno i cambiamenti reciproci in es. LETTURA RIPETIBILE.

Le annotazioni sui DAO manterranno le transazioni il più brevi possibile, con l'inconveniente che la funzionalità che il tuo livello di servizio sta esponendo non sarà eseguita in un'unica transazione (rollbackable).

Non ha senso annotare entrambi i livelli se la modalità di propagazione è impostata sul valore predefinito.


31

Ho posto il @Transactionalsullo @Servicestrato e impostare rollbackForqualsiasi eccezione e readOnlydi ottimizzare ulteriormente la transazione.

Per impostazione predefinita @Transactionalcercherà solo RuntimeException(Eccezioni non selezionate), impostando il rollback su Exception.class(Eccezioni controllate) eseguirà il rollback per qualsiasi eccezione.

@Transactional(readOnly = false, rollbackFor = Exception.class)

Vedi Eccezioni controllate contro non selezionate .


17

Oppure ha senso annotare entrambi i "livelli"? - non ha senso annotare sia il livello di servizio che il livello dao - se si vuole assicurarsi che il metodo DAO sia sempre chiamato (propagato) da un livello di servizio con propagazione "obbligatoria" in DAO. Ciò fornirebbe alcune restrizioni per i metodi DAO che potrebbero essere richiamati dal livello (o dai controller) dell'interfaccia utente. Inoltre, in particolare quando si testano unità DAO layer, avere DAO annotato garantirà che sia testato per la funzionalità transazionale.


Non farebbe questo risultato in una transazione nidificata? E tutti i problemi sottili che ne derivano?
HDaveva il

11
No, non ci sono transazioni nidificate in JPA. Inserirli in entrambi sarebbe perfetto - se sei già in una transazione quando colpisci il DAO, quella transazione sarebbe continuata.
fool4jesus

4
La transazione nidificata può verificarsi se si utilizza propagation=Propagation.REQUIRES_NEW. In caso contrario, nella maggior parte dei casi, inclusa la propagazione = obbligatoria, il DAO parteciperà semplicemente alla transazione esistente avviata dal livello di servizio.
Dan Carter,


15

Per Transazione a livello di database

principalmente ho usato @Transactionalin DAO solo a livello di metodo, quindi la configurazione può essere specifica per un metodo / usando il default (richiesto)

  1. Il metodo DAO che ottiene il recupero dei dati (selezionare ...) - non è necessario che @Transactional ciò possa comportare un sovraccarico a causa dell'intercettore di transazione / e del proxy AOP che devono essere eseguiti anche.

  2. Otterranno i metodi di DAO che inseriscono / aggiornano @Transactional

ottimo blog sulla transizione

A livello di applicazione
: utilizzo transazionale per la logica aziendale Vorrei poter eseguire il rolback in caso di errore imprevisto

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}

6
+1 bellissimo articolo su TransactionalinJava
oak

11

Di solito, si dovrebbe mettere una transazione a livello di servizio.

Ma come affermato prima, l'atomicità di un'operazione è ciò che ci dice dove è necessaria un'annotazione. Pertanto, se si utilizzano framework come Hibernate, in cui un'unica operazione "salva / aggiorna / elimina / ... modifica" su un oggetto ha il potenziale per modificare più righe in più tabelle (a causa della cascata attraverso il grafico dell'oggetto), di Ovviamente ci dovrebbe essere anche una gestione delle transazioni su questo specifico metodo DAO.


10

@TransactionalLe annotazioni dovrebbero essere posizionate attorno a tutte le operazioni che sono inseparabili. L'uso della @Transactionalpropagazione delle transazioni viene gestito automaticamente. In questo caso, se viene chiamato un altro metodo dal metodo corrente, tale metodo avrà la possibilità di unire la transazione in corso.

Quindi facciamo un esempio:

Abbiamo 2 modelli ie Countrye City. Mappatura relazionale diCountry e il Citymodello sono come se si Countrypossano avere più città, quindi la mappatura è come,

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Qui Paese mappato a più città recuperandole Lazily. Quindi ecco che arriva il ruolo di@Transactinal quando recuperiamo l'oggetto Paese dal database quindi otterremo tutti i dati dell'oggetto Paese ma non otterremo Set di città perché stiamo recuperando città LAZILY.

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

Quando vogliamo accedere a Set di città dall'oggetto Paese, otterremo valori nulli in quel set perché l'oggetto del set creato solo questo set non viene inizializzato con i relativi dati per ottenere i valori del set che utilizziamo @Transactional , ad es.

//with @Transactional
@Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
   Object object = country.getCities().size();   
}

Quindi, in pratica, il @Transactionalservizio può effettuare più chiamate in un'unica transazione senza chiudere la connessione con l'end point.


2
molto istruttivo, grazie! Proprio quello che stavo cercando, una spiegazione di ciò @Transactionalche è veramente
CybeX il

6

È meglio averlo nel livello di servizio! Questo è chiaramente spiegato in uno degli articoli che mi sono imbattuto ieri! Ecco il link che puoi controllare!


5

inserisci qui la descrizione dell'immagine

Il @Transactionaldovrebbe essere usato sul livello di servizio in quanto contiene la logica di business. Il livello DAO di solito ha solo operazioni CRUD del database.

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

Documento di primavera: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html


5

Il livello di servizio è il posto migliore da aggiungere @Transactional annotazioni poiché la maggior parte della logica aziendale presente qui, contiene il comportamento del caso d'uso a livello di dettaglio.

Supponiamo di aggiungerlo a DAO e dal servizio chiamiamo 2 classi DAO, una fallita e l'altra riuscita, in questo caso se @Transactional non in servizio un DB commetterà e l'altro eseguirà il rollback.

Quindi la mia raccomandazione è usare saggiamente questa annotazione e usare solo a livello di servizio.

Progetto Github - java-algos


Eccezioni come ObjectOptimisticLockingFailureException si verificano solo dopo il completamento della transazione. Se hai thread separati per altre operazioni come il servizio di posta, questo design fallisce totalmente. Stiamo soffrendo proprio ora. L'unica soluzione rimasta sarebbe AOP.
Nabin Kumar Khatiwada,

4

Prima di tutto definiamo dove dobbiamo usare la transazione ?

Penso che la risposta corretta sia: quando dobbiamo assicurarci che la sequenza di azioni venga completata insieme come un'operazione atomica o che non vengano apportate modifiche anche se una delle azioni fallisce.

È pratica diffusa mettere la logica di business nei servizi. Quindi i metodi di servizio possono contenere diverse azioni che devono essere eseguite come una singola unità logica di lavoro. In tal caso, tale metodo deve essere contrassegnato come Transazionale . Naturalmente, non tutti i metodi richiedono tale limitazione, quindi non è necessario contrassegnare l'intero servizio come transazionale .

E ancora di più - non dimenticare di tenere conto del fatto che @Transactional , ovviamente, può ridurre le prestazioni del metodo. Per vedere l'intera immagine devi conoscere i livelli di isolamento delle transazioni. Sapere che potrebbe aiutarti a evitare di usare @Transactional dove non è necessariamente necessario.


3

È meglio mantenere @Transactional in un livello intermedio separato tra DAO e Service Layer. Poiché il rollback è molto importante, è possibile posizionare tutta la manipolazione del DB nel livello intermedio e scrivere la logica aziendale nel livello di servizio. Il livello intermedio interagirà con i livelli DAO.

Questo ti aiuterà in molte situazioni come ObjectOptimisticLockingFailureException : questa eccezione si verifica solo al termine della transazione. Pertanto, non è possibile catturarlo nel livello intermedio ma ora è possibile catturarlo nel livello di servizio. Ciò non sarebbe possibile se si dispone di @Transactional nel livello di servizio. Sebbene tu possa catturare Controller ma Controller dovrebbe essere il più pulito possibile.

Se stai inviando posta o sms in thread separato dopo aver completato tutte le opzioni di salvataggio, eliminazione e aggiornamento, puoi farlo in servizio dopo che la Transazione è stata completata nel tuo livello intermedio. Ancora una volta, se si menziona @Transactional nel livello di servizio, la posta andrà anche se la transazione non riesce.

Quindi avere un livello centrale @Transaction ti aiuterà a rendere il tuo codice migliore e facile da gestire. Altrimenti, se si utilizza nel livello DAO, potrebbe non essere possibile eseguire il rollback di tutte le operazioni. Se si utilizza nel livello di servizio, potrebbe essere necessario utilizzare AOP (Aspect Oriented Programming) in alcuni casi.


2

Idealmente, il livello di servizio (Manager) rappresenta la tua logica aziendale e quindi dovrebbe essere annotato con. @TransactionalIl livello di servizio può chiamare diversi DAO per eseguire operazioni DB. Supponiamo che si verifichi un numero N di operazioni DAO in un metodo di servizio. Se la tua prima operazione DAO non è andata a buon fine, è possibile che altri siano ancora passati e lo stato del database non sarà coerente. Il livello del servizio di annotazione può salvarti da tali situazioni.



1

@Transactionalutilizza nel livello di servizio che viene chiamato utilizzando il livello controller ( @Controller) e la chiamata del livello di servizio al livello DAO ( @Repository), ad esempio operazioni correlate al database.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.