Question

Je dois exporter grande quantité de données de base de données. Voici les classes qui représente mes données:

public class Product{
...

    @OneToMany
    @JoinColumn(name = "product_id")
    @Cascade({SAVE_UPDATE, DELETE_ORPHAN})
    List<ProductHtmlSource> htmlSources = new ArrayList<ProductHtmlSource>();

...    }

ProductHtmlSource - contient grande chaîne à l'intérieur que je réellement besoin d'exporter.

Étant donné que la taille des données exportées est plus grande que la mémoire JVM je lis mes données par morceaux. Comme ceci:

final int batchSize = 1000;      
for (int i = 0; i < 50; i++) {
  ScrollableResults iterator = getProductIterator(batchSize * i, batchSize * (i + 1));
  while (iterator.getScrollableResults().next()) {
     Product product = (Product) iterator.getScrollableResults().get(0); 
     List<String> htmls = product.getHtmlSources();
     <some processing>
  }

}

Code de getProductIterator:

public ScrollableResults getProductIterator(int offset, int limit) {
        Session session = getSession(true);
        session.setCacheMode(CacheMode.IGNORE);
        ScrollableResults iterator = session
                .createCriteria(Product.class)
                .add(Restrictions.eq("status", Product.Status.DONE))
                .setFirstResult(offset)
                .setMaxResults(limit)
                .scroll(ScrollMode.FORWARD_ONLY);
        session.flush();
        session.clear();

        return iterator;
    }

Le problème est que malgré que je séance de compensation après avoir lu de chaque bloc de données objets Product quelque part et je s'accumule suis obtenir exception OutOfMemory. Le problème n'est pas dans le bloc de traitement code même sans elle, je reçois une erreur de mémoire. La taille du lot est pas un problème puisque 1000 objets sont assis facilement dans la mémoire.

Profiler a montré que les objets en classe accumule org.hibernate.engine.StatefulPersistenceContext.

Le stacktrace:

Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:99)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:518)
    at java.lang.StringBuffer.append(StringBuffer.java:307)
    at org.hibernate.type.TextType.get(TextType.java:41)
    at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
    at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:154)
    at org.hibernate.type.AbstractType.hydrate(AbstractType.java:81)
    at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2101)
    at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1380)
    at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1308)
    at org.hibernate.loader.Loader.getRow(Loader.java:1206)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:580)
    at org.hibernate.loader.Loader.doQuery(Loader.java:701)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
    at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
    at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:36)
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:565)
    at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:63)
    at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1716)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
    at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:109)
    at org.hibernate.collection.PersistentBag.size(PersistentBag.java:225)
    **at com.rivalwatch.plum.model.Product.getHtmlSource(Product.java:76)
    at com.rivalwatch.plum.model.Product.getHtmlSourceText(Product.java:80)
    at com.rivalwatch.plum.readers.AbstractDataReader.getData(AbstractDataReader.java:64)**
Était-ce utile?

La solution

On dirait que vous appelez getProductIterator () avec le début et la fin des numéros de ligne, tandis que getProductIterator () attend la ligne de départ et un nombre de lignes. En tant que votre « limite supérieure » obtient plus vous lisez des données dans les grandes morceaux. Je pense que vous voulez dire passer batchSize comme second argument à getProductIterator ().

Autres conseils

Pas une réponse directe, mais pour ce genre de manipulation de données, j'utiliser l'interface StatelessSession .

KeithL est juste - vous passez une limite de plus en plus. Mais le casser de cette façon n'a pas de sens de toute façon. Le point entier d'un curseur de défilement vous est ce processus une ligne à la fois il n'y a donc pas besoin de le casser en morceaux. La taille d'extraction réduit les déplacements à la base de données au coût d'utilisation plus de mémoire. Le schéma général doit être:

Query q = session.createCriteria(... no offset or limit ...);
q.setCacheMode(CacheMode.IGNORE); // prevent query or second level caching
q.setFetchSize(1000);  // experiment with this to optimize performance vs. memory
ScrollableResults iterator = query.scroll(ScrollMode.FORWARD_ONLY);
while (iterator.next()) {
  Product p = (Product)iterator.get();
  ...
  session.evict(p);  // required to keep objects from accumulating in the session
}

Cela dit, l'erreur est getHtmlSources si le problème peut être sans aucun rapport avec la session / curseur / numéro de défilement. Si les chaînes html sont énormes et ils sont en cours de référence tout le temps, vous pouvez simplement être à court de mémoire contiguë.

BTW, je ne vois pas une méthode getScrollableResults sur ScrollableResults.

Au risque de paraître stupide - avez-vous envisagé de le faire d'une autre manière

?

Personnellement, j'éviter de faire le traitement par lots que « loin » de la base de données. Je ne sais pas quelle base de données que vous utilisez, mais il y a habituellement un mécanisme pour tirer efficacement un ensemble de données sur la base de données et dans un fichier même si elle implique la manipulation modérément simple sur la sortie. Les procédures stockées, les services publics d'exportation spécifiques. Enquêter sur ce qui est disponible auprès de votre fournisseur de base de données.

Pouvez-vous poster le stacktrace d'exception? Il peut être résolu en adoptant des options JVM appropriées pour GC.

Je pense que cela est lié -. Java StringBuilder énorme frais généraux

Attend du StackTrace qu'une très grande chaîne est en cours de création et de provoquer l'exception.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top