Wo soll ich die @ Transactional-Annotation platzieren: bei einer Schnittstellendefinition oder bei einer implementierenden Klasse?

88

Die Frage aus dem Titel im Code:

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


public class FooServiceImpl implements FooService {
   ...
}

vs.

public interface FooService {
   void doSmth ();
}

@Transactional (readonly = true)
public class FooServiceImpl implements FooService {
   ...
}
römisch
quelle

Antworten:

119

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

Das Spring-Team empfiehlt, dass Sie nur konkrete Klassen mit der @TransactionalAnnotation versehen, anstatt Schnittstellen mit Annotationen zu versehen. Sie können die @TransactionalAnnotation zwar auf einer Schnittstelle (oder einer Schnittstellenmethode) platzieren, dies funktioniert jedoch nur so, wie Sie es erwarten würden, wenn Sie schnittstellenbasierte Proxys verwenden. Die Tatsache, dass Anmerkungen nicht vererbt werden, bedeutet, dass bei Verwendung klassenbasierter Proxys die Transaktionseinstellungen von der klassenbasierten Proxy-Infrastruktur nicht erkannt werden und das Objekt nicht in einen Transaktions-Proxy eingeschlossen wird (was ausgesprochen schlecht wäre ). . Befolgen Sie daher bitte den Rat des Spring-Teams und kommentieren Sie nur konkrete Klassen (und die Methoden konkreter Klassen) mit der @TransactionalAnmerkung.

Hinweis: Da dieser Mechanismus auf Proxys basiert, werden nur 'externe' Methodenaufrufe abgefangen, die über den Proxy eingehen. Dies bedeutet, dass 'Selbstaufruf', dh eine Methode innerhalb des Zielobjekts, die eine andere Methode des Zielobjekts aufruft, zur Laufzeit nicht zu einer tatsächlichen Transaktion führt, selbst wenn die aufgerufene Methode mit markiert ist @Transactional!

(Hervorhebung zum ersten Satz hinzugefügt, andere Hervorhebung vom Original.)

Romain Hippeau
quelle
Big +1 Übrigens, ich habe mir erlaubt, das Zitat zu einem Zitat zu machen, damit die Leute nicht verwirrt sind, und die Kursivschrift und dergleichen aus dem Original wieder hinzugefügt.
TJ Crowder
Ich habe diese Anweisung bereits in Spring Docu gelesen, kann aber immer noch nicht verstehen, warum Transaktionseinstellungen von der klassenbasierten Proxy-Infrastruktur nicht erkannt werden . Ich persönlich denke, dies ist nur eine Einschränkung der Spring-Implementierung, nicht das Problem der zugrunde liegenden Proxy-Infrastruktur.
dma_k
Wenn man sich die Spring-Quellen ansieht, kann man herausfinden, dass JdkDynamicAopProxybei jedem Methodenaufruf (siehe auch DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()) alle Bean-Advisors durchlaufen werden , was im Falle einer deklarativen Transaktionskonfiguration der Fall ist BeanFactoryTransactionAttributeSourceAdvisor. Im Gegenzug TransactionAttributeSourcePointcut#matches()wird aufgerufen, der die transaktionsbezogenen Informationen sammeln soll. Es wird die Zielklasse übergeben und kann immer alle Schnittstellen durchlaufen, die diese Klasse implementiert. Nun: Warum kann das nicht zuverlässig funktionieren?
dma_k
2
@dma_k - Die Java-Laufzeit bietet nur direkten Zugriff auf Klassenanmerkungen, die von Oberklassen geerbt wurden, oder auf Methodenanmerkungen ohne Vererbung. Pointcut.matches () müsste also tatsächlich alle Schnittstellen rekursiv durchlaufen, die "echte" überschriebene Methode mit Reflexion finden, Bridge-Methoden handhaben, überladen, varargs, generics, Proxys und natürlich schnell . Dann müssen Sie mit Diamant Vererbung beschäftigen - was möglicherweise viele geerbt @TransactionalAnmerkungen gelten würde? Also, obwohl alles möglich ist , beschuldige ich Spring nicht. jira.springsource.org/browse/SPR-975
Peter Davis
9

