Gdzie należy adnotacja @Transactional?

528

Należy umieścić @Transactionalw DAOklasach i / lub ich metod czy też lepiej opisywanie klas usług, które są wzywające użyciu obiektów DAO? Czy ma sens dodawanie adnotacji do obu „warstw”?

Thomas Einwaller
źródło

Odpowiedzi:

537

Myślę, że transakcje należą do warstwy usług. To ten, który wie o jednostkach pracy i przypadkach użycia. To właściwa odpowiedź, jeśli masz kilka wstrzyknięć DAO do Usługi, które muszą współpracować w ramach jednej transakcji.

duffymo
źródło
Zgadzam się z tym. Czasami to nie ma znaczenia, ale czasami możesz z tego skorzystać, np. Sesja Hibernacji jest rozłożona na czas transakcji, więc wszystkie załadowane obiekty znajdują się w pamięci podręcznej pierwszego poziomu i nie trzeba ponownie podłączać obiektów do sesji, a także leniwie ładowane właściwości działają bez zakłóceń.
dma_k
5
Czy transakcja globalna może składać się z więcej niż jednego z tych @Transactional (propagation = Propagation.REQUIRED)? Czy @Transactional jest zawsze granicą transakcji? Nie jestem pewien, czy otrzymałem to z dokumentacji, ale wydaje się nieco, że możesz tworzyć nawet transakcje, które składają się z @Transactional metod i wszystkiego, co działa w środku
lisak
1
Tak, myślę, że najbardziej zewnętrzny @ transakcyjny jest tym, który staje się granicą transakcji, jeśli włączona jest Propagation.REQUIRED.
duffymo,
309

Zasadniczo zgadzam się z innymi, stwierdzając, że transakcje zwykle rozpoczynane są na poziomie usługi (w zależności od wymaganej szczegółowości).

Jednak w międzyczasie zacząłem również dodawać @Transactional(propagation = Propagation.MANDATORY)do mojej warstwy DAO (i innych warstw, które nie mogą uruchamiać transakcji, ale wymagają już istniejących), ponieważ o wiele łatwiej jest wykryć błędy, w przypadku których zapomniałeś rozpocząć transakcję w abonencie wywołującym ( np. usługa). Jeśli Twoje DAO jest opatrzone adnotacją o obowiązkowej propagacji, otrzymasz wyjątek stwierdzający, że po wywołaniu metody nie ma żadnej aktywnej transakcji.

Mam również test integracji, w którym sprawdzam wszystkie ziarna (postprocesor bean) pod kątem tej adnotacji i nie udaje się, jeśli istnieje @Transactional adnotacja z propagacją inną niż Obowiązkowa w fasoli, która nie należy do warstwy usług. W ten sposób upewniam się, że nie rozpoczynamy transakcji na niewłaściwej warstwie.

mnp
źródło
3
Należy to zrobić w warstwie dao (interfejsy), w dao impl. warstwa czy obie?
Johan
3
Nie wiem, czy pasuje to do tej dyskusji, ale inną wskazówką może być dodanie @Transactional (readOnly = true) w dao impl w operacjach niepisujących.
maxivis
10
@Johan Spring zaleca umieszczanie adnotacji transakcji na klasach implementacji zamiast na interfejsach.
Mathias G.,
Bardzo podoba mi się ten pomysł, próbując go również w moich projektach
Thomas Einwaller
1
Zobaczę, czy rozumiem ... czy mówisz, że powinienem umieścić @Transactionalklasę implementacji usługi i powinienem wdrożyć implementację klasy @Transactional(propagation = MANDATORY)DAO (repozytorium)?
John Henckel,
93

Adnotacje transakcyjne należy umieszczać wokół wszystkich nierozdzielnych operacji.

Na przykład połączenie to „zmień hasło”. Składa się z dwóch operacji

  1. Zmień hasło.
  2. Sprawdź zmianę.
  3. Wyślij klientowi wiadomość e-mail o zmianie hasła.

Zatem w powyższym przypadku, jeśli audyt się nie powiedzie, to czy zmiana hasła również nie powiedzie się? Jeśli tak, to transakcja powinna wynosić około 1 i 2 (czyli w warstwie usługi). Jeśli wiadomość e-mail nie powiedzie się (prawdopodobnie powinna być w tym bezpieczna, aby nie zawiodła), to czy powinna wycofać hasło zmiany i audyt?

To są pytania, które musisz zadać, decydując, gdzie umieścić @Transactional.

Michael Wiles
źródło
41

