hibernate lista vs set

Hibernate Lista vs Set – czego używać?

Inspiracją do tego wpisu był post na mojej grupie facebookowej. Gdzie kolega Robert (pozdrawiam 😉) zadał właśnie takie pytanie. Ja udzieliłem tam dosyć długiej odpowiedzi i stwierdziłem, że po drobnych poprawkach mogę zrobić z tego wpis na bloga. Oczywiście na drobnych poprawkach się nie skończyło, tak naprawdę musiałem napisać wszystko jeszcze raz.

Zacznę od tego, że na przestrzeni lat zaszło wiele zmian w Hibernate. Wychodzą nowe wersje i przede wszystkim zostało naprawionych wiele błędów. Niektóre z nich niestety były obecne w Hibernate latami. Pamiętam jak pewnego razu miałem jakiś problem związany z Hibernate’em. Zacząłem szukać w google, na stackoverflow i w końcu dokopałem się do jakiegoś otwartego buga na Jirze Hibernate’a.

Bug był otwarty przez 12 lat! Niestety nie pamiętam, czego dotyczył i nie jestem w stanie zweryfikować czy nadal jest otwarty. Inny bug, o którym wiem, wisiał ponad 5 lat! I był to dosyć poważny bug, ale w końcu został naprawiony.

Niestety z rozwojem takiego skomplikowanego narzędzia wiąże się wiele problemów. Tak samo jest z używaniem go. Wiele z nich musimy rozwiązywać każdego dnia. I jednym z takich problemów jest to jakiej kolekcji używać w jakiej sytuacji. Oczywiście chodzi tu o użycie kolekcji w kontekście relacji one-to-many i many-to-many.

W tym wpisie rozpatruję listę i set w kontekście Hibernate’a, a nie różnicę pomiędzy tymi kolekcjami.

Lista czy Set ?

Tak naprawdę nie ma jednoznacznej odpowiedzi na to pytanie. Tak samo jak z wieloma rzeczami związanymi z programowaniem. To zależy od tego, czego w dane sytuacji potrzebujesz, albo czego potrzebujesz bardziej. Nawet gdy przestudiujesz przykłady z dokumentacji i później sekcję 26. Performance Tuning and Best Practices, to trudno też o jednoznaczną odpowiedź.

Ze stosowaniem list wiążą się pewne problemy, wynika to z tego, że implementacją listy, jakiej używa Hibernate to PersistentBag. Lista ta jest listą nieuporządkowana i może przechowywać duplikaty. W związku z tym, że dopuszczone są duplikaty Hibernate w sytuacjach, gdy dodajemy i usuwamy elementy z tej listy, może generować dodatkowe zapytania do bazy danych. Więc w większości przypadków tam, gdzie będziemy wykonywać jakieś operacje na kolekcjach w relacji one-to-many i zwłaszcza w relacji many-to-many powinniśmy używać Seta. Dzięki takiemu podejściu unikniemy niepotrzebnych dodatkowych zapytań i nasza aplikacja będzie miała trochę lepszą wydajność (będzie po prostu mnie zapytań do bazy danych).

 

Co to znaczy, że lista jest nieuporządkowana (unordered)?

Hibernate używa PersistentBag jako implementacji listy. PersistentBag nie jest uporządkowaną kolekcją tzn. że nie zachowuje kolejności wstawiania elementów inaczej niż domyślna implementacja listy w Javie czyli ArrayList. ArrayList zachowuje kolejność wstawiania elementaów.

 

W relacji many-to-many właściwie zawsze powinniśmy stosować set, ponieważ w przypadku listy zawsze pojawią się dodatkowe zapytanie dla tabeli łączącej. Dla listy w sytaucji gdy chcemy usunąć jakiś element z kolekcji, Hibernate najpierw usunie wszystkie elementy z tabeli łączącej, a później doda wszystkie te, które pozostały na liście jeszcze raz.

Kolejną sytuacją dotyczącą listy jest to, że Hibernate nie może w jednym zapytaniu zwrócić więcej niż jednego PersistentBag. Wynika to, z tego, że w liście dopuszczone są duplikaty. Wiec jeśli mamy zapytanie z dwoma i więcej joinami (połączone np. relacją one-to-many), to i tak musimy stosować Set, ponieważ Hibernate nie wie, która strona danego joina została z duplikowana.

Kiedy stosować listę w Hibernate?

Generalnie Set zużywa zwykle trochę więcej pamięci niż Lista. Wynika to z implementacji. Więc  jak mamy dużo odczytów w aplikacji w relacji one-to-many (a często się tak dzieje). To stosując wszędzie seta, mamy automatycznie trochę więcej pracy dla Garbage Collectora. Oczywiście można powiedzieć, że to trochę przedwczesna optymalizacja, mikro optymalizacja i że nie ma to aż tak dużego znaczenia itd. Jednak tak to mniej więcej wygląda w praktyce. Więc jeśli bardzo często odczytujesz, ale nie wykonujesz operacji na kolekcjach z powiązanymi encjami, to może dobrym rozwiązaniem dla Ciebie będzie stosowanie listy?

Kolejna rzecz to z Hibernate’owej listy łatwo zrobić uporządkowaną listę poprzez dodanie adnotację @OrderBy lub adnotację @OrderColumn. Więc jeśli potrzebujemy uporządkowanej listy, wystarczy jedna adnotacja.

Ostatni i tym razem bardziej oczywisty przypadek, to zastosowanie listy wtedy gdy chcemy zwrócić duplikaty, które mamy w bazie danych. Set ze względu na swoją naturę przechowuje tylko unikalne wartości, więc do wyświetlenia duplikatów, będziemy potrzebowali listy.

Jak używać seta w Hibernate prawidłowo?

Przy użyciu seta trzeba zaimplementować metody equals i hashcode i dodatkowo trzeba to robić w sposób odpowiedni dla Hibernate’a. Niestety wielu programistów zapomina o implementacji tych metod, gdy używa seta. Także wielu robi to trochę na pałę, generując te metody lub dodając lombokowe adnotacje 😉. Co w większości przypadków nie jest dobre. Pamiętaj więc o implementacji tych metod dla encji, które przechowujesz w setach.

Podsumowanie

Tak jak już napisałem, nie ma jednoznacznej odpowiedzi na to pytanie. Trzeb mieć świadomość różnych zachowań Hibernate’a i różnych sytuacji, na które możesz natrafić.

W Hibernate warto zawsze włączyć (lokalnie) logowanie zapytań do bazy danych i obserwować jakie zapytania generuje Hibernate. Pomoże to uniknąć problemów ze stosowaniem odpowiedniej kolekcji, ale także pomaga często w wykrywaniu takich problemów jak N + 1 zapytań. A jeśli potrzebujesz bardziej kompleksowej wiedzy na temat Hibernate, to zapraszam do mojego kursu Hibernate, w którym znajdziesz bardzo dużą dawkę wiedzy na temat Hibernate i jego wykorzystania w codziennej pracy.

 

Kurs Hibernate

Mateusz Dąbrowski

Cześć jestem Mateusz, zajmuję się programowaniem już ponad 12 lat z czego ponad 8 programuję w Javie. Zapraszam Cię do lektury mojego bloga. Możesz przeczytać więcej o mnie >>TUTAJ<<

2 thoughts to “Hibernate Lista vs Set – czego używać?”

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *