2011-11-14 15 views
26

To jest pytanie projektowe, konkretny kod nie został przesłany, aby chronić moje dno.org.hibernate.Session.clear() za szkodliwe?

Podczas pracy z Hibernate średnia przepływu pracy jest następujący:

  1. otwartą sesję
  2. Rozpoczęcie transakcji
  3. wykonaj gospodarczej (czytać i modyfikować dane)
  4. dokonać transakcji
  5. sesja

z możliwym iteracje przez 2-4.

Jakie są rozsądne przypadki użycia dla Session.clear()?

A: Konkretny problem, jaki mam, to (duży) fragment kodu, który ładuje i modyfikuje obiekty, a następnie wyczyści sesję, zasadniczo usuwając wprowadzone zmiany. (Zadanie biznesowe do wykonania nie obejmuje modyfikacji jednostek, więc kod "działa").

Wydaje mi się, że właściwym projektem byłoby upewnienie się, że (duży) fragment kodu nie wprowadza zmian, których nie chce zapisać?

B: Sądzę, że Session.clear() istnieje dla wygody/elastyczności, nie dlatego, że warto go używać.

Czy źle zrozumiałem filozofię Hibernacji?

C: Podżeganie: Czy jest to zły pomysł na to, aby kod struktury bezwarunkowo czyścił() sesję po zakończeniu zadania? IMHO, framework powinien narzekać, jeśli sesja jest brudna po zakończeniu zadania! Sesja powinna zostać zamknięta, ponieważ zadanie zostało wykonane ... (Nie licząc wydajności minutowej)

(Etykiety A, B i C, aby można było wskazać, na którą część odpowiadasz).

Odpowiedz

29

Ad. A: Wygląda na to, że wiesz, co robi clear(). Powodem, dla którego należy go jawnie nazwać, jest usunięcie wszystkich zarządzanych jednostek z pamięci podręcznej L1, aby nie rosła w nieskończoność podczas przetwarzania dużych zestawów danych w jednej transakcji.

Odrzuca wszystkie zmiany wprowadzone do zarządzanych obiektów nie jawnie utrwalone. Oznacza to, że możesz bezpiecznie modyfikować jednostkę, zaktualizować ją jawnie i wyczyścić sesję. Jest to projekt z prawej strony. Oczywiście, jeśli nie wprowadzono żadnych zmian (długa, ale tylko do odczytu), clear() jest zawsze bezpieczny. Można używać także stateless sessions.

Ad. B: Nie, istnieje z powyższych powodów: upewnić się, że L1 (pamięć podręczna sesji) nie rośnie zbyt mocno. Oczywiście ręczne jego utrzymywanie jest kiepskim pomysłem i wskazówką, że należy użyć innego narzędzia do dużych zbiorów danych, ale czasami jest to konieczne.

Należy zauważyć, że w specyfikacji JPA dostępna jest również metoda clear() i flush().W takim przypadku należy zawsze najpierw wywołać flush(), aby wprowadzić zmiany w bazie danych (aktualizacja jawna) przed wywołaniem clear().

Ad. C: To naprawdę dobry pomysł, aby ostrzec użytkownika (być może, wysyłając komunikat ostrzegawczy, a nie wyrzucając wyjątek), gdy usunie sesję z brudnymi zmianami. Ponadto nie sądzę, aby kod zadzwonił pod numer clear(), chyba że jest pewien, że kod użytkownika uruchamia się lub nie wprowadza żadnych zmian.

+0

ad A): przetwarzanie dużych zbiorów danych w ramach jednej transakcji brzmi jak zły projektowania do mnie (przyznane, nie może mieć myśli o wszystkich możliwych problemów ...). Co rozumiesz przez "jawne" utrzymywanie istoty? session.save (entity)? Reklama C): Wygląda na to, że zgadzamy się, że ramy nie powinny tego robić - struktura nie może być pewna, co robią klienci. :-) –

+0

@MortenLauritsenKhodabocus: oczywiście ładowanie i modyfikowanie tysięcy rekordów w jednej transakcji hibernacji jest znakiem, że należy użyć innego narzędzia. Tak, przez * jawnie * mam na myśli wywołanie 'save()', a nie pozwolić Hibernate'owi odkryć brudne byty. –

+0

@MortenLauritsenKhodabocus Proszę oświecić mnie. Dlaczego przetwarzanie dużego zestawu danych tylko do odczytu w jednej transakcji byłoby złym pomysłem? Wydaje mi się, że gdyby dotyczył zamawiania po stronie DB, byłby sprytny, ponieważ zaoszczędziłby na potrzebie wielokrotnej reorganizacji. Czy użycie transakcji nie oznacza, że ​​DB zapamięta Twój uporządkowany stan przez różne zapytania, które są częścią tej samej transakcji? Co więcej, czy funkcja "ScrollableResults API Hibernate" nie używa "jednej Transakcji"? – KyleM

3

Oto kolejny powód, dla którego właśnie natknąłem się: buforowanie poprzednich wyników podczas wielokrotnego wywoływania procedury składowanej w ramach tej samej transakcji. Uproszczony kod w następujący sposób.

//Begin transaction 
SessionFactory sf = HibernateSessionFactory.getFactory(); 
Session dbSession = sf.getCurrentSession(); 
dbSession.beginTransaction(); 

//First call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "A"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Second call to stored procedure 
Query query = dbSession.getNamedQuery("RR_CUST_OPP_DATA"); 
query.setString("custName", "B"); 
List<ShipSummaryRow> shipSummaryRows = query.list(); 

//Commit both  
dbSession.getTransaction().commit(); 

bez wyraźnego() od pierwszego, wiersze ResultSet pierwszego połączenia są kopiowane do wynikowego drugiego połączenia. Używam Oracle 11gR2.

Kluczem do replikacji tego błędu jest wykonywanie obu połączeń w ramach tej samej transakcji. Ponieważ używam wzorca otwartej sesji w widoku, oba wywołania odbywają się automatycznie w ramach tej samej transakcji (tak jak oryginalny kod wywołuje proces w pętli przechowującej wyniki każdego z nich). Dlatego nazywam to błędem; może być uznana za funkcję, ale nawet wtedy clear() nie jest wywoływana w przykładach kodu, stwierdzając, że powinna być wywołana. session.flush() nie zrobił nic. Mapowanie pliku jak poniżej. W rezultacie dodałem clear() na końcu wszystkich moich wywołań procedur. Jeszcze nie przetestowałem z moimi niestandardowymi wywołaniami SQL. To jest trywialne; zdziwiony, że błąd istnieje.

<?xml version="1.0" encoding="UTF-8"?> 
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> 
<hibernate-mapping> 
    <class name="com.jfx.rr.model.ShipSummaryRow"> 
     <id name="id" type="integer"/> 
     <property name="shipQtrString" not-null="true" type="string"/> 
     <property name="shipAmount" not-null="true" type="double"/> 
    </class> 
    <sql-query callable="true" name="RR_CUST_OPP_DATA"> 
     <return class="com.jfx.rr.model.ShipSummaryRow"> 
      <return-property column="SHIPPED_ID" name="id"/> 
      <return-property column="SHIP_QTR" name="shipQtrString"/> 
      <return-property column="SHIPPED_AMOUNT" name="shipAmount"/> 
     </return> 
     { call RR_DASHBOARD_REPORTS_PKG.RR_CUST_OPP_DATA(?, :custName) } 
    </sql-query> 
</hibernate-mapping> 
Powiązane problemy