Risposte:
Bella domanda, anche se non banale a cui rispondere.
Definisce la relazione tra le transazioni. Opzioni comuni:
Required
: Il codice verrà sempre eseguito in una transazione. Crea una nuova transazione o la riutilizza se disponibile.Requires_new
: Il codice verrà sempre eseguito in una nuova transazione. Sospende la transazione corrente se ne esiste una.Definisce il contratto dati tra le transazioni.
Read Uncommitted
: Consente letture sporche.Read Committed
: Non consente letture sporche.Repeatable Read
: Se una riga viene letta due volte nella stessa transazione, il risultato sarà sempre lo stesso.Serializable
: Esegue tutte le transazioni in sequenza.I diversi livelli hanno caratteristiche prestazionali diverse in un'applicazione multi-thread. Penso che se capisci il dirty reads
concetto sarai in grado di selezionare una buona opzione.
Esempio di quando può verificarsi una lettura sporca:
thread 1 thread 2
| |
write(x) |
| |
| read(x)
| |
rollback |
v v
value (x) is now dirty (incorrect)
Quindi potrebbe essere un default predefinito (se possibile) Read Committed
, che consente solo di leggere i valori che sono già stati impegnati da altre transazioni in esecuzione, in combinazione con un livello di propagazione di Required
. Quindi puoi lavorare da lì se la tua applicazione ha altre esigenze.
Un esempio pratico di dove verrà sempre creata una nuova transazione quando si entra nella provideService
routine e completata quando si esce:
public class FooService {
private Repository repo1;
private Repository repo2;
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void provideService() {
repo1.retrieveFoo();
repo2.retrieveFoo();
}
}
Se avessimo invece utilizzato Required
, la transazione rimarrebbe aperta se la transazione fosse già aperta quando si entrava nella routine. Si noti inoltre che il risultato di a rollback
potrebbe essere diverso in quanto diverse esecuzioni potrebbero prendere parte alla stessa transazione.
Possiamo facilmente verificare il comportamento con un test e vedere in che modo i risultati differiscono con i livelli di propagazione:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:/fooService.xml")
public class FooServiceTests {
private @Autowired TransactionManager transactionManager;
private @Autowired FooService fooService;
@Test
public void testProvideService() {
TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
fooService.provideService();
transactionManager.rollback(status);
// assert repository values are unchanged ...
}
Con un livello di propagazione di
Requires new
: ci aspettiamo che NON siafooService.provideService()
stato ripristinato poiché ha creato una propria sub-transazione.
Required
: ci aspetteremmo che tutto sia stato ripristinato e il negozio di supporto sia rimasto invariato.
sessionFactory.getCurrentTransaction()
stato aggiunto, non è più necessario eseguire HibernateTemplate
per gestire le transazioni. L'ho rimosso :)
PROPAGATION_REQUIRED = 0 ; Se DataSourceTransactionObject T1 è già stato avviato per il metodo M1. Se per un altro metodo M2 è richiesto un oggetto Transaction, non viene creato alcun nuovo oggetto Transaction. Lo stesso oggetto T1 viene utilizzato per M2
PROPAGATION_MANDATORY = 2 ; il metodo deve essere eseguito all'interno di una transazione. Se non è in corso alcuna transazione esistente, verrà generata un'eccezione
PROPAGATION_REQUIRES_NEW = 3 ; Se DataSourceTransactionObject T1 è già stato avviato per il Metodo M1 ed è in corso (eseguendo il metodo M1). Se un altro metodo M2 inizia l'esecuzione, T1 viene sospeso per la durata del metodo M2 con il nuovo DataSourceTransactionObject T2 per M2.M2 eseguito nel proprio contesto di transazione
PROPAGATION_NOT_SUPPORTED = 4 ; Se DataSourceTransactionObject T1 è già avviato per il metodo M1. Se un altro metodo M2 viene eseguito contemporaneamente. Quindi M2 non dovrebbe essere eseguito nel contesto della transazione. T1 è sospeso fino al termine di M2.
PROPAGATION_NEVER = 5 ; Nessuno dei metodi viene eseguito nel contesto della transazione.
Un livello di isolamento: si tratta di quanto una transazione può essere influenzata dalle attività di altre transazioni simultanee e supporta la coerenza lasciando i dati su molte tabelle in uno stato coerente. Implica il blocco di righe e / o tabelle in un database.
Il problema con più transazioni
scenario 1 Se la transazione T1 legge i dati dalla tabella A1 che è stata scritta da un'altra transazione concorrente T2. Se sul modo T2 è il rollback, i dati ottenuti da T1 non sono validi. Ad esempio a = 2 sono dati originali. Se T1 legge a = 1 scritto da T2. Se T2 rollback quindi a = 1 verrà eseguito il rollback a a = 2 in DB. Ma, ora, T1 ha un = 1 ma nella tabella DB viene modificato in a = 2.
Scenario2 . Se la transazione T1 legge i dati dalla tabella A1. Se un'altra transazione simultanea (T2) aggiorna i dati nella tabella A1. Quindi i dati che T1 ha letto sono diversi dalla tabella A1. Perché T2 ha aggiornato i dati nella tabella A1.Eg se T1 leggi a = 1 e T2 aggiornato a = 2. Quindi a! = b.
Scenario 3 Se la transazione T1 legge i dati dalla tabella A1 con un determinato numero di righe. Se un'altra transazione simultanea (T2) inserisce più righe nella tabella A1, il numero di righe lette da T1 è diverso dalle righe nella tabella A1
Lo scenario 1 si chiama Letture sporche.
Lo scenario 2 si chiama Letture non ripetibili.
Lo scenario 3 si chiama Phantom reads.
Pertanto, il livello di isolamento è l'estensione alla quale è possibile impedire lo scenario 1, lo scenario 2, lo scenario 3 . È possibile ottenere il livello di isolamento completo implementando il blocco. Ciò impedisce che si verifichino letture e scritture simultanee sugli stessi dati, ma influisce sulle prestazioni. Il livello di isolamento dipende dall'applicazione all'applicazione della quantità di isolamento richiesta.
ISOLATION_READ_UNCOMMITTED : Permette di leggere le modifiche che non sono ancora state impegnate. Soffre di Scenario 1, Scenario 2, Scenario 3
ISOLATION_READ_COMMITTED : consente letture da transazioni simultanee che sono state impegnate. Potrebbe risentire dello scenario 2 e dello scenario 3. Perché altre transazioni potrebbero aggiornare i dati.
ISOLATION_REPEATABLE_READ : letture multiple dello stesso campo produrranno gli stessi risultati fino a quando non viene modificato da solo. Potrebbe risentire dello scenario 3.Perché altre transazioni potrebbero inserire i dati
ISOLATION_SERIALIZABLE : lo scenario 1, lo scenario 2, lo scenario 3 non si verifica mai, è un isolamento completo, comporta un blocco completo e influisce sulle prestazioni a causa del blocco.
Puoi provare usando
public class TransactionBehaviour {
// set is either using xml Or annotation
DataSourceTransactionManager manager=new DataSourceTransactionManager();
SimpleTransactionStatus status=new SimpleTransactionStatus();
;
public void beginTransaction()
{
DefaultTransactionDefinition Def = new DefaultTransactionDefinition();
// overwrite default PROPAGATION_REQUIRED and ISOLATION_DEFAULT
// set is either using xml Or annotation
manager.setPropagationBehavior(XX);
manager.setIsolationLevelName(XX);
status = manager.getTransaction(Def);
}
public void commitTransaction()
{
if(status.isCompleted()){
manager.commit(status);
}
}
public void rollbackTransaction()
{
if(!status.isCompleted()){
manager.rollback(status);
}
}
Main method{
beginTransaction()
M1();
If error(){
rollbackTransaction()
}
commitTransaction();
}
}
È possibile eseguire il debug e vedere il risultato con valori diversi per l'isolamento e la propagazione.
Una spiegazione sufficiente su ciascun parametro è data da altre risposte; Comunque tu abbia chiesto un esempio del mondo reale, ecco quello che chiarisce lo scopo delle diverse opzioni di propagazione :
Supponi di essere responsabile dell'implementazione di un servizio di registrazione in cui viene inviata all'utente un'e-mail di conferma. Si arriva con due oggetti di servizio, uno per l'iscrizione l'utente e uno per l'invio di e-mail, che quest'ultimo è chiamato all'interno del primo. Ad esempio qualcosa del genere:/* Sign Up service */
@Service
@Transactional(Propagation=REQUIRED)
class SignUpService{
...
void SignUp(User user){
...
emailService.sendMail(User);
}
}
/* E-Mail Service */
@Service
@Transactional(Propagation=REQUIRES_NEW)
class EmailService{
...
void sendMail(User user){
try{
... // Trying to send the e-mail
}catch( Exception)
}
}
Potresti aver notato che il secondo servizio è di tipo propagazione REQUIRES_NEW e inoltre è probabile che generi un'eccezione (server SMTP inattivo, e-mail non valida o altri motivi). Probabilmente non vuoi che l'intero processo venga ripristinato, come rimuovere le informazioni dell'utente dal database o altre cose; pertanto si chiama il secondo servizio in una transazione separata.
Tornando al nostro esempio, questa volta ti preoccupi della sicurezza del database, quindi definisci le tue classi DAO in questo modo:/* User DAO */
@Transactional(Propagation=MANDATORY)
class UserDAO{
// some CRUD methods
}
Ciò significa che ogni volta che viene creato un oggetto DAO, e quindi un potenziale accesso a db, dobbiamo rassicurare che la chiamata è stata effettuata dall'interno di uno dei nostri servizi, il che implica che dovrebbe esistere una transazione live; in caso contrario si verifica un'eccezione. Pertanto la propagazione è di tipo OBBLIGATORIA .
Il livello di isolamento definisce in che modo le modifiche apportate ad alcuni repository di dati da una transazione influiscono su altre transazioni simultanee e anche come e quando tali dati modificati diventano disponibili per altre transazioni. Quando definiamo una transazione utilizzando il framework Spring, siamo anche in grado di configurare in quale livello di isolamento verrà eseguita la stessa transazione.
@Transactional(isolation=Isolation.READ_COMMITTED)
public void someTransactionalMethod(Object obj) {
}
Il livello di isolamento READ_UNCOMMITTED indica che una transazione può leggere dati che non sono ancora stati trasferiti da altre transazioni.
Il livello di isolamento READ_COMMITTED indica che una transazione non è in grado di leggere dati non ancora impegnati da altre transazioni.
REPEATABLE_READ livello di isolamento indica che se una transazione legge più volte un record dal database, il risultato di tutte quelle operazioni di lettura deve essere sempre lo stesso.
Il livello di isolamento SERIALIZZABILE è il più restrittivo di tutti i livelli di isolamento. Le transazioni vengono eseguite con il blocco a tutti i livelli (lettura, intervallo e blocco della scrittura) in modo che appaiano come se fossero eseguite in modo serializzato.
La propagazione è la capacità di decidere come incapsulare i metodi di business in transazioni sia logiche che fisiche.
Il comportamento Spring REQUIRED indica che verrà utilizzata la stessa transazione se è già presente una transazione nel contesto di esecuzione del metodo bean corrente.
REQUIRES_NEW comporta che una nuova transazione fisica verrà sempre creata dal contenitore.
Il comportamento NESTED effettua transazioni Spring nidificate per utilizzare la stessa transazione fisica, ma imposta i punti di salvataggio tra le chiamate nidificate in modo che anche le transazioni interne possano eseguire il rollback indipendentemente dalle transazioni esterne.
Il comportamento OBBLIGATORIO afferma che una transazione aperta esistente deve già esistere. In caso contrario, verrà generata un'eccezione dal contenitore.
Il comportamento MAI indica che una transazione aperta esistente non deve già esistere. Se esiste una transazione, il contenitore genererà un'eccezione.
Il comportamento NOT_SUPPORTED verrà eseguito al di fuori dell'ambito di qualsiasi transazione. Se esiste già una transazione aperta, questa verrà messa in pausa.
Il comportamento SUPPORTI verrà eseguito nell'ambito di una transazione se esiste già una transazione aperta. Se non è presente una transazione già aperta, il metodo verrà eseguito comunque ma in modo non transazionale.
Una transazione rappresenta un'unità di lavoro con un database.
In primavera TransactionDefinition
interfaccia che definisce le proprietà delle transazioni conformi a Spring. @Transactional
l'annotazione descrive gli attributi di transazione su un metodo o una classe.
@Autowired
private TestDAO testDAO;
@Transactional(propagation=TransactionDefinition.PROPAGATION_REQUIRED,isolation=TransactionDefinition.ISOLATION_READ_UNCOMMITTED)
public void someTransactionalMethod(User user) {
// Interact with testDAO
}
Propagazione (riproduzione): è usi per relazione tra transazioni. (analogo alla comunicazione inter-thread Java)
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| value | Propagation | Description |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
| -1 | TIMEOUT_DEFAULT | Use the default timeout of the underlying transaction system, or none if timeouts are not supported. |
| 0 | PROPAGATION_REQUIRED | Support a current transaction; create a new one if none exists. |
| 1 | PROPAGATION_SUPPORTS | Support a current transaction; execute non-transactionally if none exists. |
| 2 | PROPAGATION_MANDATORY | Support a current transaction; throw an exception if no current transaction exists. |
| 3 | PROPAGATION_REQUIRES_NEW | Create a new transaction, suspending the current transaction if one exists. |
| 4 | PROPAGATION_NOT_SUPPORTED | Do not support a current transaction; rather always execute non-transactionally. |
| 5 | PROPAGATION_NEVER | Do not support a current transaction; throw an exception if a current transaction exists. |
| 6 | PROPAGATION_NESTED | Execute within a nested transaction if a current transaction exists. |
+-------+---------------------------+------------------------------------------------------------------------------------------------------+
Isolamento: l' isolamento è una delle proprietà ACID (Atomicità, Coerenza, Isolamento, Durabilità) delle transazioni del database. L'isolamento determina come l'integrità della transazione è visibile ad altri utenti e sistemi. Utilizza per il blocco delle risorse, ad esempio il controllo della concorrenza, assicurarsi che solo una transazione possa accedere alla risorsa in un determinato punto.
Percezione di blocco: il livello di isolamento determina la durata della conservazione dei blocchi.
+---------------------------+-------------------+-------------+-------------+------------------------+
| Isolation Level Mode | Read | Insert | Update | Lock Scope |
+---------------------------+-------------------+-------------+-------------+------------------------+
| READ_UNCOMMITTED | uncommitted data | Allowed | Allowed | No Lock |
| READ_COMMITTED (Default) | committed data | Allowed | Allowed | Lock on Committed data |
| REPEATABLE_READ | committed data | Allowed | Not Allowed | Lock on block of table |
| SERIALIZABLE | committed data | Not Allowed | Not Allowed | Lock on full table |
+---------------------------+-------------------+-------------+-------------+------------------------+
Percezione della lettura: si verificano i seguenti 3 tipi di problemi principali:
UPDATES
da un altro tx.INSERTS
e / o DELETES
da un altro txLivelli di isolamento con diversi tipi di letture:
+---------------------------+----------------+----------------------+----------------+
| Isolation Level Mode | Dirty reads | Non-repeatable reads | Phantoms reads |
+---------------------------+----------------+----------------------+----------------+
| READ_UNCOMMITTED | allows | allows | allows |
| READ_COMMITTED (Default) | prevents | allows | allows |
| REPEATABLE_READ | prevents | prevents | allows |
| SERIALIZABLE | prevents | prevents | prevents |
+---------------------------+----------------+----------------------+----------------+
Non vuoi quasi mai usarlo Read Uncommited
poiché non è veramente ACID
conforme. Read Commmited
è un buon punto di partenza predefinito. Repeatable Read
è probabilmente necessario solo in scenari di reporting, rollup o aggregazione. Nota che molti DB, inclusi postgres, in realtà non supportano la lettura ripetibile, devi Serializable
invece usarli . Serializable
è utile per le cose che sai che devono accadere completamente indipendentemente da qualsiasi altra cosa; pensalo come synchronized
in Java. Serializable va di pari passo con la REQUIRES_NEW
propagazione.
Uso REQUIRES
per tutte le funzioni che eseguono query UPDATE o DELETE, nonché le funzioni di livello "servizio". Per le funzioni di livello DAO che eseguono solo SELECT, io uso SUPPORTS
quale parteciperà a un TX se ne è già stato avviato uno (cioè viene chiamato da una funzione di servizio).
Transaction Isolation e Transaction Propagation sebbene correlati ma sono chiaramente due concetti molto diversi. In entrambi i casi le impostazioni predefinite vengono personalizzate nel componente di confine del client utilizzando la gestione delle transazioni dichiarativa o la gestione delle transazioni programmatiche . I dettagli di ciascun livello di isolamento e attributi di propagazione sono disponibili nei collegamenti di riferimento di seguito.
Per due o più transazioni / connessioni in esecuzione a un database, come e quando le modifiche apportate dalle query in una transazione hanno un impatto / sono visibili alle query in una transazione diversa. Si riferiva anche al tipo di blocco dei record del database che verrà utilizzato per isolare le modifiche in questa transazione da altre transazioni e viceversa. Questo è in genere implementato dal database / risorsa che partecipa alla transazione.
.
Propagazione delle transazioni
In un'applicazione enterprise per una determinata richiesta / elaborazione ci sono molti componenti che sono coinvolti per completare il lavoro. Alcuni di questi componenti segnano i limiti (inizio / fine) di una transazione che verrà utilizzata nel rispettivo componente e nei suoi componenti secondari. Per questo limite transazionale di componenti, Propagazione transazione specifica se il rispettivo componente parteciperà o meno alla transazione e cosa succede se il componente chiamante ha già o meno una transazione già creata / avviata. È uguale agli attributi di transazione Java EE. Questo è in genere implementato dal gestore transazioni / connessioni client.
Riferimento:
Ho corso outerMethod
, method_1
emethod_2
con la modalità di propagazione differenti.
Di seguito è riportato l'output per diverse modalità di propagazione.
Metodo esterno
@Transactional
@Override
public void outerMethod() {
customerProfileDAO.method_1();
iWorkflowDetailDao.method_2();
}
Method_1
@Transactional(propagation=Propagation.MANDATORY)
public void method_1() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "XXX");
session.save(entity);
System.out.println("Method - 1 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Method_2
@Transactional()
@Override
public void method_2() {
Session session = null;
try {
session = getSession();
Temp entity = new Temp(0l, "CCC");
session.save(entity);
int i = 1/0;
System.out.println("Method - 2 Id "+entity.getId());
} finally {
if (session != null && session.isOpen()) {
}
}
}
Possiamo aggiungere per questo:
@Transactional(readOnly = true)
public class Banking_CustomerService implements CustomerService {
public Customer getDetail(String customername) {
// do something
}
// these settings have precedence for this method
@Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateCustomer(Customer customer) {
// do something
}
}
Puoi usare così:
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<ModificaOperativitaRapporto> eventMessage) {
//here some transaction related code
}
Puoi usare questa cosa anche:
public interface TransactionStatus extends SavepointManager {
boolean isNewTransaction();
boolean hasSavepoint();
void setRollbackOnly();
boolean isRollbackOnly();
void flush();
boolean isCompleted();
}