Prawidłową odpowiedzią dla tradycyjnych architektur Spring jest umieszczenie semantyki transakcyjnej w klasach usług z powodów, które inni już opisali.

Pojawiający się trend na wiosnę to projektowanie oparte na domenie (DDD). Spring Roo ładnie ilustruje ten trend. Chodzi o to, aby obiekt domeny POJO był znacznie bogatszy niż w typowych architekturach Spring (zwykle są one anemiczne ), a w szczególności, aby umieścić semantykę transakcji i trwałości na samych obiektach domeny. W przypadkach, gdy wszystko, czego potrzeba, to proste operacje CRUD, kontrolery sieciowe działają bezpośrednio na obiektach POJO obiektu domeny (w tym kontekście działają jako podmioty) i nie ma warstwy usług. W przypadkach, gdy potrzebna jest jakaś koordynacja między obiektami domeny, możesz obsłużyć komponent bean usługi za pomocą@Transactionzgodnie z tradycją. Można ustawić propagację transakcji na obiektach domeny REQUIREDtak, aby obiekty domeny korzystały z wszelkich istniejących transakcji, takich jak transakcje rozpoczęte w komponencie bean usługi.

Technicznie technika ta wykorzystuje AspectJ i <context:spring-configured />. Roo używa definicji typu AspectJ w celu oddzielenia semantyki encji (transakcji i trwałości) od elementów obiektu domeny (zasadniczo pól i metod biznesowych).

naXa
źródło
38

Normalnym przypadkiem byłoby umieszczanie adnotacji na poziomie warstwy usługi, ale tak naprawdę zależy to od twoich wymagań.

Adnotacje w warstwie usług skutkują dłuższymi transakcjami niż adnotacje na poziomie DAO. W zależności od poziomu izolacji transakcji, który może powodować problemy, ponieważ transakcje współbieżne nie zobaczą nawzajem swoich zmian, np. POWTARZANE CZYTANIE.

Adnotacje na temat DAO sprawią, że transakcje będą jak najkrótsze, z tą wadą, że funkcjonalność, którą ujawnia twoja warstwa usług, nie będzie wykonywana w pojedynczej transakcji (możliwej do rozszerzenia).

Adnotacja na obu warstwach nie ma sensu, jeśli tryb propagacji jest ustawiony na domyślny.

hammarback
źródło
31

Kładę @Transactionalna @Servicewarstwie i ustawiam rollbackFordowolny wyjątek oraz w readOnlycelu dalszej optymalizacji transakcji.

Domyślnie @Transactionalbędzie szukał tylko RuntimeException(Niezaznaczone wyjątki), ustawiając wycofywanie na Exception.class(Sprawdzone wyjątki), będzie przywracał dla każdego wyjątku.

@Transactional(readOnly = false, rollbackFor = Exception.class)

Zobacz Sprawdzone vs. Niezaznaczone wyjątki .

użytkownik2601995
źródło
17

Czy ma sens dodawanie adnotacji do obu „warstw”? - czy nie ma sensu adnotować zarówno warstwy usługi, jak i warstwy dao - jeśli chce się upewnić, że metoda DAO jest zawsze wywoływana (propagowana) z warstwy usługi z propagacją „obowiązkową” w DAO? Zapewniłoby to pewne ograniczenie dla wywoływania metod DAO z warstwy interfejsu użytkownika (lub kontrolerów). Ponadto - szczególnie w przypadku testowania jednostkowego warstwy DAO - adnotacja DAO zapewni również przetestowanie jej pod kątem funkcjonalności transakcyjnej.

tweekran
źródło
Czy nie spowodowałoby to zagnieżdżonych transakcji? I wszystkie subtelne problemy, które się z tym wiążą?
HDave
11
Nie, nie ma zagnieżdżonych transakcji w JPA. Umieszczenie ich w obu przypadkach byłoby całkowicie w porządku - jeśli jesteś już w transakcji, gdy trafisz DAO, transakcja będzie kontynuowana.
fool4jesus
4
Jeśli używasz, może wystąpić transakcja zagnieżdżona propagation=Propagation.REQUIRES_NEW. W przeciwnym razie w większości przypadków, w tym propagacji = obowiązkowe, DAO po prostu weźmie udział w istniejącej transakcji rozpoczętej przez warstwę usługi.
dan carter
15

Do transakcji na poziomie bazy danych

głównie używałem @Transactionalw DAO tylko na poziomie metody, więc konfiguracja może być specyficzna dla metody / przy użyciu domyślnej (wymagane)

  1. Metoda DAO, która pobiera dane (wybierz ..) - nie potrzebuje @Transactional tego, może prowadzić do narzutu z powodu przechwytywania transakcji / i proxy AOP, które również muszą zostać wykonane.

  2. Otrzymają metody DAO, które wstawiają / aktualizują @Transactional

