2010-03-15 26 views
7

Popraw mnie, jeśli coś jest nie tak.DAO, wiosna i hibernacja

Teraz, gdy używamy Spring DAO dla szablonów ORM, gdy używamy atrybutu @Transactional, , nie mamy kontroli nad transakcją i/lub sesją, gdy metoda jest wywoływana zewnętrznie, a nie w ramach metody.

Leniwe ładowanie zapisuje zasoby - mniej zapytań do db, mniej pamięci, aby wszystkie kolekcje zostały pobrane w pamięci aplikacji.

Tak więc, jeśli leniwy = fałsz, to wszystko jest pobierane, wszystkie powiązane zbiory, które nie są efektywne, jeśli w połączonym zestawie znajduje się 10 000 rekordów.

Mam teraz metodę w klasie DAO, która ma zwrócić mi obiekt User. Ma kolekcje reprezentujące połączone tabele bazy danych. Potrzebuję uzyskać obiekt według id, a następnie zapytać o jego kolekcje.

Hibernacja "nie udało się leniwie zainicjować kolekcji" pojawia się, gdy próbuję uzyskać dostęp do połączonej kolekcji, którą zwraca ta metoda DAO.

Proszę wyjaśnić, co to jest obejście?

Aktualizacja: Dobra, pozwól, że cię o to zapytam. DAO jest warstwą abstrakcyjną, więc metoda "getUserById (Integer id)" ma zwrócić obiekt.

Co jeśli w niektórych przypadkach potrzebuję tych powiązanych kolekcji obiektu użytkownika, aw innych sytuacjach potrzebuję tych kolekcji.

Czy istnieją tylko dwa sposoby: 1) lazy loading = false 2) tworzenie różnych metod: getUserByIdWithTheseCollections() getUserByIdWithOtherCollections() i wewnątrz tych metod wykorzystywać swoje podejście?

Mam na myśli, czy istnieją tylko 2 sposoby i nic lepszego?

Aktualizacja 2: Wyjaśnij proszę, co dałoby mi wyraźne użycie SESSIONFACTORY? Jak to wygląda w praktyce? Tworzymy instancję obiektu DAO, , a następnie wprowadzamy do niej fabrykę sesji, co oznaczałoby, że dwa kolejne wywołania metody do DAO będą wykonywane w ramach tej samej transakcji? Wydaje mi się, że tak czy inaczej DAO jest odseparowany od klas, które z niego korzystają!

Logika i transakcje są zawarte w DAO, prawda?

Odpowiedz

6

można uzyskać w transakcji związanej kolekcji, aby go załadować, gdy jesteś nadal w ramach transakcji:

User user = sessionFactory.getCurrentSession().get(User.class, userId); 
user.getLinkedCollection().size(); 
return user; 

Jak BalusC wskazał, można użyć Hibernate.initialize() zamiast size(). To dużo czystsze.

Następnie, po zwrocie takiego obiektu, pole leniwego jest już zainicjowane.

Odpowiadasz na swoje PS - czy możliwe są transakcje na poziomie usługi (zamiast DAO)? Wydaje się, że tak jak każde połączenie DAO w oddzielnej transakcji wydaje się być marnotrawstwem (i może być nieprawidłowe).

+0

@Konrad Garus Proszę zobaczyć mój dopisek pytania, tutaj tekst jest mniej czytelny, więc proszę Cię tam. – EugeneP

+0

@EugeneP Zobacz zaktualizowaną odpowiedź. –

5

Uważam, że najlepiej umieścić @Transactional w warstwie usługi, a nie w warstwie DAO. W przeciwnym razie wszystkie twoje połączenia DAO są w oddzielnych sesjach hibernacji - wszystkie obiekty o równości nie będą działać.

+1

Jest to dobry sposób radzenia sobie z tym podczas pracy z SpringDAO. –

1

Moim zdaniem najlepszym sposobem rozwiązania tego problemu będzie zaprojektowanie aplikacji w modelu sesji na żądanie. Następnie, jeśli nawet masz obiekt pobrany z DAO, dopóki twój wzór OSIV nie działa, możesz bezpiecznie używać obiektu w dowolnym miejscu aplikacji, nawet w widokach bez przeszkadzania w tym. To chyba lepiej, że te proponowane rozwiązanie, ponieważ:

  1. Hibernate.initialize() lub rozmiar jest bardzo sztuczny obejście - co zrobić, jeśli chcesz mieć Użytkownikowi innej kolekcji zainicjowany, byś napisać inny sposób coraz użytkownika?
  2. usługi warstwa modelu transakcyjnym jest OK, ale ten sam problem pojawia się, gdy chcesz uzyskać obiekt wyodrębniony z warstwy usługowej go używać w kontroler lub widoku
1

Można zrobić coś jak następuje:

public User getByUserId(Long id, String ... fetch) { 
    Criteria criteria = createCriteria(); 

    if (fetch != null) { 
     for (String fieldName : fetch) { 
      criteria.setFetchMode(fieldName, FetchMode.JOIN); // fetch these fields eagerly 
     } 
    } 
    return criteria.add(Restrictions.eq("id", id)).list(); 
}