123 votes

JPA : Quel est le modèle approprié pour l’itération sur les jeux de résultats volumineux ?

Disons que j'ai une table avec des millions de lignes. En utilisant JPA, ce est la bonne façon d'effectuer une itération sur une requête sur la table, de telle sorte que je n'ai pas tout en mémoire une Liste avec des millions d'objets?

Par exemple, je pense que la suite va exploser si la table est grande:

List<Model> models = entityManager().createQuery("from Model m", Model.class).getResultList();

for (Model model : models)
{
     System.out.println(model.getId());
}

Est pagination (en boucle et la mise à jour manuellement setFirstResult()/setMaxResult()) vraiment la meilleure solution?

Edit: le principal cas d'utilisation je suis le ciblage est une sorte de lot de travail. C'est bien si il prend du temps pour s'exécuter. Il n'y a pas de client web de cause; j'ai juste besoin de "faire quelque chose" pour chaque ligne, une (ou plusieurs petits N) à la fois. Je suis juste essayer d'éviter de les avoir tous en mémoire en même temps.

57voto

Caffeine Coma Points 10544

Page 537 de Persistance Java avec Hibernate donne une solution en utilisant `` , mais hélas, c’est seulement pour la mise en veille prolongée.

Il semble que l’utilisation de / et itération manuelle est vraiment nécessaire. Voici ma solution à l’aide de la JPA :

puis, l’utiliser comme ceci :

42voto

Zds Points 2086

J'ai essayé les réponses présentées ici, mais JBoss 5.1 + MySQL Connector/J 5.1.15 + Hibernate 3.3.2 ne fonctionne pas avec ceux-ci. Nous avons simplement migré à partir de JBoss 4.x pour JBoss 5.1, nous avons donc coincé avec elle pour l'instant, et donc la dernière Hibernate nous pouvons utiliser est 3.3.2.

L'ajout de quelques paramètres supplémentaires ont fait le travail, et le type de code s'exécute sans OOMEs:

        StatelessSession session = ((Session) entityManager.getDelegate()).getSessionFactory().openStatelessSession();

        Query query = session
                .createQuery("SELECT a FROM Address a WHERE .... ORDER BY a.id");
        query.setFetchSize(Integer.valueOf(1000));
        query.setReadOnly(true);
        query.setLockMode("a", LockMode.NONE);
        ScrollableResults results = query.scroll(ScrollMode.FORWARD_ONLY);
        while (results.next()) {
            Address addr = (Address) results.get(0);
            // Do stuff
        }
        results.close();
        session.close();

L'essentiel des lignes sont les paramètres de la requête entre createQuery et de défilement. Sans eux, le "scroll" appel tente de charge tout en mémoire et jamais terminée ou s'exécute à OutOfMemoryError.

Avis que je reçois l'EntityManager/SessionFactory via JNDI parce que nous sommes toujours en cours d'exécution sur les EJB 2.1 SessionBean ici. Toutes mes excuses, mais je voulais à présent code s'exécute et, au lieu d'essayer de deviner comment il peut fonctionner dans certains autres cas.

32voto

Cyberax Points 938

Cela vous est impossible vraiment à droite JPA, toutefois Hibernate a prise en charge pour les sessions sans état et ensembles de jeux de résultats déroulables.

Nous traitons régulièrement des milliards de lignes avec son aide.

Voici un lien vers la documentation : http://docs.jboss.org/hibernate/core/3.3/reference/en/html/batch.html#batch-statelesssession

19voto

Tomasz Nurkiewicz Points 140462

Pour être honnête, je suggère de laisser JPA et le bâton avec JDBC (mais certainement l'aide d' JdbcTemplate classe de soutien, ou autres). JPA (et d'autres ORM/fournisseurs de spécifications) n'est pas conçu pour fonctionner sur de nombreux objets à l'intérieur d'une transaction, car ils supposent que tout chargé doit rester dans le cache de premier niveau (d'où la nécessité d' clear() en JPA).

Aussi je recommande plus bas niveau de la solution parce que les frais généraux de l'ORM (réflexion n'en est qu'une partie émergée de l'iceberg) pourrait être tellement important, que l'itération sur la plaine ResultSet, même en utilisant certains soutien léger comme mentionné JdbcTemplate sera beaucoup plus rapide.

JPA est tout simplement pas conçu pour effectuer des opérations sur un grand nombre d'entités. Vous pouvez jouer avec flush()/clear() pour éviter OutOfMemoryError, mais considérez ceci une fois de plus. Vous gagnez très peu de payer le prix de l'énorme consommation de ressources.

5voto

frm Points 1398

Il dépend du type d'opération que vous avez à faire. Pourquoi êtes-vous en boucle de plus d'un million de ligne? Êtes-vous la mise à jour de quelque chose en mode batch? Allez-vous afficher tous les enregistrements à un client? Êtes-vous le calcul des statistiques sur l'extrait entités?

Si vous allez à l'affichage d'un million d'enregistrements pour le client, veuillez revoir votre interface utilisateur. Dans ce cas, la solution la plus appropriée est la pagination de vos résultats et de l'aide d' setFirstResult() et setMaxResult().

Si vous avez lancé une mise à jour d'un grand nombre d'enregistrements, vous aurez de mieux conserver la mise à jour simple et utiliser Query.executeUpdate(). En option, vous pouvez exécuter la mise à jour en mode asynchrone à l'aide d'un Message-Driven Bean o un Travail de Gestionnaire.

Si vous êtes le calcul des statistiques sur l'extrait d'entités, vous pouvez prendre l'avantage sur le regroupement des fonctions définies par la spécification JPA.

Pour les autres cas, veuillez être plus précis :)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X