bardzo dobry blog o transakcjach

Na poziomie aplikacji -
używam transakcji dla logiki biznesowej Chciałbym mieć możliwość rolbacka w przypadku nieoczekiwanego błędu

@Transactional(rollbackFor={MyApplicationException.class})
public void myMethod(){

    try {    
        //service logic here     
    } catch(Throwable e) {

        log.error(e)
        throw new MyApplicationException(..);
    }
}
lukass77
źródło
6
+1 bardzo ładny artykuł o TransactionalnaJava
dąb
11

Zwykle transakcję należy umieścić w warstwie usługi.

Ale, jak powiedziano wcześniej, atomowość operacji mówi nam, gdzie adnotacja jest konieczna. Tak więc, jeśli używasz frameworków takich jak Hibernacja, w których pojedyncza operacja „zapisz / aktualizuj / usuń / ... modyfikacja” na obiekcie może potencjalnie zmodyfikować kilka wierszy w kilku tabelach (z powodu kaskady przez wykres obiektów) oczywiście powinno być również zarządzanie transakcjami dla tej konkretnej metody DAO.

yannick555
źródło
10

@TransactionalAdnotacje należy umieszczać wokół wszystkich nierozdzielnych operacji. Korzystanie z @Transactionalpropagacji transakcji jest obsługiwane automatycznie. W takim przypadku, jeśli inna metoda zostanie wywołana przez bieżącą metodę, wówczas ta metoda będzie miała opcję dołączenia do bieżącej transakcji.

Weźmy więc przykład:

Mamy czyli 2 modelu Countryi City. Mapowanie relacyjne Countryi Citymodel jest jak Countrymożna mieć wiele Miast, więc mapowanie jest jak,

@OneToMany(fetch = FetchType.LAZY, mappedBy="country")
private Set<City> cities;

Tutaj Kraj jest mapowany na wiele miast z ich pobieraniem Lazily. Więc tutaj pojawia się rola, @Transactinalkiedy odzyskujemy obiekt Country z bazy danych, wtedy otrzymamy wszystkie dane obiektu Country, ale nie otrzymamy zbioru miast, ponieważ pobieramy miasta LAZILY.

//Without @Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //After getting Country Object connection between countryRepository and database is Closed 
}

Kiedy chcemy uzyskać dostęp do Zestawu Miast z obiektu kraju, wówczas otrzymamy wartości zerowe w tym Zestawie, ponieważ obiekt Zestawu utworzony tylko w tym Zestawie nie jest inicjowany z danymi tam, aby uzyskać wartości Zestawu, którego używamy, @Transactionaltj.

//with @Transactional
@Transactional
public Country getCountry(){
   Country country = countryRepository.getCountry();
   //below when we initialize cities using object country so that directly communicate with database and retrieve all cities from database this happens just because of @Transactinal
   Object object = country.getCities().size();   
}

Zasadniczo @Transactionalusługa może wykonywać wiele połączeń w jednej transakcji bez zamykania połączenia z punktem końcowym.

Harshal Patil
źródło
2
bardzo pouczające, dziękuję! Właśnie tego szukałem, wyjaśnienie, co @Transactionalnaprawdę jest
CybeX
6

Lepiej mieć go w warstwie serwisowej! Wyjaśnia to jasno jeden z artykułów, z którymi się wczoraj spotkałem! Oto link , który możesz sprawdzić!

zegar słoneczny
źródło
5

wprowadź opis zdjęcia tutaj

@TransactionalNależy stosować na warstwie usługowej, ponieważ zawiera logikę biznesową. Warstwa DAO zwykle ma tylko operacje CRUD bazy danych.

// the service class that we want to make transactional
@Transactional
public class DefaultFooService implements FooService {

    Foo getFoo(String fooName);

    Foo getFoo(String fooName, String barName);

    void insertFoo(Foo foo);

    void updateFoo(Foo foo);
}

Dokument wiosenny: https://docs.spring.io/spring/docs/4.2.x/spring-framework-reference/html/transaction.html

Alin
źródło
5

Warstwa serwisowa jest najlepszym miejscem do dodawania @Transactionaladnotacji, ponieważ większość logiki biznesowej jest tutaj dostępna, zawiera zachowanie przypadków użycia na poziomie szczegółów.

