Kamil Kliczbor @ asptip.net

9Sep/130

NHibernate Session Get vs Load

NhLogoWhite64_med

Wstęp

Wpis ten jest poświęcony sposobom pobierania encji w NHibernate o znanym identyfikatorze. W kontekście pobrania danych pojedynczej encji dostępnymi sposobami są:

  • użycie dowolnego API zapytań z restykcją na identyfikator, np.

user = session.QueryOver<User>()
                    .Where(x => x.Id == user.Id)
                    .SingleOrDefault<User>();
  • metoda Get()

  • metoda Load()

  • odwołanie z innej encji np. var user = employee.User.

W tym wpisie skupimy się na działaniu, podobieństwach oraz różnicach pomiędzy metodą Get() i Load() oraz następstwami ich użycia.

Pobieranie encji z bazy danych na podstawie identyfikatora

Wygodnym sposobem na pozyskiwanie uchwytu do obiektu persystentnego jest korzystanie z metod Get() i Load() na podstawie jego identyfikatora. Słowo uchwyt zostało użyte tutaj celowo, gdyż niekoniecznie od razu uzyskamy encje. Może się zdarzyć, że otrzymamy obiekt proxy (aka uchwyt), co przedstawię już za moment. Zaletą wykorzystanie metod Get() i Load() jest to, że podczas pobierania obiektu mogą one skorzystać z pamięci podręcznej, unikając przy tym niepotrzebnego ruchu do bazy danych, jeśli obiekt się w niej już znajduje.

Spójrzmy na podstawową różnicę pomiędzy tymi dwiema metodami:

  • Jeżeli Load() nie może znaleźć obiektu w pamięci podręcznej lub w bazie danych, rzucany jest wyjątek. Metoda Load() nigdy nie zwraca null.

  • Get() zwraca wartość null, jeśli obiekt nie może zostać znaleziony.

Dodatkowo:

  • Metoda Load() może zwrócić obiekt proxy zamiast rzeczywistego obiektu,

  • natomiast Get() nigdy nie zwraca proxy - przynajmniej takie jest jej wzorcowe zachowanie. Ale czy na pewno ? - o tym później

 

Proxy jest zastępczym obiektem, który uruchamia ładowanie rzeczywistego obiektu z bazy danych, gdy następuje próba pobrania po raz pierwszy dowolnej właściwości obiektu innej niż identyfikator.

Zobaczmy w aplikacji testowej jak zachowa się NHibernate w przypadku, gdy użyjemy obu metod do pobrania nieistniejącej encji.

Różnice pomiędzy Get a Load

W pierwszym przykładzie jak najbardziej oczekiwanym rezultatem wykonania testu było zwrócenie null dla identyfikatora 234, gdyż encja o takim Id nie istnieje w bazie. W drugim teście pomimo, że obiekt o identyfikatorze 234 nie istnieje w bazie metoda Load() zwróciła obiekt proxy, którego identyfikator ma wartość 234. Jak widać w teście tym odwołanie się do Id użytkownika, który nie istnieje w bazie danych nie stanowi problemu i jest jak najbardziej akceptowalne. W dodatku nie został rzucony żaden wyjątek. Z kolei w ostatnim teście, wyjątek zostanie dopiero rzucony przy próbie wczytania innej właściwości niż identyfikator.

Zweryfikujmy teraz jakie typy encji zwracane są przez te metody:

Get_Load_typ_encji

 W pierwszym przykładzie widać, że wczytana instancja typu User jest tego typu. Jednak w przypadku, gdy korzystamy z metody Load() zwrócony obiekt jest typu "UserProxy".

Na koniec sprawdźmy co się stanie, jeżeli spróbujemy wczytać encję, gdy została ona wczytana już wcześniej przy pomocy Get() lub Load().

Get_i_Load_naprzemiennie

Powyższe dwa przykłady są całkiem ciekawe, gdyż nieoczekiwanie otrzymujemy rezultat odwrotny od spodziewanego - z metody Get() zwracany jest obiekt proxy, a z metody Load() zinstancjonowany obiekt. Wytłumaczenie tego zjawiska jest proste - obiekt który zostaje wczytany jako pierwszy trafia do cache pierwszego poziomu, skąd potem jest podczytywany o ile zaistnieje taka potrzeba.

Kiedy Get a kiedy Load ?

Wybór pomiędzy użyciem metody Get () i Load() jest w miarę prosty: Jeśli jesteś pewien, że pożądany obiekt istnieje w bazie, a niemożliwość jego pobrania jest dobrym argumentem do wyrzucenia wyjątku (np. obiekt został usunięty -  można tego doświadczyć stosując podejście optimistic concurrency) to użycie Load() jest dobrym rozwiązaniem. Jeśli nie jesteś pewien, czy obiekt o który pytasz jest persystentny, lepiej użyć Get () i porównać zwracaną wartość z wartością null,a następnie odpowiednio obsłuż sterowanie przepływem programu poprzez np. rzucenie wyjątku, albo przekierowanie na inną stronę w aplikacji web.

Korzystanie z Load() pociąga za sobą dalsze implikacje: aplikacja może pobrać obiekt proxy do instancji nie wykonując natychmiast zapytania do bazy danych. Zapytanie to zostaje odłożone w czasie, co może być niekiedy bardzo przydatne. Wynika z tego cenna uwaga: Load() nie musi od razu rzucać wyjątkiem, jeżeli nie znajdzie obiektu persystentnego w pamięci podręcznej lub w bazie danych. Wyjątek zostanie rzucony później, kiedy aplikacja będzie próbowała użyć obiektu proxy.

 Innym przykładem użycia Load() może być przypadek, gdy chcemy przypisać referencje do innego obiektu. We fragmencie kodu przedstawionym poniżej wczytanie instancji encji Company zostanie przesunięte na później, co  niekiedy może być bardzo przydatne.

user.Company = Session.Load<Company>(companyId)

Zdarzają się przypadki, że zachodzi czasem konieczność rozpoznania typu wczytanego obiektu na podstawie instancji. W przypadku metody Get() jest to jak najbardziej możliwe, natomiast w przypadku Load() nie jest to do końca pewne, gdyż tak jak już pisałem wcześniej - Load() może zwrócić obiekt proxy.

Deproxyfikacja

Zobaczmy jak można wybrnąć z problemu rozpoznawania typu persystentnej instancji encji. Aby tego dokonać, sprawdźmy w jaki sposób NHibernate tworzy obiekty proxy. Domyślnie NHibernate posiada swój wewnętrzny generator obiektów proxy, po części bazujący na LinFu.DynamicProxy (spójrz na NHibernate.Proxy.DynamicProxy.DefaultyProxyMethodBuilder). Jednakże, żeby mieć możliwość sprawdzenia typu obiektu w uniwersalny sposób  tj. niezależny od implementacji fabryki obiektów proxy, można użyć pomocniczej metody GetClassWithoutInitializingProxy znajdującym się w klasie NHibernateProxyHelper. Metoda pomocnicza pozwoli na wyciągnięcie typu encji bez konieczności inicjalizacji obiektu proxy.

 Unproxyfied

Podsumowanie

We wpisie tym przybliżyłem różnice i podobieństwa pomiędzy metodami Get() i Load() interfejsu ISession oraz następstwa ich użycia. Mam nadzieję, że teraz będziesz mógł używać tych dwóch metod z większą świadomością ich działania.

 

Comments (0) Trackbacks (0)

No comments yet.


Leave a comment

No trackbacks yet.