Dlaczego potrzebuję Transakcji w hibernacji do operacji tylko do odczytu?

107

Dlaczego potrzebuję Transakcji w hibernacji do operacji tylko do odczytu?

Czy następująca transakcja powoduje zablokowanie bazy danych?

Przykładowy kod do pobrania z DB:

Transaction tx = HibernateUtil.getCurrentSession().beginTransaction(); // why begin transaction?
//readonly operation here

tx.commit() // why tx.commit? I don't want to write anything

Czy mogę użyć session.close() zamiast tx.commit()?

user93796
źródło
9
Transakcja jest wymagana przez samą bazę danych. Możesz przeczytać o trybie automatycznego zatwierdzania tutaj: community.jboss.org/wiki/…
Bhesh Gurung
@BheshGurung Chyba wymagamy transkacji tylko do operacji zapisu
user93796
4
Czy przeczytałeś część „Obalanie mitów o automatycznym zatwierdzaniu” w linku?
Bhesh Gurung

Odpowiedzi:

131

W rzeczywistości możesz mieć powody, aby oznaczać transakcje jako tylko do odczytu.

  1. Transakcje do czytania mogą wyglądać naprawdę dziwnie i często ludzie nie zaznaczają metod transakcji w tym przypadku. Ale JDBC i tak utworzy transakcję, po prostu będzie działać, autocommit=truejeśli inna opcja nie zostanie wyraźnie ustawiona.
  2. Ale nie ma gwarancji, że Twoja metoda nie zapisuje w bazie danych. Jeśli oznaczysz metodę jako @Transactional(readonly=true), Spring ustawi transakcję JDBC w tryb tylko do odczytu, więc zdecydujesz, czy to faktycznie można zapisywać do bazy danych w ramach tej transakcji. Jeśli twoja architektura jest uciążliwa i niektórzy członkowie zespołu mogą zdecydować się na umieszczenie zapytania modyfikującego tam, gdzie nie jest to oczekiwane, ta flaga wskaże ci problematyczne miejsce.
  3. Również transakcje tylko do odczytu mogą być optymalizowane przez bazy danych, ale jest to oczywiście specyficzne dla DB. Np. MySQL dodał obsługę tego tylko w InnoDB począwszy od wersji 5.6.4.
  4. Jeśli nie używasz bezpośrednio JDBC, ale raczej ORM, może to być problem. Na przykład społeczność Hibernate twierdzi, że praca poza transakcją może powodować nieprzewidywalne zachowanie. Dzieje się tak, ponieważ Hibernate otworzy transakcję, ale nie zamknie jej samodzielnie, więc połączenie zostanie zwrócone do puli połączeń, a transakcja nie zostanie zatwierdzona. Co się wtedy stanie? JDBC zachowuje ciszę, więc jest to specyficzne dla implementacji (MySQL wycofuje transakcję, Oracle zatwierdza ją). Można to również skonfigurować na poziomie puli połączeń (np. C3P0 daje taką opcję, domyślnie wycofywanie).
  5. Kolejna rzecz, jeśli chodzi o Hibernację, Spring ustawia FlushMode na MANUAL w przypadku transakcji tylko do odczytu, co prowadzi do innych optymalizacji, takich jak brak potrzeby brudnych kontroli.
  6. Możesz chcieć przesłonić lub ustawić jawnie poziom izolacji transakcji. Wpływa to również na transakcje odczytu, ponieważ robisz lub nie chcesz czytać niezatwierdzonych zmian, być narażonym na odczyty fantomowe itp.

Podsumowując - można iść w obie strony, ale trzeba rozumieć konsekwencje.

Stanislav Bashkyrtsev
źródło
Dzięki za odpowiedź Używam hibernacji (natywna hibernacja, nie jpa) Ale hibernacja zmusza mnie do rozpoczęcia transkacji przed wykonaniem jakiejkolwiek operacji db.Jeśli nie uruchomię żadnej, generuje błąd informujący o braku aktywnego tracscationn.Czy mogę oznaczyć transkację jako tylko do odczytu jeśli tak, to jak?
user93796
Flaga tylko do odczytu jest w rzeczywistości ustawiona na połączenie, a nie na samą transakcję, i nie można uzyskać do niej dostępu przez Hibernację, tak po prostu. Musisz korzystać z obsługi Spring Transaction i korzystać z adnotacji lub konfiguracji transakcyjnej opartej na języku XML. W ten sposób Spring przejmuje kontrolę nad połączeniami i transakcjami zamiast Hibernate.
Stanislav Bashkyrtsev
1
Bardzo dobrze wyjaśnione! Inny artykuł Marka Richardsa dość dobrze wyjaśnia pułapki flagi tylko do odczytu w adnotacji transakcyjnej - ibm.com/developerworks/java/library/j-ts1/index.html .
Mahesh
48

