2009-10-23 14 views
33

Eksperymentuję z użyciem wzorca poleceń, aby umożliwić mojej warstwie internetowej pracę z elementami Hibernacji w kontekście pojedynczej transakcji (co pozwala uniknąć wyjątków leniwego ładowania). Jestem jednak teraz zdezorientowany tym, jak powinienem postępować z transakcjami.Wiosenna @Transakcyjna propaganda tylko do odczytu

Moje polecenia wywołują metody warstwy usługi opatrzone adnotacjami z adnotacjami @Transactional. Niektóre z tych metod warstwy usług są tylko do odczytu - np. @Transactional(readOnly=true) - a niektóre z nich to odczyt/zapis.

Moja warstwa usługi udostępnia obsługę komend, która wykonuje polecenia przekazane do niej w imieniu warstwy internetowej.

@Transactional 
public Command handle(Command cmd) throws CommandException 

Przypuszczam, że mam rację w tworzeniu obsługi Polecenie za handle() metody transakcyjne. W tym miejscu pojawia się zamieszanie. Jeśli implementacja polecenia wywołuje metody z wieloma warstwami usług, nie ma sposobu, aby program obsługi wywoływania wiedział, czy operacje wywoływane w komendzie będą tylko do odczytu, do odczytu/zapisu lub kombinacji z dwóch.

Nie rozumiem, w jaki sposób propagacja działa w tym przykładzie. Jeśli miałbym wykonać handle() metodę handle() metoda readOnly=true, co się stanie, jeśli polecenie następnie wywoła metodę warstwy usługi, która jest opatrzona komentarzem z @Transactional(realOnly=false)?

byłbym wdzięczny lepsze zrozumienie tego i komentarze mile widziane ...

Andrew

+1

Więc który z obu przeciwstawnych odpowiedzi jest prawdziwa? Czy ktoś chciał sprawdzić? – LuGo

+0

Od 'handle()' _may_ metody wywołania, które zapisują, transakcja musi dopuszczać zapisy. To byłoby w porządku i poprawne rozwiązanie. Jeśli naprawdę chciałeś, możesz zbadać uruchamianie programowo TX i przełączanie readOnly - być może za pomocą atrybutu Command - ale poważnie wątpię, że to warte wysiłku. –

Odpowiedz

51

Przede wszystkim, od wiosny nie czyni sam upór, że nie można określić dokładnie, co powinno oznaczać readOnly . Ten atrybut jest tylko wskazówką dla dostawcy, zachowanie zależy od, w tym przypadku, od stanu hibernacji.

Jeśli podasz readOnly jako true tryb równo zostanie ustawiony jako FlushMode.NEVER w bieżącej sesji hibernacji zapobiegając sesję od popełnienia transakcję.

Ponadto, setReadOnly (true) zostanie wywołany w połączeniu JDBC, które jest również wskazówką do bazowej bazy danych. Jeśli twoja baza danych obsługuje to (najprawdopodobniej tak), ma to zasadniczo taki sam efekt jak FlushMode.NEVER, ale jest silniejszy, ponieważ nie możesz nawet ręcznie spłukać.

Zobaczmy teraz, jak działa propagacja transakcji.

Jeśli nie ustawisz jawnie readOnly na true, będziesz mieć transakcje odczytu/zapisu. W zależności od atrybutów transakcji (np. REQUIRES_NEW), czasami transakcja jest zawieszona w pewnym momencie, nowa jest uruchamiana i ostatecznie zatwierdzana, a następnie pierwsza transakcja jest wznawiana.

OK, już prawie jesteśmy. Zobaczmy, co przynosi readOnly w tym scenariuszu.

Jeśli metoda w odczytu/zapisu transakcji wymaga to metoda, która wymaga readOnly transakcję, pierwszy powinien zostać zawieszony, ponieważ w przeciwnym razie równo/popełnić by się stało pod koniec drugiej metody.

Odwrotnie, jeśli wywołać metodę z poziomu readOnly transakcji wymagającej odczytu/zapisu znowu pierwszy zostanie zawieszony, ponieważ nie może być zaczerwieniona/popełnione, a druga metoda wymaga tego.

W readOnly do readOnly, a odczytu/zapisu do odczytu/zapisu przypadki transakcja zewnętrzna nie musi być zawieszony (chyba że podasz propagację inaczej, oczywiście).

+0

Czy na pewno? Czy "tylko do odczytu" naprawdę zastąpi określoną politykę propagacji? Trudno było znaleźć odniesienia, ale przynajmniej ten post, który stwierdza coś przeciwnego: http://imranbohoran.blogspot.ch/2011/01/spring-transactions-readonly-whats-it.html –

+11

Jeśli zadzwonisz do fasoli, która ma tylko do odczytu, a następnie ten komponent bean wywołuje inny komponent bean z odczytem-odczytem, ​​nowa transakcja nie jest uruchamiana, druga komponent bean uczestniczy w istniejącej transakcji tylko do odczytu, a zmiany, które wprowadza drugi komponent, nie są zatwierdzane. –

+7

