From 1cb2b25654a14cc16436b4a223713bc1670b0307 Mon Sep 17 00:00:00 2001 From: Per Minborg Date: Fri, 4 Sep 2020 13:10:03 +0200 Subject: [PATCH] Allow Join type to be settable, fix #54 --- .../standard/internal/StreamBuilderTest.java | 5 +- .../standard/internal/StandardRenderer.java | 3 +- .../internal/StandardJoinConfiguration.java | 62 +++++++++++++++++++ .../internal/StandardStreamConfiguration.java | 35 +++++++---- .../StandardStreamConfigurationTest.java | 13 +++- streamconfiguration/pom.xml | 5 ++ .../StreamConfiguration.java | 46 +++++++++----- .../src/main/java9/module-info.java | 1 + 8 files changed, 138 insertions(+), 32 deletions(-) create mode 100644 provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardJoinConfiguration.java diff --git a/provider/builder-standard/src/test/java/com/speedment/jpastreamer/builder/standard/internal/StreamBuilderTest.java b/provider/builder-standard/src/test/java/com/speedment/jpastreamer/builder/standard/internal/StreamBuilderTest.java index 90a3c2ec..54b8f64f 100644 --- a/provider/builder-standard/src/test/java/com/speedment/jpastreamer/builder/standard/internal/StreamBuilderTest.java +++ b/provider/builder-standard/src/test/java/com/speedment/jpastreamer/builder/standard/internal/StreamBuilderTest.java @@ -23,6 +23,7 @@ import com.speedment.jpastreamer.streamconfiguration.StreamConfiguration; import org.junit.jupiter.api.Test; +import javax.persistence.criteria.JoinType; import java.util.HashSet; import java.util.Optional; import java.util.Set; @@ -162,12 +163,12 @@ public Class entityClass() { } @Override - public Set> joins() { + public Set> joins() { return new HashSet<>(); } @Override - public StreamConfiguration joining(Field field) { + public StreamConfiguration joining(Field field, JoinType joinType) { throw new UnsupportedOperationException(); } } diff --git a/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java b/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java index 4e3f5e81..f9c0e8d7 100644 --- a/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java +++ b/provider/renderer-standard/src/main/java/com/speedment/jpastreamer/renderer/standard/internal/StandardRenderer.java @@ -65,7 +65,8 @@ public RenderResult render(final Pipeline pipeline, final StreamConfig criteria.getRoot().alias(pipeline.root().getSimpleName()); criteria.getQuery().select(criteria.getRoot()); - streamConfiguration.joins().forEach(field -> criteria.getRoot().fetch(field.columnName(), JoinType.LEFT)); + streamConfiguration.joins() + .forEach(joinConfiguration -> criteria.getRoot().fetch(joinConfiguration.field().columnName(), joinConfiguration.joinType())); criteriaMerger.merge(pipeline, criteria); diff --git a/provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardJoinConfiguration.java b/provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardJoinConfiguration.java new file mode 100644 index 00000000..d85ab2cd --- /dev/null +++ b/provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardJoinConfiguration.java @@ -0,0 +1,62 @@ +package com.speedment.jpastreamer.streamconfiguration.standard.internal; + +import com.speedment.jpastreamer.field.Field; +import com.speedment.jpastreamer.streamconfiguration.StreamConfiguration; + +import javax.persistence.criteria.JoinType; + +import static java.util.Objects.requireNonNull; + +final class StandardJoinConfiguration implements StreamConfiguration.JoinConfiguration { + + private final Field field; + private final JoinType joinType; + + StandardJoinConfiguration(final Field field, final JoinType joinType) { + this.field = requireNonNull(field); + this.joinType = requireNonNull(joinType); + } + + @Override + public Field field() { + return field; + } + + @Override + public JoinType joinType() { + return joinType; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + StandardJoinConfiguration that = (StandardJoinConfiguration) o; + + if (!field.equals(that.field)) return false; + return joinType == that.joinType; + } + + @Override + public int hashCode() { + int result = field.hashCode(); + result = 31 * result + joinType.hashCode(); + return result; + } + + @Override + public String toString() { + return label(joinType) + " on " + field.columnName(); + } + + private static String label(final JoinType joinType) { + switch (joinType) { + case INNER: return "inner join"; + case LEFT: return "left outer join"; + case RIGHT: return "right outer join"; + } + return joinType.name(); + } + +} \ No newline at end of file diff --git a/provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfiguration.java b/provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfiguration.java index 7582a2c9..52292642 100644 --- a/provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfiguration.java +++ b/provider/streamconfiguration-standard/src/main/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfiguration.java @@ -15,6 +15,7 @@ import com.speedment.jpastreamer.field.Field; import com.speedment.jpastreamer.streamconfiguration.StreamConfiguration; +import javax.persistence.criteria.JoinType; import java.util.Collections; import java.util.HashSet; import java.util.Set; @@ -25,16 +26,16 @@ public final class StandardStreamConfiguration implements StreamConfiguration { private final Class entityClass; - private final Set> joins; + private final Set> joinConfigurations; public StandardStreamConfiguration(final Class entityClass) { this.entityClass = requireNonNull(entityClass); - this.joins = Collections.emptySet(); + this.joinConfigurations = Collections.emptySet(); } - private StandardStreamConfiguration(final Class entityClass, final Set> joins) { + private StandardStreamConfiguration(final Class entityClass, final Set> joinConfigurations) { this.entityClass = entityClass; - this.joins = new HashSet<>(joins); + this.joinConfigurations = new HashSet<>(joinConfigurations); } @Override @@ -43,14 +44,16 @@ public Class entityClass() { } @Override - public Set> joins() { - return Collections.unmodifiableSet(joins); + public Set> joins() { + return Collections.unmodifiableSet(joinConfigurations); } @Override - public StreamConfiguration joining(Field field) { - final Set> newjoins = new HashSet<>(joins); - newjoins.add(field); + public StreamConfiguration joining(final Field field, final JoinType joinType) { + requireNonNull(field); + requireNonNull(joinType); + final Set> newjoins = new HashSet<>(joinConfigurations); + newjoins.add(new StandardJoinConfiguration<>(field, joinType)); return new StandardStreamConfiguration<>(entityClass, newjoins); } @@ -62,21 +65,29 @@ public boolean equals(Object o) { final StandardStreamConfiguration that = (StandardStreamConfiguration) o; if (!entityClass.equals(that.entityClass)) return false; - return joins.equals(that.joins); + return joinConfigurations.equals(that.joinConfigurations); } @Override public int hashCode() { int result = entityClass.hashCode(); - result = 31 * result + joins.hashCode(); + result = 31 * result + joinConfigurations.hashCode(); return result; } @Override public String toString() { + + final String joinText = joinConfigurations.isEmpty() + ? "" + : " joining " + joinConfigurations.stream() + .map(Object::toString) + .sorted() + .collect(Collectors.joining(", ")); + return "StandardStreamConfiguration{" + "of " + entityClass.getSimpleName() + - " joining " + joins.stream().map(Field::columnName).sorted().collect(Collectors.joining(", ")) + + joinText + '}'; } } \ No newline at end of file diff --git a/provider/streamconfiguration-standard/src/test/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfigurationTest.java b/provider/streamconfiguration-standard/src/test/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfigurationTest.java index 615dfc9e..2a1d6bce 100644 --- a/provider/streamconfiguration-standard/src/test/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfigurationTest.java +++ b/provider/streamconfiguration-standard/src/test/java/com/speedment/jpastreamer/streamconfiguration/standard/internal/StandardStreamConfigurationTest.java @@ -12,12 +12,14 @@ */ package com.speedment.jpastreamer.streamconfiguration.standard.internal; +import com.speedment.jpastreamer.field.Field; import com.speedment.jpastreamer.streamconfiguration.StreamConfiguration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Arrays; +import javax.persistence.criteria.JoinType; import java.util.Collections; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -40,9 +42,13 @@ void entityClass() { @Test void joins() { final StreamConfiguration j1 = initial.joining(Film$.title); - assertEquals(Collections.singleton(Film$.title), j1.joins()); + assertEquals(Collections.singleton(new StandardJoinConfiguration<>(Film$.title, JoinType.LEFT)), j1.joins()); final StreamConfiguration j2 = j1.joining(Film$.length); - assertEquals(Stream.of(Film$.title, Film$.length).collect(Collectors.toSet()), j2.joins()); + final Set> excpected = Stream.>of(Film$.title, Film$.length) + .map(f -> new StandardJoinConfiguration<>(f, JoinType.LEFT)) + .collect(Collectors.toSet()); + + assertEquals(excpected, j2.joins()); assertNotSame(j1, j2); } @@ -67,5 +73,6 @@ void testToString() { assertTrue(toString.contains(Film.class.getSimpleName())); assertTrue(toString.contains(Film$.title.columnName())); assertTrue(toString.contains(Film$.length.columnName())); + System.out.println(toString); } } \ No newline at end of file diff --git a/streamconfiguration/pom.xml b/streamconfiguration/pom.xml index 81e490bb..958401b2 100644 --- a/streamconfiguration/pom.xml +++ b/streamconfiguration/pom.xml @@ -46,6 +46,11 @@ field + + javax.persistence + javax.persistence-api + + diff --git a/streamconfiguration/src/main/java/com/speedment/jpastreamer/streamconfiguration/StreamConfiguration.java b/streamconfiguration/src/main/java/com/speedment/jpastreamer/streamconfiguration/StreamConfiguration.java index 838ccb19..05721753 100644 --- a/streamconfiguration/src/main/java/com/speedment/jpastreamer/streamconfiguration/StreamConfiguration.java +++ b/streamconfiguration/src/main/java/com/speedment/jpastreamer/streamconfiguration/StreamConfiguration.java @@ -15,6 +15,7 @@ import com.speedment.jpastreamer.field.Field; import com.speedment.jpastreamer.rootfactory.RootFactory; +import javax.persistence.criteria.JoinType; import java.util.ServiceLoader; import java.util.Set; @@ -49,31 +50,43 @@ public interface StreamConfiguration { * @return the fields that shall be joined in * a future stream */ - Set> joins(); + Set> joins(); /** * Creates and returns a new StreamConfiguration configured with * the provided {@code field} so that it will be - * eagerly joined when producing elements in the future Stream. + * eagerly joined when producing elements in the future Stream + * using {@link JoinType#LEFT}. *

