From 326426ee36bc28be3d0c71498d17d2d7743411bd Mon Sep 17 00:00:00 2001 From: Ming Date: Fri, 22 Apr 2016 22:31:22 -0400 Subject: [PATCH] Adding support for LEFT OUTER JOIN...ON queries and upgrading the Hibernate version used in the Hibernate legacy version. --- .../hibernate/HibernateQueryComposer.java | 49 +++++++++++++++++- .../jinq/hibernate/JPAJinqStreamWrapper.java | 8 +++ .../jinq/hibernate/QueryJPAJinqStream.java | 8 +++ jinq-hibernate-legacy/pom.xml | 2 +- .../test/org/jinq/hibernate/JinqJPATest.java | 51 +++++++++++++++++++ .../org/jinq/hibernate/JinqJPATestBase.java | 10 ++-- jinq-jpa/main/org/jinq/jpa/JPAJinqStream.java | 5 ++ .../org/jinq/jpa/JPAJinqStreamWrapper.java | 2 +- .../main/org/jinq/jpa/QueryJPAJinqStream.java | 8 +++ 9 files changed, 136 insertions(+), 7 deletions(-) diff --git a/jinq-hibernate-legacy/main/org/jinq/hibernate/HibernateQueryComposer.java b/jinq-hibernate-legacy/main/org/jinq/hibernate/HibernateQueryComposer.java index 45b898a6..cae37667 100644 --- a/jinq-hibernate-legacy/main/org/jinq/hibernate/HibernateQueryComposer.java +++ b/jinq-hibernate-legacy/main/org/jinq/hibernate/HibernateQueryComposer.java @@ -35,6 +35,7 @@ import org.jinq.jpa.transform.LimitSkipTransform; import org.jinq.jpa.transform.MetamodelUtil; import org.jinq.jpa.transform.MultiAggregateTransform; +import org.jinq.jpa.transform.OuterJoinOnTransform; import org.jinq.jpa.transform.OuterJoinTransform; import org.jinq.jpa.transform.QueryTransformException; import org.jinq.jpa.transform.SelectTransform; @@ -43,7 +44,9 @@ import org.jinq.orm.internal.QueryComposer; import org.jinq.orm.stream.JinqStream.AggregateGroup; import org.jinq.orm.stream.JinqStream.JoinToIterable; +import org.jinq.orm.stream.JinqStream.JoinWithSource; import org.jinq.orm.stream.JinqStream.Select; +import org.jinq.orm.stream.JinqStream.WhereForOn; import org.jinq.orm.stream.NextOnlyIterator; import org.jinq.tuples.Pair; import org.jinq.tuples.Tuple; @@ -297,6 +300,43 @@ public HibernateQueryComposer applyTransformWithLambda(JPQLOneLambdaQuery return new HibernateQueryComposer<>(this, (JPQLQuery)cachedQuery.get(), lambdas, lambdaInfo); } + public HibernateQueryComposer applyTransformWithTwoLambdas(OuterJoinOnTransform transform, Object lambda1, Object lambda2) + { + LambdaInfo lambdaInfo1 = lambdaAnalyzer.extractSurfaceInfo(lambda1, lambdas.size(), hints.dieOnError); + if (lambdaInfo1 == null) { translationFail(); return null; } + LambdaInfo lambdaInfo2 = lambdaAnalyzer.extractSurfaceInfo(lambda2, lambdas.size() + 1, hints.dieOnError); + if (lambdaInfo2 == null) { translationFail(); return null; } + Optional> cachedQuery = hints.useCaching ? + cachedQueries.findInCache(query, transform.getTransformationTypeCachingTag(), new String[] {lambdaInfo1.getLambdaSourceString(), lambdaInfo2.getLambdaSourceString()}) : null; + if (cachedQuery == null) + { + cachedQuery = Optional.empty(); + JPQLQuery newQuery = null; + try { + LambdaAnalysis lambdaAnalysis1 = lambdaInfo1.fullyAnalyze(metamodel, hints.lambdaClassLoader, hints.isObjectEqualsSafe, hints.isAllEqualsSafe, hints.isCollectionContainsSafe, hints.dieOnError); + if (lambdaAnalysis1 == null) { translationFail(); return null; } + LambdaAnalysis lambdaAnalysis2 = lambdaInfo2.fullyAnalyze(metamodel, hints.lambdaClassLoader, hints.isObjectEqualsSafe, hints.isAllEqualsSafe, hints.isCollectionContainsSafe, hints.dieOnError); + if (lambdaAnalysis2 == null) { translationFail(); return null; } + getConfig().checkLambdaSideEffects(lambdaAnalysis1); + getConfig().checkLambdaSideEffects(lambdaAnalysis2); + newQuery = transform.apply(query, lambdaAnalysis1, lambdaAnalysis2, null); + } + catch (QueryTransformException e) + { + translationFail(e); + } + finally + { + // Always cache the resulting query, even if it is an error + cachedQuery = Optional.ofNullable(newQuery); + if (hints.useCaching) + cachedQuery = cachedQueries.cacheQuery(query, transform.getTransformationTypeCachingTag(), new String[] {lambdaInfo1.getLambdaSourceString(), lambdaInfo2.getLambdaSourceString()}, cachedQuery); + } + } + if (!cachedQuery.isPresent()) { translationFail(); return null; } + return new HibernateQueryComposer<>(this, (JPQLQuery)cachedQuery.get(), lambdas, lambdaInfo1, lambdaInfo2); + } + public HibernateQueryComposer applyTransformWithLambdas(JPQLMultiLambdaQueryTransform transform, Object [] groupingLambdas) { LambdaInfo[] lambdaInfos = new LambdaInfo[groupingLambdas.length]; @@ -484,7 +524,14 @@ public QueryComposer leftOuterJoinFetchIterable( { return applyTransformWithLambda(new JoinFetchTransform(getConfig()).setIsExpectingStream(false).setIsOuterJoinFetch(true), joinLambda); } - + + @Override + public QueryComposer> leftOuterJoinWithSource( + JoinWithSource join, WhereForOn on) + { + return applyTransformWithTwoLambdas(new OuterJoinOnTransform(getConfig()).setIsExpectingStream(true), join, on); + } + @Override public Long count() { diff --git a/jinq-hibernate-legacy/main/org/jinq/hibernate/JPAJinqStreamWrapper.java b/jinq-hibernate-legacy/main/org/jinq/hibernate/JPAJinqStreamWrapper.java index 4f4a70d8..34e8f93f 100644 --- a/jinq-hibernate-legacy/main/org/jinq/hibernate/JPAJinqStreamWrapper.java +++ b/jinq-hibernate-legacy/main/org/jinq/hibernate/JPAJinqStreamWrapper.java @@ -250,6 +250,14 @@ public JPAJinqStream> leftOuterJoinList( return wrap(wrapped.leftOuterJoinList(join)); } + @Override + public JPAJinqStream> leftOuterJoin( + org.jinq.orm.stream.JinqStream.JoinWithSource join, + org.jinq.orm.stream.JinqStream.WhereForOn on) + { + return wrap(wrapped.leftOuterJoin(join, on)); + } + @Override public JPAJinqStream> group( org.jinq.orm.stream.JinqStream.Select select, diff --git a/jinq-hibernate-legacy/main/org/jinq/hibernate/QueryJPAJinqStream.java b/jinq-hibernate-legacy/main/org/jinq/hibernate/QueryJPAJinqStream.java index 4ae6a3a8..94c6316b 100644 --- a/jinq-hibernate-legacy/main/org/jinq/hibernate/QueryJPAJinqStream.java +++ b/jinq-hibernate-legacy/main/org/jinq/hibernate/QueryJPAJinqStream.java @@ -164,6 +164,14 @@ public JPAJinqStream> leftOuterJoinList( return wrap(super.leftOuterJoinList(join)); } + @Override + public JPAJinqStream> leftOuterJoin( + org.jinq.orm.stream.JinqStream.JoinWithSource join, + org.jinq.orm.stream.JinqStream.WhereForOn on) + { + return wrap(super.leftOuterJoin(join, on)); + } + @Override public JPAJinqStream> group( org.jinq.orm.stream.JinqStream.Select select, diff --git a/jinq-hibernate-legacy/pom.xml b/jinq-hibernate-legacy/pom.xml index 930f3fa7..9993bbd2 100644 --- a/jinq-hibernate-legacy/pom.xml +++ b/jinq-hibernate-legacy/pom.xml @@ -50,7 +50,7 @@ org.hibernate hibernate-entitymanager - 4.3.10.Final + 5.1.0.Final provided diff --git a/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATest.java b/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATest.java index ef190b3f..d91269f6 100644 --- a/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATest.java +++ b/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATest.java @@ -26,6 +26,7 @@ import org.jinq.orm.stream.JinqStream.Where; import org.jinq.orm.stream.QueryJinqStream; import org.jinq.tuples.Pair; +import org.junit.Assert; import org.junit.Test; public class JinqJPATest extends JinqJPATestBase @@ -211,6 +212,56 @@ public void testOuterJoin11() assertEquals(1, results.size()); } + + @Test + public void testOuterJoinOn() + { + List> results = streams.streamAll(em, Item.class) + .leftOuterJoin( + (i, source) -> source.stream(Supplier.class), + (item, supplier) -> item.getName().substring(0, 1).equals(supplier.getName().substring(0, 1))) + .toList(); + assertEquals("SELECT A, B FROM org.jinq.hibernate.test.entities.Item A LEFT OUTER JOIN org.jinq.hibernate.test.entities.Supplier B ON SUBSTRING(A.name, 0 + 1, 1 - 0) = SUBSTRING(B.name, 0 + 1, 1 - 0)", query); + Collections.sort(results, (c1, c2) -> c1.getOne().getName().compareTo(c2.getOne().getName())); + assertEquals(5, results.size()); + assertEquals("Lawnmowers", results.get(0).getOne().getName()); + Assert.assertNull(results.get(0).getTwo()); + assertEquals("Talent", results.get(2).getOne().getName()); + assertEquals("Talent Agency", results.get(2).getTwo().getName()); + } + + @Test + public void testOuterJoinOnTrueAndNavigationalLinks() + { + List> results = streams.streamAll(em, Item.class) + .leftOuterJoin( + (i, source) -> JinqStream.from(i.getSuppliers()), + (item, supplier) -> true) + .toList(); + assertEquals("SELECT A, B FROM org.jinq.hibernate.test.entities.Item A LEFT OUTER JOIN A.suppliers B", query); + assertEquals(6, results.size()); + } + + @Test + public void testOuterJoinOnWithParametersAndIndirect() + { + String match = "Screws"; + List> results = streams.streamAll(em, Item.class) + .select(i -> i.getName()) + .leftOuterJoin( + (i, source) -> source.stream(Supplier.class), + (itemName, supplier) -> itemName.equals(match)) + .toList(); + assertEquals("SELECT A.name, B FROM org.jinq.hibernate.test.entities.Item A LEFT OUTER JOIN org.jinq.hibernate.test.entities.Supplier B ON A.name = :param0", query); + Collections.sort(results, (c1, c2) -> c1.getOne().compareTo(c2.getOne())); + assertEquals(7, results.size()); + assertEquals("Lawnmowers", results.get(0).getOne()); + Assert.assertNull(results.get(0).getTwo()); + assertEquals("Screws", results.get(1).getOne()); + assertEquals("Screws", results.get(2).getOne()); + assertEquals("Screws", results.get(3).getOne()); + } + @Test public void testOuterJoinFetch() { diff --git a/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATestBase.java b/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATestBase.java index ee9ab1b4..dad9a285 100644 --- a/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATestBase.java +++ b/jinq-hibernate-legacy/test/org/jinq/hibernate/JinqJPATestBase.java @@ -38,10 +38,12 @@ public class JinqJPATestBase public static void setUpBeforeClass() throws Exception { Configuration configuration = new Configuration().configure("META-INF/hibernate.cfg.xml"); - ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() - .applySettings(configuration.getProperties()) - .build(); - sessionFactory = configuration.buildSessionFactory(serviceRegistry); +// ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder() +// .applySettings(configuration.getProperties()) +// .build(); +// sessionFactory = configuration.buildSessionFactory(serviceRegistry); + // Hibernate keeps changing how it's configured. The old way no longer works, apparently. + sessionFactory = configuration.buildSessionFactory(); streams = new JinqHibernateStreamProvider(sessionFactory); // Hibernate's ClassMetadata doesn't have as much information as the Criteria API diff --git a/jinq-jpa/main/org/jinq/jpa/JPAJinqStream.java b/jinq-jpa/main/org/jinq/jpa/JPAJinqStream.java index 05a7d22a..f752b01d 100644 --- a/jinq-jpa/main/org/jinq/jpa/JPAJinqStream.java +++ b/jinq-jpa/main/org/jinq/jpa/JPAJinqStream.java @@ -90,6 +90,11 @@ public interface JPAJinqStream extends JinqStream @Override public JPAJinqStream> leftOuterJoinList(JoinToIterable join); + @Override + public JPAJinqStream> leftOuterJoin( + JinqStream.JoinWithSource join, + JinqStream.WhereForOn on); + @Override public JPAJinqStream> group(Select select, AggregateGroup aggregate); diff --git a/jinq-jpa/main/org/jinq/jpa/JPAJinqStreamWrapper.java b/jinq-jpa/main/org/jinq/jpa/JPAJinqStreamWrapper.java index a1b94815..3fb5aa94 100644 --- a/jinq-jpa/main/org/jinq/jpa/JPAJinqStreamWrapper.java +++ b/jinq-jpa/main/org/jinq/jpa/JPAJinqStreamWrapper.java @@ -250,7 +250,7 @@ public JPAJinqStream> leftOuterJoinList( } @Override - public JinqStream> leftOuterJoin( + public JPAJinqStream> leftOuterJoin( org.jinq.orm.stream.JinqStream.JoinWithSource join, org.jinq.orm.stream.JinqStream.WhereForOn on) { diff --git a/jinq-jpa/main/org/jinq/jpa/QueryJPAJinqStream.java b/jinq-jpa/main/org/jinq/jpa/QueryJPAJinqStream.java index 3fbd21b5..2e2e4d51 100644 --- a/jinq-jpa/main/org/jinq/jpa/QueryJPAJinqStream.java +++ b/jinq-jpa/main/org/jinq/jpa/QueryJPAJinqStream.java @@ -163,6 +163,14 @@ public JPAJinqStream> leftOuterJoinList( return wrap(super.leftOuterJoinList(join)); } + @Override + public JPAJinqStream> leftOuterJoin( + org.jinq.orm.stream.JinqStream.JoinWithSource join, + org.jinq.orm.stream.JinqStream.WhereForOn on) + { + return wrap(super.leftOuterJoin(join, on)); + } + @Override public JPAJinqStream> group( org.jinq.orm.stream.JinqStream.Select select,