Sie können sie in die Benutzeroberfläche einfügen, aber seien Sie gewarnt, dass in einigen Fällen möglicherweise keine Transaktionen stattfinden. Siehe den zweiten Tipp in Abschnitt 10.5.6 der Spring-Dokumente:

Spring empfiehlt, dass Sie nur konkrete Klassen (und Methoden konkreter Klassen) mit der Annotation @Transactional kommentieren, anstatt Schnittstellen mit Anmerkungen zu versehen. Sie können die Annotation @Transactional sicherlich auf einer Schnittstelle (oder einer Schnittstellenmethode) platzieren, dies funktioniert jedoch nur so, wie Sie es erwarten würden, wenn Sie schnittstellenbasierte Proxys verwenden. Die Tatsache, dass Java-Annotationen nicht von Schnittstellen geerbt werden, bedeutet, dass bei Verwendung klassenbasierter Proxys (Proxy-Zielklasse = "true") oder des webbasierten Aspekts (mode = "Aspektj") die Transaktionseinstellungen gelten Wird von der Proxy- und Webinfrastruktur nicht erkannt, und das Objekt wird nicht in einen Transaktionsproxy eingeschlossen, was ausgesprochen schlecht wäre.

Ich würde empfehlen, sie aus diesem Grund in die Implementierung aufzunehmen.

Außerdem scheinen mir Transaktionen ein Implementierungsdetail zu sein, sodass sie in der Implementierungsklasse sein sollten. Stellen Sie sich Wrapper-Implementierungen für die Protokollierung oder Testimplementierungen (Mocks) vor, die nicht transaktional sein müssen.

AngerClown
quelle
9

Die Empfehlung von Spring lautet, dass Sie die konkreten Implementierungen anstelle einer Schnittstelle mit Anmerkungen versehen. Es ist nicht falsch, die Annotation auf einer Schnittstelle zu verwenden. Es ist nur möglich, diese Funktion zu missbrauchen und versehentlich Ihre @ Transaction-Deklaration zu umgehen.

Wenn Sie etwas Transaktionales in einer Schnittstelle markiert haben und dann im Frühjahr auf eine der implementierenden Klassen an anderer Stelle verweisen, ist es nicht ganz offensichtlich, dass das von Spring erstellte Objekt die Annotation @Transactional nicht berücksichtigt.

In der Praxis sieht es ungefähr so ​​aus:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}
zmf
quelle
1

Unterstützung von @Transactional für die konkreten Klassen:

Ich ziehe es vor, eine Lösung in drei Abschnitten zu entwerfen: eine API, eine Implementierung und ein Web (falls erforderlich). Ich versuche mein Bestes, um die API so leicht / einfach / POJO wie möglich zu halten, indem ich Abhängigkeiten minimiere. Es ist besonders wichtig, wenn Sie es in einer verteilten / integrierten Umgebung spielen, in der Sie die APIs häufig gemeinsam nutzen müssen.

Für das Einfügen von @Transactional sind Spring-Bibliotheken im API-Bereich erforderlich, was meiner Meinung nach nicht effektiv ist. Daher ziehe ich es vor, es in der Implementierung hinzuzufügen, in der die Transaktion ausgeführt wird.

Firdous Amir
quelle
0

Das Einfügen in die Benutzeroberfläche ist in Ordnung, solange sich alle vorhersehbaren Implementierer Ihrer IFC um TX-Daten kümmern (Transaktionen sind keine Probleme, mit denen sich nur Datenbanken befassen). Wenn sich die Methode nicht um TX kümmert (aber Sie müssen sie für den Ruhezustand oder was auch immer dort ablegen), setzen Sie sie auf das Gerät.

Außerdem ist es möglicherweise etwas besser, @Transactionaldie Methoden in der Benutzeroberfläche zu platzieren:

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