2012-12-06 14 views
5

GORM działa dobrze po wyjęciu z pudełka, o ile nie ma partii z więcej niż 10.000 obiektów. Bez optymalizacji napotkasz problemy OutOfMemory.Sesja hibernacji sesji w partiach

powszechnym rozwiązaniem jest przepłukać() i gładkie() sesję każdy N (EGN = 500) obiektów:

Session session = sessionFactory.currentSession 
Transaction tx = session.beginTransaction(); 
def propertyInstanceMap = org.codehaus.groovy.grails.plugins.DomainClassGrailsPlugin.PROPERTY_INSTANCE_MAP 

Date yesterday = new Date() - 1 

Criteria c = session.createCriteria(Foo.class) 
c.add(Restrictions.lt('lastUpdated',yesterday)) 
ScrollableResults rawObjects = c.scroll(ScrollMode.FORWARD_ONLY) 

int count=0; 
while (rawObjects.next()) { 
    def rawOject = rawObjects.get(0); 

    fooService.doSomething() 

    int batchSize = 500 
    if (++count % batchSize == 0) { 
     //flush a batch of updates and release memory: 
     try{ 
      session.flush(); 
     }catch(Exception e){ 
      log.error(session) 
      log.error(" error: " + e.message) 
      throw e 
     } 
     session.clear(); 
     propertyInstanceMap.get().clear() 
    } 
} 

session.flush() 
session.clear() 
tx.commit() 

jednak pewne problemy nie mogę rozwiązać:

  1. Jeśli używam currentSession, to kontroler nie powiedzie się, ponieważ sesja jest pusta
  2. Jeśli używam sessionFactory.openSession(), to currentSession jest nadal używana w FooService. Z tego powodu mogę użyć zapisu session.save (object). Ale to oznacza, że ​​muszę zmodyfikować fooService.doSomething() i zduplikować kod dla pojedynczej operacji (zwykła notacja grails taka jak fooObject.save()) i operacji wsadowej (session.save (fooObject()) .. notacja).
  3. Jeśli użyję Foo.withSession {session->} lub Foo.withNewSession {session->}, to obiekty klasy Foo zostaną wyczyszczone przez session.clear() zgodnie z oczekiwaniami. Wszystkie pozostałe obiekty nie są wyczyszczone(), co prowadzi do wycieku pamięci.
  4. Z tego powodu mogę użyć Evict (obiekt) do ręcznego wyczyszczenia sesji. Ale prawie niemożliwe jest uzyskanie wszystkich istotnych obiektów, z powodu autofetchowania zestawień.

Więc nie mam pojęcia, jak rozwiązać moje problemy, nie czyniąc FooService.doSomething() bardziej złożonym. Szukam czegoś takiego jak withSession {} dla wszystkich domen. Lub, aby zapisać sesję na początku (Sesja tmp = currentSession) i zrobić coś takiego jak sessionFactory.setCurrentSession (tmp). Oba nie istnieje!

Każdy pomysł jest mile widziany!

+2

Wygląda to na pracę, którą należy wykonać w całości metodą serwisową. Jeśli w ramach metody serwisowej używasz 'currentSession', czy twój kontroler nadal działa? – doelleri

+3

Zgadzam się z @doelleri. Usługi to miejsce wright, aby to zrobić. Pamiętaj też, że domyślnie są one transakcyjne, jeśli chcesz ręcznie obsłużyć status, użyj 'Domain.withTransaction' i ustaw' static transactional = false' lub pozwól, aby usługa zajmowała się zatwierdzaniem/wycofywaniem. –

+0

Kod, który tam zamieściłem, znajduje się już w metodzie usługi. Tak, mógłbym użyć kontekstu transakcji metody usługi, ale to nie rozwiąże mojego problemu. @doelleri - Odpowiedź na twoje pytanie brzmi: kontroler jest hamowany, więc użytkownik nie może zrobić nic więcej w aplikacji, z wyjątkiem zamknięcia przeglądarki.(patrz problem 1) – Waldemar

Odpowiedz

0

zmodyfikowane podejście do tego, co robisz będzie:

  1. Loop ciągu całej kolekcji (rawObjects) i zapisać listę wszystkich identyfikatorów dla tych obiektów.
  2. Przeprowadź pętlę nad listą identyfikatorów. Przy każdej iteracji szukaj tylko jednego obiektu, według jego id.

Następnie użyj tego samego okresowego czyszczenia pamięci podręcznej sesji, co teraz.

Przy okazji, someone else has suggested an approach similar to yours. Ale zauważ, że kod w tym linku jest nieprawidłowy; linie, które wyczyszczają sesję, powinny znajdować się wewnątrz instrukcji if, tak jak w rozwiązaniu.

Powiązane problemy