2010-06-18 7 views
5

Der folgende Code funktioniert, aber Hibernate lässt nie seinen Griff von einem Objekt los. Das Aufrufen von session.clear() verursacht Ausnahmen in Bezug auf das Abrufen einer verknüpften Klasse und das Aufrufen von session.evict(currentObject) vor dem Abrufen des nächsten Objekts kann den Speicher ebenfalls nicht freigeben. Irgendwann erschöpfe ich meinen Haufen Platz.Hibernate: Gehen Sie Millionen von Zeilen und keinen Speicherverlust

Beim Überprüfen meiner Heap-Dumps ist StatefulPersistenceContext das Stammverzeichnis des Garbage Collectors für alle Referenzen, die auf meine Objekte verweisen.

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

Ich habe Beispiele in der [Hibernate-Referenz] (http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-update) gesehen, jedoch dort 'session.flush()' wurde vor 'session.clear()' aufgerufen. Könnten Sie versuchen, ob es einen Unterschied macht? –

+0

Welche Datenbank ist das? Nicht alle unterstützen echtes Cursor-Scrollen. Außerdem sehe ich nicht, dass Sie die ScrollableResults schließen. –

+0

Ich habe MySQL verwendet. –

Antwort

1

Ich denke, eine meiner Probleme war, dass

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

kombiniert mit

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

resultierte die in d Spülung einen Lauf zu früh ausführen. Das war die Ursache für Ausnahmen in Bezug auf Sammlungen. Sammlungen können nicht in einer StatelessSession behandelt werden, also ist das nicht möglich. Ich weiß nicht, warum session.evict(currentObject) nicht funktioniert, wenn Session.clear() funktioniert, aber das ist die Art, wie ich es für jetzt behandeln muss. Ich werfe die Antwortpunkte auf denjenigen, der das herausfinden kann.

Also, für jetzt, da haben wir eine Antwort. Ein manuelles Scrollfenster ist erforderlich, das Schließen der ScrollableResults hilft nicht, und ich muss eine Session.clear() ordnungsgemäß ausführen.

+0

Dies sollte in einer Bearbeitung der Frage, nicht als Antwort gehen. –

+1

Warum ist das? Dies löst mein Problem erfolgreich. Es stellt zwar eine Frage zu Session.evict, über die ich mich bewegen könnte, aber es ist die Antwort. –

0

Paar Dinge würde ich vorschlagen:

Versuchen setCacheMode(CacheMode.IGNORE) über die Kriterien nennen, bevor es zu öffnen.

Fügen Sie in der advanceScroll() -Methode if (sr != null) sr.close(); hinzu, sodass das vorherige ScrollableResults geschlossen wird, bevor Sie die Neuzuweisung an das neue Skript vornehmen.

Eine Frage: Was ist der Grund dafür, setMaxSize() aufzurufen, und dann den Offset zu verfolgen und dann die scrollbaren Ergebnisse wieder zu öffnen, warum nicht einfach?

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

Keiner der Vorschläge in dieser Antwort funktionierte leider. setMaxSize() und solche waren Versuche, das Problem zu lösen, indem sie das Fenster dessen, was abgefragt wurde, kontrollierten. Mit oder ohne sie wächst die Erinnerung weiter. Der sr.close() Versuch war ebenfalls fruchtlos. –

+0

Tut mir leid das zu hören. Geben Sie die StatelessSession, die Pascal erwähnt hat, die beste Wette. –

3

nicht die Stateful Session hier Verwenden Sie, es ist nicht nur das richtige Werkzeug, um Millionen von Zeilen gehen und einen Bericht erstellen. Verwenden Sie stattdessen The StatelessSession interface.

Bei der Verwendung von MySQL Connector/J auch das nicht genug ist, müssen Sie auch von den JDBC-Treiber erfolgt die interne Pufferung besiegen, mit 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

Momentan löst die Verwendung von StatelessSession das Speicherproblem, löst aber die Hölle aus, wenn auf verknüpfte OneToMany Joins zugegriffen wird (Lazy Loading). Art von einem interessanten Gewinn-verlieren. Irgendwelche Vorschläge, während ich über das Testen gehe, wenn eifriges Laden dieses löst? –

+0

Stellt sich heraus, dass StatelessSession keine Sammlungen verarbeiten kann. Ich habe einen Weg gefunden, Stateful zu benutzen und mein Gedächtnis zu kontrollieren. Siehe meine Antwort auf meine eigene Frage. –

Verwandte Themen