Oracle
 sql >> Database >  >> RDS >> Oracle

Tira indietro A se B va storto. stivale a molla, jdbctemplate

@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).