Niepoprawnie - jak @dancarter mówi, metoda ** read/write ** wywoływana w ramach transakcji ** readOnly ** nie powiedzie się po cichu, przynajmniej w przypadku integracji Spring Hibernate. Ponieważ zewnętrzny przechwytywacz TX jest tylko do odczytu, sesja hibernacji nigdy nie jest opróżniana ... i nie są wykonywane żadne aktualizacje SQL. (To jest z domyślnymi atrybutami propagacji - możesz wypróbować 'REQUIRES_NEW', ale nie jest to właściwe rozwiązanie dla większości scenariuszy.) –

8

Domyślnie propagacja transakcji jest WYMAGANA, co oznacza, że ​​ta sama transakcja będzie propagowana od osoby dzwoniącej z transakcji do klienta transakcyjnego. W tym przypadku również status "tylko do odczytu" będzie się rozprzestrzeniał. Na przykład. jeśli transakcja tylko do odczytu wywoła transakcję odczytu i zapisu, cała transakcja będzie tylko do odczytu.

Czy możesz użyć wzorca Otwórz sesję w widoku, aby umożliwić leniwy załadunek? W ten sposób twoja metoda obsługi wcale nie musi być transakcyjna.

+1

Jak mówi @sijk, stan tylko do odczytu propaguje się do wewnątrz - bez ostrzeżeń i żadnej diagnozy, dlaczego Hibernacja nie działa 't commit :( –

+0

Używam jpa + hibernacja + wiosna, aw przypadku, gdy transakcja tylko do odczytu zwana transakcją readwrite i cała akcja była w transakcji readwrite, jednostki, które były trwałe, zostały zatwierdzone, ale jednostki, które zostały zmienione przez moduły pobierające/setery nie zostały zatwierdzone. –

9

Testowanie propogacji jest dość łatwe.

BeanS wywołuje transakcyjną = tylko do odczytu Bean1, która wykonuje wyszukiwanie i wywołuje transakcję = odczyt-zapis Bean2, który zapisuje nowy obiekt.

  • Bean1 uruchamia tx tylko do odczytu.

31 09: 39: 44,199 [pool-1-thread-1] DEBUG osorm.jpa.JpaTransactionManager - Tworzenie nowej transakcji z nazwy [nz.co.vodafone.wcim.business.Bean1.startSomething]: PROPAGATION_ REQUIRED, ISOLATION_DEFAULT, readOnly; ''

  • Fasola 2 uczestniczy w niej.

31 09: 39: 44,230 [pool-1-thread-1] DEBUG o.s.orm.jpa.JpaTransactionManager - Udział w istniejących transakcję

Nic nie jest zaangażowana w bazie danych.

Teraz zmień Bean2 @Transactional adnotacji dodać propagation=Propagation.REQUIRES_NEW

  • Bean1 uruchamia tylko do odczytu TX.

31 09: 31: 36,418 [pool-1-thread-1] DEBUG osorm.jpa.JpaTransactionManager - Tworzenie nowej transakcji z nazwy [nz.co.vodafone.wcim.business.Bean1.startSomething]: PROPAGATION_ REQUIRED, ISOLATION_DEFAULT, readOnly; ''

  • Bean2 rozpoczyna nowy odczyt-zapis tx

31 09: 31: 36,449 [pool-1-thread-1] DEBUG osorm.jpa.JpaTransactionManager - Zawieszenie bieżącej transakcji, tworząc nowa transakcja z nazwą [nz.co.vodafone.wcim.business.Bean2.createSomething]

Zmiany wprowadzone przez Bean2 są teraz zatwierdzone w bazie danych.

Oto przykład, przetestowany z danymi sprężynowymi, hibernacji i Oracle.

@Named 
public class BeanS { 

@Inject 
Bean1 bean1; 

@Scheduled(fixedRate = 20000) 
public void runSomething() { 
    bean1.startSomething(); 
} 

} 


@Named 
@Transactional(readOnly = true) 
public class Bean1 { 

Logger log = LoggerFactory.getLogger(Bean1.class); 

@Inject 
private CircuitStateRepository csr; 

@Inject 
private Bean2 bean2; 

public void startSomething() { 

    Iterable<CircuitState> s = csr.findAll(); 
    CircuitState c = s.iterator().next(); 
    log.info("GOT CIRCUIT {}", c.getCircuitId()); 
    bean2.createSomething(c.getCircuitId()); 

} 

} 


@Named 
@Transactional(readOnly = false) 
public class Bean2 { 

    @Inject 
    CircuitStateRepository csr; 

    public void createSomething(String circuitId) { 

     CircuitState c = new CircuitState(circuitId + "-New-" + new DateTime().toString("hhmmss"), new DateTime()); 

     csr.save(c); 

    } 


} 
4

To wydaje się ignorować ustawienia dla bieżącej aktywnej transakcji, to stosuje się jedynie do ustawienia nowej transakcji:

 
org.springframework.transaction.PlatformTransactionManager 
TransactionStatus getTransaction(TransactionDefinition definition) 
         throws TransactionException 
Return a currently active transaction or create a new one, according to the specified propagation behavior. 
Note that parameters like isolation level or timeout will only be applied to new transactions, and thus be ignored when participating in active ones. 
Furthermore, not all transaction definition settings will be supported by every transaction manager: A proper transaction manager implementation should throw an exception when unsupported settings are encountered. 
An exception to the above rule is the read-only flag, which should be ignored if no explicit read-only mode is supported. Essentially, the read-only flag is just a hint for potential optimization.