Oracle
 sql >> Base de données >  >> RDS >> Oracle

Annulez A si B tourne mal. botte de printemps, jdbctemplate

@Transactional l'annotation au printemps fonctionne en enveloppant votre objet dans un proxy qui à son tour enveloppe les méthodes annotées avec @Transactional dans une opération. En raison de cette annotation ne fonctionnera pas sur les méthodes privées (comme dans votre exemple) car les méthodes privées ne peuvent pas être héritées => ils ne peuvent pas être enveloppés (ce n'est pas vrai si vous utilisez des transactions déclaratives avec aspectj, alors les mises en garde liées au proxy ci-dessous ne s'appliquent pas).

Voici une explication de base de la façon dont @Transactional la magie du printemps opère.

Vous avez écrit :

class A {
    @Transactional
    public void method() {
    }
}

Mais voici ce que vous obtenez réellement lorsque vous injectez un haricot :

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
       }
   }
} 

Cela a des limites. Ils ne fonctionnent pas avec @PostConstruct méthodes car elles sont appelées avant que l'objet ne soit proxy. Et même si vous avez tout configuré correctement, les transactions ne sont annulées que sur non cochées exception par défaut. Utilisez @Transactional(rollbackFor={CustomCheckedException.class}) si vous avez besoin d'une restauration sur une exception vérifiée.

Une autre mise en garde fréquemment rencontrée que je connais :

@Transactional la méthode ne fonctionnera que si vous l'appelez "de l'extérieur", dans l'exemple suivant b() ne sera pas enveloppé dans la transaction :

class X {
   public void a() {
      b();
   }

   @Transactional
   public void b() {
   }
}

C'est aussi parce que @Transactional fonctionne en proxyant votre objet. Dans l'exemple ci-dessus a() appellera X.b() n'est pas une méthode "printemps proxy" améliorée b() il n'y aura donc pas de transaction. Comme solution de contournement, vous devez appeler b() d'un autre bean.

Lorsque vous rencontrez l'une de ces mises en garde et que vous ne pouvez pas utiliser une solution de contournement suggérée (rendez la méthode non privée ou appelez b() d'un autre bean), vous pouvez utiliser TransactionTemplate au lieu de transactions déclaratives :

public class A {
    @Autowired
    TransactionTemplate transactionTemplate;

    public void method() {
        transactionTemplate.execute(status -> {
            A();
            B();
            return null;
        });
    }

...
} 

Mettre à jour

Répondre à la question mise à jour de l'OP en utilisant les informations ci-dessus.

Quelle méthode doit être annotée avec @Transactional:changes() ? base de donnéesChanges() ?

@Transactional(rollbackFor={Exception.class})
public void changes() throws Exception {
    someLogicBefore();
    databaseChanges();
    someLogicAfter();
}

Assurez-vous que changes() est appelé "de l'extérieur" d'un bean, pas de la classe elle-même et après l'instanciation du contexte (par exemple, ce n'est pas afterPropertiesSet() ou @PostConstruct méthode annotée). Comprenez que le printemps annule la transaction uniquement pour les exceptions non vérifiées par défaut (essayez d'être plus précis dans la liste des exceptions vérifiées rollbackFor).