Projekt aplikacji hibernacji z opóźnionym ładowaniem

87

Zwykle używam Hibernate w połączeniu z frameworkiem Spring i jego deklaratywnymi możliwościami demarkacji transakcji (np. @Transactional ).

Jak wszyscy wiemy, hibernacja stara się być tak nieinwazyjna i jak najbardziej przejrzysta , jednak okazuje się to nieco trudniejsze w przypadku lazy-loadednawiązywania relacji.


Widzę wiele alternatyw projektowych o różnych poziomach przejrzystości.

  1. Spraw, aby relacje nie były leniwe (np. fetchType=FetchType.EAGER)
    • To wioaluje całą ideę leniwego ładowania.
  2. Zainicjuj kolekcje przy użyciu Hibernate.initialize(proxyObj);
    • Oznacza to stosunkowo wysokie sprzężenie z DAO
    • Chociaż możemy zdefiniować interfejs z initialize, inne implementacje nie gwarantują żadnego odpowiednika.
  3. Dodaj zachowanie transakcji do Modelsamych obiektów trwałych (używając dynamicznego serwera proxy lub @Transactional)
    • Nie próbowałem podejścia dynamicznego proxy, chociaż nigdy nie wydawało mi się, że @Transactional działa na samych trwałych obiektach. Prawdopodobnie w związku z tym, że hibernacja działa na serwerze proxy.
    • Utrata kontroli podczas rzeczywistych transakcji
  4. Zapewnij leniwy / nieleniwy interfejs API, np. loadData()IloadDataWithDeps()
    • Zmusza aplikację do tego, aby wiedziała, kiedy zastosować którą procedurę, ponownie ścisłe połączenie
    • Przepełnienie metody,, loadDataWithA()....,loadDataWithX()
  5. Wymuś wyszukiwanie zależności, np. Tylko przez wykonanie byId()operacji
    • Wymaga wielu procedur nie zorientowanych obiektowo, np. findZzzById(zid), A następnie getYyyIds(zid)zamiastz.getY()
    • Przydatne może być pobieranie każdego obiektu w kolekcji jeden po drugim, jeśli między transakcjami występuje duży narzut przetwarzania.
  6. Uczyń część aplikacji @Transactional zamiast tylko DAO
    • Możliwe rozważania na temat transakcji zagnieżdżonych
    • Wymaga procedur dostosowanych do zarządzania transakcjami (np. Wystarczająco małych)
    • Mały wpływ zautomatyzowany, chociaż może skutkować dużymi transakcjami
  7. Zapewnij DAO dynamiczne profile pobierania , np.loadData(id, fetchProfile);
    • Aplikacje muszą wiedzieć, którego profilu użyć i kiedy
  8. Typ transakcji AoP, np. Przechwytywanie operacji i wykonywanie transakcji w razie potrzeby
    • Wymaga manipulacji kodem bajtowym lub użycia serwera proxy
    • Utrata kontroli podczas zawierania transakcji
    • Czarna magia, jak zawsze :)

Czy przegapiłem jakąś opcję?


Które podejście preferujesz, próbując zminimalizować wpływ lazy-loadedrelacji na projekt aplikacji?

(Och, przepraszam za WoT )

Johan Sjöberg
źródło
przykład dla opcji 2 i 5: m-hewedy.blogspot.ch/2010/03/…
Adrien Be
Czy mógłbyś podać przykład opcji 4?
degreeightdc

Odpowiedzi:

26

Jak wszyscy wiemy, hibernacja stara się być jak najbardziej nieinwazyjna i przejrzysta

Powiedziałbym, że początkowe założenie jest błędne. Przejściowa trwałość jest mitem, ponieważ aplikacja zawsze powinna dbać o cykl życia jednostki i rozmiar ładowanego grafu obiektu.

Zauważ, że Hibernate nie potrafi czytać myśli, dlatego jeśli wiesz, że potrzebujesz określonego zestawu zależności dla określonej operacji, musisz jakoś wyrazić swoje zamiary Hibernate.

Z tego punktu widzenia rozwiązania, które wyraźnie wyrażają te intencje (mianowicie 2, 4 i 7), wyglądają rozsądnie i nie cierpią z powodu braku przejrzystości.