* This prevents the N+1 problem if the field is accessed in * elements in the future Stream. *

* @param field to join - * @return this StreamConfigurationBuilder + * @return a new StreamConfiguration configured with + * the provided {@code field} so that it will be + * eagerly joined when producing elements in the future Stream + * using {@link JoinType#LEFT} */ - StreamConfiguration joining(Field field); - - // BEGIN: Mandates implementation - @Override - int hashCode(); - - @Override - boolean equals(Object obj); + default StreamConfiguration joining(Field field) { + return joining(field, JoinType.LEFT); + } - @Override - String toString(); - // END: Mandates implementation + /** + * Creates and returns a new StreamConfiguration configured with + * the provided {@code field} so that it will be + * eagerly joined when producing elements in the future Stream + * using the provided {@code joinType}. + *

+ * This prevents the N+1 problem if the field is accessed in + * elements in the future Stream. + *

+ * @param field to join + * @return a new StreamConfiguration configured with + * the provided {@code field} so that it will be + * eagerly joined when producing elements in the future Stream + * using the provided {@code joinType} + */ + StreamConfiguration joining(Field field, JoinType joinType); /** * Creates and returns a new StreamConfiguration that can be used @@ -95,4 +108,9 @@ static StreamConfiguration of(final Class entityClass) { } + interface JoinConfiguration { + Field field(); + JoinType joinType(); + } + } \ No newline at end of file diff --git a/streamconfiguration/src/main/java9/module-info.java b/streamconfiguration/src/main/java9/module-info.java index 0c43334a..27f0fabd 100644 --- a/streamconfiguration/src/main/java9/module-info.java +++ b/streamconfiguration/src/main/java9/module-info.java @@ -12,6 +12,7 @@ */ module jpastreamer.streamconfiguration { requires transitive jpastreamer.field; + requires transitive java.persistence; requires jpastreamer.rootfactory; exports com.speedment.jpastreamer.streamconfiguration;