This is an automated email from the git hooks/post-receive script. New commit to branch feature/658_use_hibernate_streaming in repository topia. See https://gitlab.nuiton.org/nuiton/topia.git commit b5d7179e6b94cecddbc9f54004821435a8f5bc46 Author: Brendan Le Ny <bleny@codelutin.com> Date: Mon Jan 8 14:59:55 2018 +0100 Enable streaming results Introduce both: - TopiaQueryBuilderRunQueryStep#stream as a public API - AbstractTopiaDao#stream as a protected helper in DAO implementations https://gitlab.nuiton.org/nuiton/topia/issues/658 --- .../topia/it/legacy/TopiaJpaSupportTest.java | 32 +++++++++++++++ .../persistence/TopiaQueryBuilderRunQueryStep.java | 12 ++++++ .../persistence/internal/AbstractTopiaDao.java | 25 ++++++++++++ .../internal/support/HibernateTopiaJpaSupport.java | 47 +++++++++++++++++++++- .../topia/persistence/support/TopiaJpaSupport.java | 13 ++++++ 5 files changed, 127 insertions(+), 2 deletions(-) diff --git a/topia-it/src/test/java/org/nuiton/topia/it/legacy/TopiaJpaSupportTest.java b/topia-it/src/test/java/org/nuiton/topia/it/legacy/TopiaJpaSupportTest.java index 7c5857aa..6c88259a 100644 --- a/topia-it/src/test/java/org/nuiton/topia/it/legacy/TopiaJpaSupportTest.java +++ b/topia-it/src/test/java/org/nuiton/topia/it/legacy/TopiaJpaSupportTest.java @@ -24,6 +24,8 @@ package org.nuiton.topia.it.legacy; * #L% */ +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; import org.apache.commons.lang3.reflect.FieldUtils; import org.junit.Assert; @@ -43,6 +45,8 @@ import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** * Tests the TopiaContext#find|findAll|findUnique methods @@ -130,6 +134,34 @@ public class TopiaJpaSupportTest { } @Test + public void testStream() { + Preconditions.checkState(personneDao.count() == 3); + + String query = "from " + Personne.class.getName() + + " where " + Personne.PROPERTY_GENDER + "=:g"; + try (Stream<Personne> stream = jpaSupport.stream(query, ImmutableMap.of("g", Gender.FEMALE))) { + List females = stream.collect(Collectors.toList()); + Assert.assertEquals(2, females.size()); + } + + try (Stream<Personne> stream = jpaSupport.stream(query, ImmutableMap.of("g", Gender.MALE))) { + List males = stream.collect(Collectors.toList()); + Assert.assertEquals(1, males.size()); + } + + try (Stream<Personne> stream = jpaSupport.stream("from " + Personne.class.getName(), ImmutableMap.of())) { + List all = stream.collect(Collectors.toList()); + Assert.assertEquals(3, all.size()); + } + + try (Stream<Personne> stream = jpaSupport.stream("from " + Personne.class.getName() + + " where " + Personne.PROPERTY_NAME + "=:pax", ImmutableMap.of("pax", "nobody"))) { + List none = stream.collect(Collectors.toList()); + Assert.assertEquals(0, none.size()); + } + } + + @Test public void testFind() throws TopiaException { Assert.assertEquals(3, personneDao.count()); diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaQueryBuilderRunQueryStep.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaQueryBuilderRunQueryStep.java index 26aef9c8..e6e9386e 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaQueryBuilderRunQueryStep.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaQueryBuilderRunQueryStep.java @@ -29,6 +29,7 @@ import org.nuiton.util.pagination.PaginationParameter; import org.nuiton.util.pagination.PaginationResult; import java.util.List; +import java.util.stream.Stream; /** * This interface represents different common operations that a user may do after a query is defined (using the @@ -127,6 +128,17 @@ public interface TopiaQueryBuilderRunQueryStep<E extends TopiaEntity> List<E> findAll(); /** + * Like {@link #findAll()} but getting a stream that may lazily fetch data. + * + * Actual behavior rely on implementation. + * + * Caller should {@link Stream#close()} the stream. + * + * @since 3.4 + */ + Stream<E> stream(); + + /** * Get all the elements in a lazy loading list. The entities will be loaded gradually when the returned Iterable is * iterated. * diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/AbstractTopiaDao.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/AbstractTopiaDao.java index 7f44b5da..8974c3bf 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/AbstractTopiaDao.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/AbstractTopiaDao.java @@ -73,6 +73,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.stream.Stream; /** * <p> @@ -567,6 +568,20 @@ public abstract class AbstractTopiaDao<E extends TopiaEntity> implements TopiaDa return result; } + protected <O> Stream<O> stream(String hql) { + Preconditions.checkNotNull(hql); + Map<String, Object> hqlParameters = Collections.emptyMap(); + Stream<O> result = stream(hql, hqlParameters); + return result; + } + + protected <O> Stream<O> stream(String hql, Map<String, Object> hqlParameters) { + Preconditions.checkNotNull(hql); + Preconditions.checkNotNull(hqlParameters); + Stream<O> result = topiaJpaSupport.stream(hql, hqlParameters); + return result; + } + protected <O> List<O> find(String hql, int startIndex, int endIndex) { Preconditions.checkNotNull(hql); Map<String, Object> hqlParameters = Collections.emptyMap(); @@ -1030,6 +1045,11 @@ public abstract class AbstractTopiaDao<E extends TopiaEntity> implements TopiaDa } @Override + public Stream<E> stream() { + return getNextStep().stream(); + } + + @Override public List<E> find(int startIndex, int endIndex) { return getNextStep().find(startIndex, endIndex); } @@ -1199,6 +1219,11 @@ public abstract class AbstractTopiaDao<E extends TopiaEntity> implements TopiaDa } @Override + public Stream<E> stream() { + return topiaDao.stream(hql, hqlParameters); + } + + @Override public Iterable<E> findAllLazy() { return topiaDao.findAllLazy(hql + (fromHql || withOrderByClause ? "" : " ORDER BY id "), hqlParameters); } diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/support/HibernateTopiaJpaSupport.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/support/HibernateTopiaJpaSupport.java index b5d6b3f0..3ea2ac25 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/support/HibernateTopiaJpaSupport.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/internal/support/HibernateTopiaJpaSupport.java @@ -26,7 +26,6 @@ package org.nuiton.topia.persistence.internal.support; import com.google.common.base.Preconditions; import com.google.common.collect.Iterables; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.hibernate.FlushMode; @@ -41,8 +40,11 @@ import org.nuiton.topia.persistence.support.TopiaHibernateSupport; import org.nuiton.topia.persistence.support.TopiaJpaSupport; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import java.util.stream.Stream; /** * This class is the Hibernate implementation of TopiaJpaSupport. It realizes the bridge between the JPA specification @@ -127,7 +129,7 @@ public class HibernateTopiaJpaSupport implements TopiaJpaSupport { Query query = prepareQuery(jpaql, parameters); List result = query.list(); - result = firesSupport.fireEntitiesLoad(this, result); + result = firesSupport.fireEntitiesLoad(this, result); // FIXME brendan 08/01/18 unchecked assignment warn is legit: we can't be sure it topia entities, it may be any object return result; } catch (MultipleBagFetchException mbfe) { throw new TopiaQueryException( @@ -150,6 +152,47 @@ public class HibernateTopiaJpaSupport implements TopiaJpaSupport { } } + /** + * Like {@link #findAll(String, Map)} but getting a stream that may lazily fetch data. + * + * Actual behavior rely on JPA implementation. + * + * According to {@link Query#stream()}, caller should {@link Stream#close()} the stream. + * + * @since 3.4 + */ + @Override + public <T> Stream<T> stream(String jpaql, Map<String, Object> parameters) { + try { + Query query = prepareQuery(jpaql, parameters); + + Stream result = query.stream(); + Consumer<T> consumer = row -> { + List singleton = Collections.singletonList(row); + firesSupport.fireEntitiesLoad(this, singleton); // FIXME brendan 08/01/18 unchecked assignment warn is legit: we can't be sure it topia entities, it may be any object + }; + return result.peek(consumer); + } catch (MultipleBagFetchException mbfe) { + throw new TopiaQueryException( + "unable to fetch multiple bags during " + mbfe.getBagRoles(), + mbfe, + jpaql, + parameters); + } catch (HibernateException he) { + throw new TopiaQueryException( + "unable to stream", + he, + jpaql, + parameters); + } catch (RuntimeException re) { + throw new TopiaQueryException( + "unable to stream", + re, + jpaql, + parameters); + } + } + @Override public <T> T findAny(String jpaql, Map<String, Object> parameters) { diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/support/TopiaJpaSupport.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/support/TopiaJpaSupport.java index e7de7314..bc49bb91 100644 --- a/topia-persistence/src/main/java/org/nuiton/topia/persistence/support/TopiaJpaSupport.java +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/support/TopiaJpaSupport.java @@ -28,6 +28,7 @@ import org.nuiton.topia.persistence.QueryMissingOrderException; import java.util.List; import java.util.Map; +import java.util.stream.Stream; /** * This API provides methods to use persistence using JPA queries @@ -51,6 +52,18 @@ public interface TopiaJpaSupport { <T> List<T> findAll(String jpaql, Map<String, Object> parameters); /** + * Streaming on the result of a {@link #findAll}. + * + * Streaming is not part of JPA but may be part a JPA implementation so we provide + * a default implementation that will work on any JPA vendor. + * + * @since 3.4 + */ + default <T> Stream<T> stream(String jpaql, Map<String, Object> parameters) { + return this.<T>findAll(jpaql, parameters).stream(); + } + + /** * Allow to do some JPA-QL query using the given bounds. * <ul> * <li>No lower bound : {@code startIndex = 0}.</li> -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.