Załóżmy, że dodajemy go do DAO, a z usługi nazywamy 2 klasy DAO, jedna nie powiodła się, a druga zakończyła się sukcesem, w tym przypadku, jeśli @Transactionalnie jest w służbie, jedna baza danych zatwierdzi, a druga przywróci.

Dlatego moim zaleceniem jest mądre stosowanie tej adnotacji i używanie jej tylko w warstwie serwisowej.

Projekt Github - java-algos

VishalTambe
źródło
Wyjątki, takie jak ObjectOptimisticLockingFailureException, występują tylko po zakończeniu transakcji. Jeśli masz osobne wątki dla innych operacji, takich jak usługa pocztowa, ten projekt całkowicie zawiedzie. Cierpimy teraz. Jedynym pozostawionym rozwiązaniem będzie AOP.
Nabin Kumar Khatiwada
4

Przede wszystkim określmy, gdzie musimy użyć transakcji ?

Myślę, że poprawna odpowiedź brzmi - kiedy musimy upewnić się, że sekwencja akcji zostanie zakończona razem jako jedna operacja atomowa lub nie zostaną wprowadzone żadne zmiany, nawet jeśli jedna z akcji zakończy się niepowodzeniem.

Dobrze znaną praktyką jest wprowadzanie logiki biznesowej do usług. Metody serwisowe mogą więc zawierać różne działania, które muszą być wykonywane jako pojedyncza logiczna jednostka pracy. Jeśli tak, to taką metodę należy oznaczyć jako transakcyjną . Oczywiście nie każda metoda wymaga takich ograniczeń, więc nie musisz oznaczać całej usługi jako transakcyjnej .

Co więcej - nie zapomnij wziąć pod uwagę, że @Transactional może oczywiście obniżyć wydajność metody. Aby zobaczyć cały obraz, musisz znać poziomy izolacji transakcji. Wiedząc, że może to pomóc uniknąć korzystania z @Transactional tam, gdzie nie jest to konieczne.

smaiakov
źródło
3

Lepiej zachować @Transactional w oddzielnej środkowej warstwie między DAO a warstwą usług. Ponieważ wycofywanie jest bardzo ważne, możesz umieścić całą manipulację DB w środkowej warstwie i zapisać logikę biznesową w Warstwie usług. Środkowa warstwa będzie współdziałać z twoimi warstwami DAO.

Pomoże Ci to w wielu sytuacjach, takich jak ObjectOptimisticLockingFailureException - ten wyjątek występuje tylko po zakończeniu transakcji. Tak więc nie możesz złapać go w środkowej warstwie, ale możesz teraz złapać w warstwie usługi. Nie byłoby to możliwe, jeśli masz @Transactional w warstwie usługi. Chociaż możesz złapać kontroler, ale kontroler powinien być tak czysty, jak to możliwe.

Jeśli wysyłasz pocztę lub SMS-y w osobnym wątku po ukończeniu wszystkich opcji zapisywania, usuwania i aktualizacji, możesz to zrobić w usłudze po zakończeniu transakcji w warstwie środkowej. Ponownie, jeśli wspomnisz @Transactional w warstwie usług, poczta zostanie wysłana, nawet jeśli transakcja się nie powiedzie.

Posiadanie środkowej warstwy @Transaction pomoże uczynić kod lepszym i łatwiejszym w obsłudze. W przeciwnym razie, jeśli użyjesz w warstwie DAO, możesz nie być w stanie przywrócić wszystkich operacji. Jeśli używasz w warstwie usługi, w niektórych przypadkach może być konieczne użycie AOP (Programowanie zorientowane na aspekty).

Nabin Kumar Khatiwada
źródło
2

Idealnie, warstwa usługowa (Manager) reprezentuje logikę biznesową i dlatego powinna być opatrzona adnotacjami z. @TransactionalWarstwa serwisowa może wywoływać różne DAO w celu wykonywania operacji DB. Załóżmy sytuacje, w których masz N operacji DAO w metodzie usługi. Jeśli Twoja pierwsza operacja DAO zakończy się niepowodzeniem, inne mogą być nadal przekazywane i skończysz niespójnym stanem DB. Adnotacja Warstwa usługi może uchronić Cię przed takimi sytuacjami.

salsinga
źródło
1

Wolę używać @Transactionalw warstwie usług na poziomie metody.

Spark-Beginner
źródło
1

@Transactionalwykorzystuje w warstwie usługi, która jest wywoływana przy użyciu warstwy kontrolera ( @Controller) i wywołania warstwy usługi do warstwy DAO ( @Repository), tj. operacji związanych z bazą danych.

sob
źródło