axtavt
źródło
Oczywiście masz rację, jak najbardziej przejrzyste działa tylko do tej pory. To kilka fajnych wyborów, na które wybrałeś.
Johan Sjöberg
IMHO: idealnie poprawna odpowiedź. Rzeczywiście, to mit. BTW: mój głos byłby na opcje 4 i 7 (lub w ogóle odejście od ORM)
G. Demecki
7

Nie jestem pewien, do jakiego problemu (spowodowanego lenistwem) sugerujesz, ale dla mnie największym problemem jest uniknięcie utraty kontekstu sesji w moich własnych pamięciach podręcznych aplikacji. Typowy przypadek:

  • obiekt foojest ładowany i umieszczany na mapie;
  • inny wątek pobiera ten obiekt z mapy i wywołuje foo.getBar()(coś, co nigdy wcześniej nie było wywoływane i jest oceniane leniwie);
  • Bum!

Aby rozwiązać ten problem, mamy kilka zasad:

  • zawijaj sesje w możliwie najbardziej przejrzysty sposób (np. OpenSessionInViewFilterdla aplikacji internetowych);
  • mają wspólne API dla wątków / pul wątków, w których wiązanie / odłączanie sesji db odbywa się gdzieś wysoko w hierarchii (opakowane try/finally), więc podklasy nie muszą o tym myśleć;
  • podczas przekazywania obiektów między wątkami, przekazuj identyfikatory zamiast samych obiektów. Wątek odbierający może załadować obiekt, jeśli zajdzie taka potrzeba;
  • podczas buforowania obiektów nigdy nie buforuj obiektów, ale ich identyfikatory. Miej abstrakcyjną metodę w klasie DAO lub menedżera, aby załadować obiekt z pamięci podręcznej Hibernacji drugiego poziomu, gdy znasz identyfikator. Koszt odzyskania obiektów z pamięci podręcznej Hibernacji 2 poziomu jest nadal znacznie tańszy niż przejście do DB.

Jak widać, nie jest to rzeczywiście nieinwazyjne i przejrzyste . Ale koszt jest nadal do zniesienia, porównując go z ceną, którą musiałbym zapłacić za chętny załadunek. Problem z ostatnim polega na tym, że czasami prowadzi to do efektu motyla podczas ładowania pojedynczego obiektu, do którego się odwołujemy, nie mówiąc już o kolekcji obiektów. Zużycie pamięci, użycie procesora i opóźnienie, aby wspomnieć o najmniejszym, są również znacznie gorsze, więc myślę, że mogę z tym żyć.

mindas
źródło
Dzięki za odpowiedź. Utrata transparencyjest spowodowana zmuszeniem aplikacji do zwracania uwagi na ładowanie leniwych obiektów. Gdyby wszystko było pobierane chętnie, aplikacja mogłaby być całkowicie nieświadoma tego, czy obiekty są utrwalone w bazie danych, czy nie, ponieważ Foo.getBar()zawsze się powiedzie. > when passing objects between threads, pass IDstak, odpowiadałoby to numerowi 5.
Johan Sjöberg
3

Bardzo powszechnym wzorcem jest użycie OpenEntityManagerInViewFilter, jeśli tworzysz aplikację internetową.

Jeśli budujesz usługę, otworzyłbym TX na publicznej metodzie usługi, a nie na DAO, ponieważ bardzo często metoda wymaga pobrania lub zaktualizowania kilku jednostek.

To rozwiąże każdy „wyjątek Lazy Load”. Jeśli potrzebujesz czegoś bardziej zaawansowanego do dostrajania wydajności, myślę, że pobieranie profili jest najlepszym rozwiązaniem.

Augusto
źródło
1
Chyba chciałeś powiedzieć: bardzo częstym antywzorzec projektowy ... . Chociaż zgodziłbym się z otwarciem TX na poziomie usług, ale korzystanie z tego OSIVjest nadal przeciwieństwem i prowadzi do bardzo poważnych problemów, takich jak niemożność prawidłowego radzenia sobie z wyjątkami lub pogorszenie wydajności. Podsumowując: IMHO OSIV to łatwe rozwiązanie, ale dobre tylko dla zabawkowych projektów.
G. Demecki