web-dev-qa-db-fra.com

Où dois-je placer l'annotation @Transactional: dans une définition d'interface ou dans une classe d'implémentation?

La question du titre dans le code:

@Transactional (readonly = true)
public interface FooService {
   void doSmth ();
}


public class FooServiceImpl implements FooService {
   ...
}

contre

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}
79
Roman

De http://static.springsource.org/spring/docs/2.0.x/reference/transaction.html

La recommandation de l'équipe Spring est que vous annotez uniquement les classes concrètes avec le @Transactional annotation, par opposition aux annotations d'interfaces. Vous pouvez certainement placer le @Transactional annotation sur une interface (ou une méthode d'interface), mais cela ne fonctionnera comme prévu que si vous utilisez des proxys basés sur une interface. Le fait que les annotations soient non héritées signifie que si vous utilisez des proxy basés sur une classe, les paramètres de transaction ne seront pas reconnus par l'infrastructure de proxy basée sur une classe et l'objet ne sera pas encapsulé dans un proxy transactionnel (qui serait décidément mauvais). Veuillez donc suivre les conseils de l'équipe Spring et annoter uniquement les classes concrètes (et les méthodes des classes concrètes) avec le @Transactional annotation.

Remarque: Étant donné que ce mécanisme est basé sur des mandataires, seuls les appels de méthode "externes" entrant via le proxy seront interceptés. Cela signifie que "l'auto-invocation", c'est-à-dire une méthode dans l'objet cible l'appel d'une autre méthode de l'objet cible ne conduira pas à une transaction réelle au moment de l'exécution même si la méthode invoquée est marquée avec @Transactional!

(Italique ajouté à la première phrase, autre accentuation par rapport à l'original.)

108
Romain Hippeau

Vous pouvez les mettre sur l'interface mais soyez averti que les transactions peuvent ne pas se produire dans certains cas. Voir la deuxième astuce dans Section 10.5.6 des documents Spring:

Spring vous recommande d'annoter uniquement les classes concrètes (et les méthodes des classes concrètes) avec l'annotation @Transactional, par opposition aux annotations d'interfaces. Vous pouvez certainement placer l'annotation @Transactional sur une interface (ou une méthode d'interface), mais cela ne fonctionne que comme vous vous y attendez si vous utilisez des proxys basés sur une interface. Le fait que Java ne sont pas héritées des interfaces signifie que si vous utilisez des proxy basés sur une classe (proxy-target-class = "true") ou l'aspect basé sur le tissage (mode = " aspectj "), les paramètres de transaction ne sont pas reconnus par l'infrastructure de proxy et de tissage, et l'objet ne sera pas encapsulé dans un proxy transactionnel, ce qui serait décidément mauvais.

Je recommanderais de les mettre sur la mise en œuvre pour cette raison.

De plus, pour moi, les transactions semblent être un détail d'implémentation, elles devraient donc être dans la classe d'implémentation. Imaginez avoir des implémentations de wrapper pour la journalisation ou tester des implémentations (mocks) qui n'ont pas besoin d'être transactionnelles.

9
AngerClown

recommandation de Spring est que vous annotez les implémentations concrètes au lieu d'une interface. Il n'est pas incorrect d'utiliser l'annotation sur une interface, il est juste possible de mal utiliser cette fonctionnalité et de contourner par inadvertance votre déclaration @Transaction.

Si vous avez marqué quelque chose de transactionnel dans une interface et que vous vous référez ensuite à l'une de ses classes d'implémentation ailleurs au printemps, il n'est pas tout à fait évident que l'objet créé par Spring ne respectera pas l'annotation @Transactional.

En pratique, cela ressemble à ceci:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}
8
zmf

Soutenir @Transactional sur les classes concrètes:

Je préfère généralement concevoir une solution en 3 sections: une API, une implémentation et un Web (si besoin). Je fais de mon mieux pour garder l'API aussi légère/simple/POJO que possible en minimisant les dépendances. C'est particulièrement important si vous le jouez dans un environnement distribué/intégré où vous devez beaucoup partager les API.

Mettre @Transactional nécessite des bibliothèques Spring dans la section API, ce que IMHO n'est pas efficace. Je préfère donc l'ajouter dans l'implémentation où la transaction est en cours d'exécution.

1
Firdous Amir

Le mettre sur l'interface est très bien tant que tous les implémenteurs prévisibles de votre IFC se soucient des données TX (les transactions ne sont pas des problèmes que seules les bases de données traitent). Si la méthode ne se soucie pas de TX (mais vous devez la mettre là pour Hibernate ou autre), mettez-la sur l'impl.

En outre, il serait peut-être préférable de placer @Transactional sur les méthodes de l'interface:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
0
engfer