@Transactional méthode appelant une autre méthode sans @Transactional anotation?

89

J'ai vu une méthode dans une classe Service qui était marquée comme @Transactional, mais elle appelait également d'autres méthodes de cette même classe qui n'étaient pas marquées comme @Transactional.

Cela signifie-t-il que l'appel à des méthodes distinctes amène l'application à ouvrir des connexions séparées à la base de données ou à suspendre la transaction parente, etc.?

Quel est le comportement par défaut d'une méthode sans annotations qui est appelée par une autre méthode avec @Transactionalannotation?

aller
la source

Réponses:

117

Lorsque vous appelez une méthode sans @Transactionaldans un bloc de transaction, la transaction parente continuera avec la nouvelle méthode. Il utilisera la même connexion à partir de la méthode parente (avec @Transactional) et toute exception provoquée dans la méthode appelée (sans @Transactionalentraîner la restauration de la transaction comme configuré dans la définition de transaction.

Si vous appelez une méthode avec une @Transactionalannotation à partir d'une méthode @Transactionaldans la même instance, le comportement transactionnel des méthodes appelées n'aura aucun impact sur la transaction. Mais si vous appelez une méthode avec une définition de transaction à partir d'une autre méthode avec une définition de transaction, et qu'ils sont dans des instances différentes, le code de la méthode appelée suivra les définitions de transaction données dans la méthode appelée.

Vous pouvez trouver plus de détails dans la section Gestion des transactions déclaratives de la documentation des transactions de printemps .

Le modèle de transaction déclarative Spring utilise le proxy AOP. le proxy AOP est donc responsable de la création des transactions. Le proxy AOP ne sera actif que si les méthodes avec dans l'instance sont appelées depuis l'extérieur de l'instance.

Arun P Johny
la source
est-ce le comportement par défaut du ressort?
rendez-vous le
Oui. C'est le comportement par défaut.
Arun P Johny
2
@Tomasz Oui. Mais il convient également de mentionner que la modification de la propagation des transactions sur une méthode appelée à partir d'une autre méthode @Transactional n'aura aucun effet.
Fil
1
@Tomasz, c'est ce que je voulais dire en disant will follow the transaction definitions given in the called method. Mais si l'appel provient de la même instance d'objet, il n'aura aucun effet puisque l'appel ne se propage pas à travers les proxys aop qui sont responsables de la maintenance de la transaction.
Arun P Johny
5
@Filip, Ce n'est pas tout à fait correct, Si vous appelez une méthode avec une @Transactionaldéfinition à partir d'un objet / instance différent, même si la méthode appelante a des @Transactionalattributs différents , la méthode appelée suivra sa propre définition de transaction.
Arun P Johny
23
  • Cela signifie-t-il que l'appel à des méthodes distinctes amène l'application à ouvrir des connexions séparées à la base de données ou à suspendre la transaction parent, etc.?

Cela dépend d'un niveau de propagation . Voici toutes les valeurs de niveau possibles .

Par exemple, dans le cas où un niveau de propagation est imbriqué, une transaction en cours sera "suspendue" et une nouvelle transaction sera créée ( note: la création réelle d'une transaction imbriquée ne fonctionnera que sur des gestionnaires de transactions spécifiques )

  • Quel est le comportement par défaut d'une méthode sans annotations appelée par une autre méthode avec l'annotation @Transactional?

Le niveau de propagation par défaut (ce que vous appelez "comportement") est REQUIS . Dans le cas où une méthode "interne" est appelée avec une @Transactionalannotation dessus (ou est traitée de manière déclarative via XML), elle s'exécutera dans la même transaction , par exemple "rien de nouveau" n'est créé.

tolitius
la source
Qu'en est-il des sous-appels de NOT_SUPPORTED qui n'ont aucune annotation? Hérite-t-il de NOT_Supported ou ont-ils ouvert une nouvelle transaction car REQURED est la valeur par défaut? Par exemple: f1.call () {f2 ()} avec l'annotation NOT_SUPPORTED pour f1 et non pour f2.
Dave
8

@Transactional marque la limite de la transaction (début / fin) mais la transaction elle-même est liée au thread. Une fois qu'une transaction démarre, elle se propage à travers les appels de méthode jusqu'à ce que la méthode d'origine soit renvoyée et que la transaction soit validée / annulée.

Si une autre méthode est appelée avec une annotation @Transactional, la propagation dépend de l'attribut de propagation de cette annotation.

sourcedelica
la source
Les 3 réponses sont en conflit les unes avec les autres dans une certaine mesure, je ne sais pas laquelle est la plus précise.
Eric Wang
1
@EricWang Je voulais juste partager que j'ai testé ce scénario aujourd'hui et que la réponse d' Arun P Johny (avec des commentaires) est la plus précise pour ce scénario d' invocations internes .
Vinay Vissh le
3

La méthode interne affectera la méthode externe si la méthode interne n'est pas annotée avec @Transactional.

Si la méthode interne est également annotée avec @Transactional avec REQUIRES_NEW, ce qui suit se produira.

...
@Autowired
private TestDAO testDAO;

@Autowired
private SomeBean someBean;

@Override
@Transactional(propagation=Propagation.REQUIRED)
public void outerMethod(User user) {
  testDAO.insertUser(user);
  try{
    someBean.innerMethod();
  } catch(RuntimeException e){
    // handle exception
  }
}


@Override
@Transactional(propagation=Propagation.REQUIRES_NEW)
public void innerMethod() {
  throw new RuntimeException("Rollback this transaction!");
}

La méthode interne est annotée REQUIRES_NEWet lève une exception RuntimeException afin qu'elle définisse sa transaction sur rollback mais N'AFFECTERA PAS la transaction externe. La transaction externe est PAUSE lorsque la transaction interne démarre, puis REPREND UNE FOIS la transaction interne terminée. Ils s'exécutent indépendamment l'un de l'autre, de sorte que la transaction externe PEUT s'engager avec succès.

saran3h
la source
1
Pour clarifier pour les débutants, je suis à peu près sûr que innerMethod () doit être sur un bean différent (aka objet java géré par Spring) que externalMethod (). S'ils sont tous les deux sur le même bean, je ne pense pas que la méthode innerMethod utilisera réellement le comportement transactionnel déclaré dans son annotation. Il utilisera plutôt ce qui est déclaré dans la déclaration externalMethod (). C'est à cause de la façon dont Spring gère AOP, qui est utilisé pour ses annotations @Transactional ( docs.spring.io/spring/docs/3.0.x/spring-framework-reference/… )
johnsimer