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-loaded
nawiązywania relacji.
Widzę wiele alternatyw projektowych o różnych poziomach przejrzystości.
- Spraw, aby relacje nie były leniwe (np.
fetchType=FetchType.EAGER)
- To wioaluje całą ideę leniwego ładowania.
- 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.
- Dodaj zachowanie transakcji do
Model
samych 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
- 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()
- Wymuś wyszukiwanie zależności, np. Tylko przez wykonanie
byId()
operacji- Wymaga wielu procedur nie zorientowanych obiektowo, np.
findZzzById(zid)
, A następniegetYyyIds(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.
- Wymaga wielu procedur nie zorientowanych obiektowo, np.
- 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
- Zapewnij DAO dynamiczne profile pobierania , np.
loadData(id, fetchProfile);
- Aplikacje muszą wiedzieć, którego profilu użyć i kiedy
- 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-loaded
relacji na projekt aplikacji?
(Och, przepraszam za WoT )
java
hibernate
spring
lazy-loading
application-design
Johan Sjöberg
źródło
źródło
Odpowiedzi:
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.
źródło
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:
foo
jest ładowany i umieszczany na mapie;foo.getBar()
(coś, co nigdy wcześniej nie było wywoływane i jest oceniane leniwie);Aby rozwiązać ten problem, mamy kilka zasad:
OpenSessionInViewFilter
dla aplikacji internetowych);try/finally
), więc podklasy nie muszą o tym myśleć;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ć.
źródło
transparency
jest 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 IDs
tak, odpowiadałoby to numerowi 5.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.
źródło
OSIV
jest 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.