Wszystkie instrukcje bazy danych są wykonywane w kontekście transakcji fizycznej, nawet jeśli nie deklarujemy jawnie granic transakcji (BEGIN / COMMIT / ROLLBACK).

Jeśli nie zadeklarujesz jawnie granic transakcji, każda instrukcja będzie musiała zostać wykonana w oddzielnej transakcji ( autocommittrybie). Może to nawet prowadzić do otwierania i zamykania jednego połączenia na instrukcję, chyba że środowisko może obsłużyć powiązanie połączenia na wątek.

Zadeklarowanie usługi jako @Transactionalzapewni jedno połączenie na cały czas trwania transakcji, a wszystkie instrukcje będą używać tego pojedynczego połączenia izolacyjnego. Jest to o wiele lepsze niż przede wszystkim rezygnacja z jawnych transakcji.

W przypadku dużych aplikacji możesz mieć wiele jednoczesnych żądań, a zmniejszenie liczby żądań pozyskania połączenia z bazą danych zdecydowanie poprawi ogólną wydajność aplikacji.

JPA nie wymusza transakcji w operacjach odczytu. Tylko zapisy kończą się wyrzucaniem wyjątku wymaganego przez transakcję na wypadek, gdybyś zapomniał rozpocząć kontekst transakcyjny. Niemniej jednak zawsze lepiej jest zadeklarować granice transakcji, nawet dla transakcji tylko do odczytu (wiosną @Transactionalumożliwia oznaczanie transakcji tylko do odczytu, co ma dużą zaletę w zakresie wydajności).

Vlad Mihalcea
źródło
1
„… wtedy każdy wyciąg będzie musiał zostać zrealizowany w oddzielnej transakcji”. Kontener nie wykonuje przetwarzania wsadowego automatycznie?
Arash
Przetwarzanie wsadowe służy do modyfikacji, a nie do odczytu danych. Mimo to przetwarzanie wsadowe JDBC nie jest domyślnie włączone podczas korzystania z JPA i hibernacji.
Vlad Mihalcea
1
Dzięki, Vlad. Przeczytałem kilka twoich artykułów. Są naprawdę przydatne.
Arash
Ten stackoverflow.com/q/34797480/179850 wydaje się być nieco sprzeczny z twoim stwierdzeniem, że transakcje tylko do odczytu przynoszą duże korzyści w zakresie wydajności. Przynajmniej nie wydaje się, aby tak było w kontekście hibernacji.
nimai
Nie, to nieprawda. Chodzi o ustawienie tylko do odczytu, podczas gdy moje dotyczy unikania automatycznego zatwierdzania.
Vlad Mihalcea
18

Transakcje faktycznie nakładają blokady na bazę danych - dobre silniki bazy danych obsługują współbieżne blokady w rozsądny sposób - i są przydatne w trybie tylko do odczytu, aby zapewnić, że żadna inna transakcja nie dodaje danych, które powodują niespójność widoku. Zawsze chcesz transakcji (chociaż czasami rozsądne jest dostrojenie poziomu izolacji, najlepiej nie robić tego na początku); jeśli nigdy nie piszesz do bazy danych podczas transakcji, zarówno zatwierdzenie, jak i wycofanie transakcji okaże się takie samo (i bardzo tanie).

Teraz, jeśli masz szczęście i Twoje zapytania skierowane do bazy danych są takie, że ORM zawsze mapuje je na pojedyncze zapytania SQL, możesz uciec bez jawnych transakcji, polegając na wbudowanym zachowaniu automatycznego zatwierdzania DB, ale ORM są stosunkowo złożonymi systemami więc nie jest wcale bezpieczne poleganie na takim zachowaniu, chyba że wykonasz dużo więcej pracy, sprawdzając, co faktycznie robi implementacja. Zapisanie wyraźnych granic transakcji jest o wiele łatwiejsze do wykonania (zwłaszcza jeśli można to zrobić za pomocą AOP lub podobnej techniki opartej na ORM; od Java 7 wzwyż można też użyć zasobów próbnych).

Donal Fellows
źródło
14

Nie ma znaczenia, czy tylko czytasz, czy nie - baza danych musi nadal śledzić Twój zestaw wyników, ponieważ inni klienci bazy danych mogą chcieć zapisać dane, które zmieniłyby Twój zestaw wyników.

Widziałem wadliwe programy, które zabijały ogromne systemy baz danych, ponieważ tylko czytały dane, ale nigdy nie zatwierdzały, zmuszając dziennik transakcji do wzrostu, ponieważ DB nie może zwolnić danych transakcji przed COMMIT lub ROLLBACK, nawet jeśli klient nic nie zrobił godzinami.

Ingo
źródło