@Transactional
l'annotazione in primavera funziona avvolgendo il tuo oggetto in un proxy che a sua volta avvolge i metodi annotati con @Transactional
in una transazione. A causa di tale annotazione non funzionerà sui metodi privati (come nel tuo esempio) perché i metodi privati non possono essere ereditati => non possono essere racchiusi (questo non è vero se si utilizzano transazioni dichiarative con aspectj, quindi gli avvertimenti relativi al proxy di seguito non si applicano).
Ecco una spiegazione di base di come @Transactional
la magia della primavera funziona.
Hai scritto:
class A {
@Transactional
public void method() {
}
}
Ma questo è ciò che ottieni effettivamente quando inietti un fagiolo:
class ProxiedA extends A {
private final A a;
public ProxiedA(A a) {
this.a = a;
}
@Override
public void method() {
try {
// open transaction ...
a.method();
// commit transaction
} catch (RuntimeException e) {
// rollback transaction
} catch (Exception e) {
// commit transaction
}
}
}
Questo ha dei limiti. Non funzionano con @PostConstruct
metodi perché vengono chiamati prima che l'oggetto venga inviato tramite proxy. E anche se hai configurato tutto correttamente, le transazioni vengono ripristinate solo se deselezionate eccezioni per impostazione predefinita. Usa @Transactional(rollbackFor={CustomCheckedException.class})
se è necessario il rollback su alcune eccezioni verificate.
Un altro avvertimento che si incontra di frequente che conosco:
@Transactional
il metodo funzionerà solo se lo chiami "dall'esterno", nell'esempio seguente b()
non sarà incluso nella transazione:
class X {
public void a() {
b();
}
@Transactional
public void b() {
}
}
È anche perché @Transactional
funziona tramite proxy del tuo oggetto. Nell'esempio sopra a()
chiamerà X.b()
non un metodo "proxy di primavera" avanzato b()
quindi non ci sarà alcuna transazione. Come soluzione alternativa devi chiamare b()
da un altro fagiolo.
Quando hai riscontrato uno di questi avvertimenti e non puoi utilizzare una soluzione alternativa suggerita (rendi il metodo non privato o chiama b()
da un altro bean) puoi usare TransactionTemplate
invece di operazioni dichiarative:
public class A {
@Autowired
TransactionTemplate transactionTemplate;
public void method() {
transactionTemplate.execute(status -> {
A();
B();
return null;
});
}
...
}
Aggiorna
Rispondere alla domanda aggiornata OP utilizzando le informazioni sopra.
Quale metodo dovrebbe essere annotato con @Transactional:changes()? databaseChanges()?
@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
someLogicBefore();
databaseChanges();
someLogicAfter();
}
Assicurati changes()
viene chiamato "dall'esterno" di un bean, non dalla classe stessa e dopo che il contesto è stato istanziato (ad es. questo non è afterPropertiesSet()
o @PostConstruct
metodo annotato). Comprendere che la transazione di rollback primaverile solo per le eccezioni non selezionate per impostazione predefinita (cerca di essere più specifico nell'elenco delle eccezioni rollbackFor verificate).