Gdzie powinienem umieścić adnotację @Transactional: w definicji interfejsu czy w klasie implementującej?


91

Pytanie z tytułu w kodzie:

@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 {
   ...
}

Odpowiedzi:


121

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

Zespół Spring zaleca, aby opisywać adnotacje tylko do konkretnych klas @Transactional, w przeciwieństwie do opisywania interfejsów. Z pewnością możesz umieścić @Transactionaladnotację w interfejsie (lub metodzie interfejsu), ale będzie to działać tak, jak byś tego oczekiwał, jeśli używasz serwerów proxy opartych na interfejsie. Fakt, że adnotacje niedziedziczone, oznacza, że ​​jeśli używasz serwerów proxy opartych na klasach, ustawienia transakcji nie zostaną rozpoznane przez infrastrukturę proxy opartą na klasach, a obiekt nie zostanie opakowany w transakcyjny serwer proxy (co byłoby zdecydowanie złe ) . Więc proszę, skorzystaj z rady zespołu Spring i dodaj adnotację tylko do konkretnych klas (i metod konkretnych klas) @Transactional.

Uwaga: ponieważ ten mechanizm jest oparty na proxy, przechwytywane będą tylko „zewnętrzne” wywołania metod przychodzące przez proxy. Oznacza to, że „samo-wywołanie”, tj. Metoda w obiekcie docelowym wywołująca inną metodę obiektu docelowego, nie doprowadzi do faktycznej transakcji w czasie wykonywania, nawet jeśli wywołana metoda jest oznaczona @Transactional!

(Wyróżnienie dodane do pierwszego zdania, inne wyróżnienia z oryginału).


Big +1 BTW, pozwoliłem sobie uczynić cytat cytatem, aby ludzie nie byli zdezorientowani, i dodałem kursywy i tym podobne z oryginału.
TJ Crowder

Przeczytałem to oświadczenie już wcześniej w Spring Docu, ale nadal nie mogę zrozumieć, dlaczego ustawienia transakcji nie będą rozpoznawane przez infrastrukturę proxy opartą na klasach ? Osobiście uważam, że to tylko wiosenne ograniczenie implementacji, a nie problem podstawowej infrastruktury proxy.
dma_k

Patrząc na źródła Springa można dowiedzieć się, że JdkDynamicAopProxyprzegląda wszystkich doradców bean przy każdym wywołaniu metody (zobacz także DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice()), co w przypadku deklaratywnej konfiguracji transakcji obejmuje BeanFactoryTransactionAttributeSourceAdvisor. Z kolei TransactionAttributeSourcePointcut#matches()jest wywoływana, która powinna zbierać informacje dotyczące transakcji. Jest przekazywany do klasy docelowej i zawsze może przechodzić przez wszystkie interfejsy implementowane przez tę klasę. Teraz: dlaczego to nie może działać niezawodnie?
dma_k

2
@dma_k - Środowisko wykonawcze Java daje tylko bezpośredni dostęp do adnotacji klas dziedziczonych z nadklas lub adnotacji metod bez dziedziczenia. Więc Pointcut.matches () musiałby faktycznie przeszukiwać rekurencyjnie wszystkie interfejsy, znaleźć "prawdziwą" nadpisaną metodę z odbiciem, obsługą metod mostkowych, przeciążeniem, varargami, rodzajami generycznymi, proxy i oczywiście zrobić to szybko . Następnie musisz poradzić sobie z dziedziczeniem diamentów - które z prawdopodobnie wielu odziedziczonych @Transactionaladnotacji będą miały zastosowanie? Więc chociaż to wszystko możliwe , nie winię Springa. jira.springsource.org/browse/SPR-975
Peter Davis

9

Możesz umieścić je w interfejsie, ale pamiętaj, że w niektórych przypadkach transakcje mogą się nie zdarzyć. Zobacz drugą wskazówkę w sekcji 10.5.6 dokumentacji Spring:

Spring zaleca, aby dodawać adnotacje tylko do klas konkretnych (i metod klas konkretnych) za pomocą adnotacji @Transactional, w przeciwieństwie do opisywania interfejsów. Z pewnością możesz umieścić adnotację @Transactional na interfejsie (lub metodzie interfejsu), ale działa to tylko tak, jak byś się tego spodziewał, jeśli używasz serwerów proxy opartych na interfejsie. Fakt, że adnotacje w języku Java nie są dziedziczone z interfejsów, oznacza, że ​​jeśli używasz serwerów proxy opartych na klasach (proxy-target-class = "true") lub aspektu tkania (tryb = "aspektj"), ustawienia transakcji są nie jest rozpoznawany przez infrastrukturę proxy i tkania, a obiekt nie zostanie opakowany w transakcyjne proxy, co byłoby zdecydowanie złe.

Z tego powodu zalecałbym umieszczenie ich na implementacji.

Dla mnie również transakcje wydają się szczegółem implementacji, więc powinny znajdować się w klasie implementacji. Wyobraź sobie, że masz implementacje opakowujące do rejestrowania lub testowania implementacji (makiety), które nie muszą być transakcyjne.


9

Spring zaleca , aby zamiast interfejsu dodawać adnotacje do konkretnych implementacji. Używanie adnotacji w interfejsie nie jest błędne, po prostu można niewłaściwie użyć tej funkcji i nieumyślnie ominąć deklarację @Transaction.

Jeśli zaznaczyłeś coś transakcyjnego w interfejsie, a następnie na wiosnę odwołałeś się do jednej z jej klas implementujących w innym miejscu, nie jest do końca oczywiste, że obiekt, który tworzy spring, nie będzie uwzględniał adnotacji @Transactional.

W praktyce wygląda to mniej więcej tak:

public class MyClass implements MyInterface { 

    private int x;

    public void doSomethingNonTx() {}

    @Transactional
    public void toSomethingTx() {}

}

1

Wspieranie @Transactional na konkretnych klasach:

Generalnie wolę projektować rozwiązanie w trzech sekcjach: API, implementacja i sieć (w razie potrzeby). Staram się, aby API było jak najlżejsze / proste / POJO, minimalizując zależności. Jest to szczególnie ważne, jeśli grasz w rozproszonym / zintegrowanym środowisku, w którym musisz dużo udostępniać interfejsy API.

Umieszczenie @Transactional wymaga bibliotek Spring w sekcji API, co nie jest skuteczne. Dlatego wolę dodać to w implementacji, w której przebiega transakcja.


0

Umieszczenie go na interfejsie jest w porządku, o ile wszyscy przewidywalni implementatorzy twojego IFC dbają o dane TX (transakcje nie są problemami, którymi zajmują się tylko bazy danych). Jeśli metoda nie dba o TX (ale musisz ją tam umieścić dla Hibernacji lub czegokolwiek), umieść ją na impl.

Ponadto może być nieco lepiej umieścić @Transactionalmetody w interfejsie:

public interface FooService {
    @Transactional(readOnly = true)
    void doSmth();
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.