2010-06-18 10 views
5

Poniższy kod działa, ale Hibernate nigdy nie przestaje chwytać żadnego obiektu. Wywołanie session.clear() powoduje, że wyjątki dotyczące pobierania połączonej klasy i wywoływania session.evict(currentObject) przed pobraniem następnego obiektu również nie zwalnia pamięci. W końcu wyczerpuję przestrzeń sterty.Hibernate: spaceruj po milionach wierszy i nie przeciekaj pamięci

Sprawdzając moje zrzuty sterty, StatefulPersistenceContext jest korzeniem odśmiecania dla wszystkich odniesień wskazujących na moje obiekty.

public class CriteriaReportSource implements JRDataSource { 

    private ScrollableResults sr; 
    private Object currentObject; 
    private Criteria c; 
    private static final int scrollSize = 10; 
    private int offset = 1; 

    public CriteriaReportSource(Criteria c) { 
     this.c = c; 
     advanceScroll(); 
    } 

    private void advanceScroll() { 
//  ((Session) Main.em.getDelegate()).clear(); 
     this.sr = c.setFirstResult(offset) 
        .setMaxResults(scrollSize) 
        .scroll(ScrollMode.FORWARD_ONLY); 
     offset += scrollSize; 
    } 

    public boolean next() { 
     if (sr.next()) { 
      currentObject = sr.get(0); 
      if (sr.isLast()) { 
       advanceScroll(); 
      } 
      return true; 
     } 

     return false; 
    } 

    public Object getFieldValue(JRField jrf) throws JRException { 
     Object retVal = null; 
     if(currentObject == null) { return null; } 
     try { 
      retVal = PropertyUtils.getProperty(currentObject, jrf.getName()); 
     } catch (Exception ex) { 
      Logger.getLogger(CriteriaReportSource.class.getName()).log(Level.SEVERE, null, ex); 
     } 
     return retVal; 
    } 
} 
+0

Widziałem przykłady tego w [odniesienie do Hibernacja] (http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-update), jednak istnieje 'session.flush()' zostało wywołane przed 'session.clear()'. Czy możesz spróbować, czy to robi różnicę? –

+0

Jaka jest baza danych? Nie wszystkie obsługują prawdziwe przewijanie kursora. Ponadto nie widzę, abyś zamknął ScrollableResults. –

+0

Używam MySQL. –

Odpowiedz

1

Myślę, że jednym z moich problemów było to, że

if (sr.isLast()) { 
    advanceScroll(); 
    //... 

połączeniu z

((Session) Main.em.getDelegate()).clear(); 
//Also, "Main.em.clear()" should do... 

w wyniku płukania d w pierwszej kolejności pobiegnij za wcześnie. To była przyczyną wyjątków dotyczących kolekcji. Kolekcje nie mogą być obsługiwane w StatelessSession, więc jest to poza stołem. Nie wiem, dlaczego session.evict(currentObject) nie działa, gdy działa Session.clear(), ale na ten sposób będę musiał sobie z tym poradzić. Przerzucę punkty odpowiedzi na każdego, kto zdoła to wymyślić.

Na razie mamy odpowiedź. Ręczne okienko przewijania jest wymagane, zamknięcie narzędzia ScrollableResults nie pomaga i muszę poprawnie uruchomić Session.clear().

+0

To powinno przejść do edycji pytania, a nie jako odpowiedzi. –

+1

Dlaczego tak jest? To skutecznie rozwiązuje mój problem. Chociaż jest to pytanie o Session.evict, które mogę przenieść, jest to odpowiedź. –

0

kilka rzeczy chciałbym zaproponować:

Spróbuj zadzwonić setCacheMode(CacheMode.IGNORE) sprawie kryteriów przed otwarciem.

W metodzie advanceScroll() dodaj if (sr != null) sr.close();, aby poprzednie Zwoje Przewijające się zamknęły przed ponownym przypisaniem do nowego.

Jedno pytanie: Jaki jest powód wywołania metody setMaxSize(), a następnie śledzenie przesunięcia, a następnie ponowne otwarcie przewijanych wyników, dlaczego nie po prostu to zrobić?

public CriteriaReportSource(Criteria c) { 
    this.c = c; 
    this.sr = c.setCacheMode(CacheMode.IGNORE) 
       .scroll(ScrollMode.FORWARD_ONLY); 
} 


public boolean next() { 
    if (sr.next()) { 
     currentObject = sr.get(0); 
     return true; 
    } 
    return false; 
} 
+0

Niestety, żadna z sugestii w tej odpowiedzi nie zadziałała. setMaxSize() i takie były próby rozwiązania problemu poprzez kontrolowanie okna tego, co zostało zapytane. Z lub bez nich pamięć wciąż rośnie. Próba sr.close() również była bezowocna. –

+0

Przykro to słyszeć. Podaj StatelessSession, o której wspomniał Pascal, który może być twoim najlepszym wyborem. –

3

Nie użyć sesji stanowej tutaj, to po prostu nie jest właściwym narzędziem do chodzić miliony wierszy i zbudować raport. Zamiast tego użyj The StatelessSession interface.

przypadku korzystania z MySQL Connector/J nawet to nie wystarczy, trzeba też pokonać buforowanie wewnętrzną wykonaną przez sterownik JDBC, z this:

Query query = session.createQuery(query); 
query.setReadOnly(true); 
// MIN_VALUE gives hint to JDBC driver to stream results 
query.setFetchSize(Integer.MIN_VALUE); 
ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY); 
// iterate over results 
while (results.next()) { 
    Object row = results.get(); 
    // process row then release reference 
    // you may need to evict() as well 
} 
results.close(); 
+1

W tej chwili, używając StatelessSession rozwiązuje problem z pamięcią, ale podnosi całe piekło, gdy skojarzone połączenia OneToMany są dostępne (leniwe ładowanie). Rodzaj interesującej wygranej. Jakiekolwiek sugestie dotyczące testowania, jeśli zależy mi na ładowaniu, rozwiązuje to? –

+0

Okazało się, że StatelessSession nie może obsłużyć kolekcji. Znalazłem sposób na wykorzystanie stanu i kontrolowanie mojej pamięci. Zobacz moją odpowiedź na moje własne pytanie. –

Powiązane problemy