Dovresti inserire il @Transactional
nelle DAO
classi 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"?
Dovresti inserire il @Transactional
nelle DAO
classi 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:
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.
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'è @Transactional
un'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.
@Transactional
la classe di implementazione del servizio e dovrei mettere @Transactional(propagation = MANDATORY)
l'implementazione della classe DAO (repository)?
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
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
.
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@Transaction
come da tradizione. È possibile impostare la propagazione della transazione sugli oggetti del dominio in REQUIRED
modo 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).
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.
Ho posto il @Transactional
sullo @Service
strato e impostare rollbackFor
qualsiasi eccezione e readOnly
di ottimizzare ulteriormente la transazione.
Per impostazione predefinita @Transactional
cercherà 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)
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.
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.
Inoltre, Spring consiglia di utilizzare l'annotazione solo su classi concrete e non su interfacce.
http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html
Per Transazione a livello di database
principalmente ho usato @Transactional
in DAO solo a livello di metodo, quindi la configurazione può essere specifica per un metodo / usando il default (richiesto)
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.
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(..);
}
}
Transactional
inJava
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.
@Transactional
Le annotazioni dovrebbero essere posizionate attorno a tutte le operazioni che sono inseparabili. L'uso della @Transactional
propagazione 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 Country
e City
. Mappatura relazionale diCountry
e il City
modello sono come se si Country
possano 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 @Transactional
servizio può effettuare più chiamate in un'unica transazione senza chiudere la connessione con l'end point.
@Transactional
che è veramente
Il @Transactional
dovrebbe 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
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.
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.
È 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.
Idealmente, il livello di servizio (Manager) rappresenta la tua logica aziendale e quindi dovrebbe essere annotato con. @Transactional
Il 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.
Preferisco usare @Transactional
a livello di servizi a livello di metodo.