From b9b245df5e1356b98201823da5938c059c8b69d8 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 7 Jan 2011 22:32:50 +0100 Subject: [PATCH] Adapted newly introduced Property abstraction. Altered JpaQueryCreator to correctly add property traversals and joins based on Property. Added integration tests to verify property traversal on collections and simple properties. --- .../jpa/repository/query/JpaQueryCreator.java | 45 ++++++++++++++++--- .../jpa/repository/query/JpaQueryPart.java | 2 +- .../data/jpa/domain/sample/User.java | 22 +++++++++ .../jpa/repository/UserRepositoryTests.java | 44 +++++++++++++++++- .../jpa/repository/sample/UserRepository.java | 6 +++ 5 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index 7480cef9a..22204709d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -19,6 +19,9 @@ import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Expression; +import javax.persistence.criteria.From; +import javax.persistence.criteria.Join; +import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; @@ -28,6 +31,7 @@ import org.springframework.data.repository.query.SimpleParameterAccessor.Bindabl import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.data.repository.query.parser.Property; import org.springframework.util.Assert; @@ -155,12 +159,14 @@ public class JpaQueryCreator extends private Predicate toPredicate(Part part, Root root, BindableParameterIterator iterator) { - Expression path = root.get(part.getProperty()); + Expression path = + toExpressionRecursively(root, part.getProperty()); switch (part.getType()) { case BETWEEN: - return builder.between(root. get(part.getProperty()), + return builder.between( + root. get(part.getProperty().toDotPath()), nextAsComparable(iterator), nextAsComparable(iterator)); case GREATER_THAN: return builder.greaterThan(getComparablePath(root, part), @@ -173,11 +179,11 @@ public class JpaQueryCreator extends case IS_NOT_NULL: return root.isNotNull(); case LIKE: - return builder.like(root. get(part.getProperty()), iterator - .next().toString()); + return builder.like(root. get(part.getProperty() + .toDotPath()), iterator.next().toString()); case NOT_LIKE: return builder.not(builder.like(root. get(part - .getProperty()), iterator.next().toString())); + .getProperty().toDotPath()), iterator.next().toString())); case SIMPLE_PROPERTY: return builder.equal(path, iterator.next()); case NEGATING_SIMPLE_PROPERTY: @@ -189,6 +195,31 @@ public class JpaQueryCreator extends } + private Expression toExpressionRecursively(Path path, + Property property) { + + Path result = path.get(property.getName()); + return property.hasNext() ? toExpressionRecursively(result, + property.next()) : result; + } + + + @SuppressWarnings("unchecked") + private Expression toExpressionRecursively(From from, + Property property) { + + if (property.isCollection()) { + Join join = from.join(property.getName()); + return (Expression) (property.hasNext() ? toExpressionRecursively( + (From) join, property.next()) : join); + } else { + Path path = from.get(property.getName()); + return (Expression) (property.hasNext() ? toExpressionRecursively( + path, property.next()) : path); + } + } + + /** * Returns a path to a {@link Comparable}. * @@ -196,11 +227,11 @@ public class JpaQueryCreator extends * @param part * @return */ - @SuppressWarnings("rawtypes") + @SuppressWarnings({ "rawtypes" }) private Expression getComparablePath(Root root, Part part) { - return root.get(part.getProperty()); + return toExpressionRecursively(root, part.getProperty()); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryPart.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryPart.java index f8afec42c..35f95c99c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryPart.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryPart.java @@ -46,7 +46,7 @@ class JpaQueryPart extends Part { */ public String getQueryPart(Parameter parameter) { - return createQueryPart(getType(), getProperty(), parameter); + return createQueryPart(getType(), getProperty().toDotPath(), parameter); } diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/User.java b/src/test/java/org/springframework/data/jpa/domain/sample/User.java index 17c2678ee..738ae0086 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/User.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/User.java @@ -25,6 +25,7 @@ import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.ManyToMany; +import javax.persistence.ManyToOne; import javax.persistence.NamedQuery; @@ -56,6 +57,9 @@ public class User { @ManyToMany private Set roles; + @ManyToOne + private User manager; + /** * Creates a new empty instance of {@code User}. @@ -241,6 +245,24 @@ public class User { } + /** + * @return the manager + */ + public User getManager() { + + return manager; + } + + + /** + * @param manager the manager to set + */ + public void setManager(User manager) { + + this.manager = manager; + } + + /* * (non-Javadoc) * diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index 1dc8ad5f5..cc31e9149 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -352,7 +352,7 @@ public class UserRepositoryTests { // Persist flushTestUsers(); - // Fetches first user from .. bdatabase + // Fetches first user from database User firstReferenceUser = repository.findById(firstUser.getId()); assertEquals(firstUser, firstReferenceUser); @@ -686,6 +686,48 @@ public class UserRepositoryTests { } + @Test + public void executesQueryMethodWithDeepTraversalCorrectly() + throws Exception { + + flushTestUsers(); + + firstUser.setManager(secondUser); + thirdUser.setManager(firstUser); + repository.save(Arrays.asList(firstUser, thirdUser)); + + List result = repository.findByManagerLastname("Arrasz"); + + assertThat(result.size(), is(1)); + assertThat(result, hasItem(firstUser)); + + result = repository.findByManagerLastname("Gierke"); + assertThat(result.size(), is(1)); + assertThat(result, hasItem(thirdUser)); + } + + + @Test + public void executesFindByColleaguesLastnameCorrectly() throws Exception { + + flushTestUsers(); + + firstUser.addColleague(secondUser); + thirdUser.addColleague(firstUser); + repository.save(Arrays.asList(firstUser, thirdUser)); + + List result = + repository.findByColleaguesLastname(secondUser.getLastname()); + + assertThat(result.size(), is(1)); + assertThat(result, hasItem(firstUser)); + + result = repository.findByColleaguesLastname("Gierke"); + assertThat(result.size(), is(2)); + assertThat(result, hasItems(thirdUser, secondUser)); + } + + private Page executeSpecWithSort(Sort sort) { flushTestUsers(); diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java index 3a91181ee..9ee513e93 100644 --- a/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java +++ b/src/test/java/org/springframework/data/jpa/repository/sample/UserRepository.java @@ -184,4 +184,10 @@ public interface UserRepository extends JpaRepository, List findByLastnameNot(String lastname); + + + List findByManagerLastname(String name); + + + List findByColleaguesLastname(String lastname); }