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.
This commit is contained in:
Oliver Gierke
2011-01-07 22:32:50 +01:00
parent d17aa75944
commit b9b245df5e
5 changed files with 110 additions and 9 deletions

View File

@@ -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<Object> path = root.get(part.getProperty());
Expression<Object> path =
toExpressionRecursively(root, part.getProperty());
switch (part.getType()) {
case BETWEEN:
return builder.between(root.<Comparable> get(part.getProperty()),
return builder.between(
root.<Comparable> 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.<String> get(part.getProperty()), iterator
.next().toString());
return builder.like(root.<String> get(part.getProperty()
.toDotPath()), iterator.next().toString());
case NOT_LIKE:
return builder.not(builder.like(root.<String> 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<Object> toExpressionRecursively(Path<Object> path,
Property property) {
Path<Object> result = path.get(property.getName());
return property.hasNext() ? toExpressionRecursively(result,
property.next()) : result;
}
@SuppressWarnings("unchecked")
private <T> Expression<T> toExpressionRecursively(From<?, ?> from,
Property property) {
if (property.isCollection()) {
Join<Object, Object> join = from.join(property.getName());
return (Expression<T>) (property.hasNext() ? toExpressionRecursively(
(From<?, ?>) join, property.next()) : join);
} else {
Path<Object> path = from.get(property.getName());
return (Expression<T>) (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<? extends Comparable> getComparablePath(Root<?> root,
Part part) {
return root.get(part.getProperty());
return toExpressionRecursively(root, part.getProperty());
}

View File

@@ -46,7 +46,7 @@ class JpaQueryPart extends Part {
*/
public String getQueryPart(Parameter parameter) {
return createQueryPart(getType(), getProperty(), parameter);
return createQueryPart(getType(), getProperty().toDotPath(), parameter);
}

View File

@@ -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<Role> 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)
*

View File

@@ -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<User> 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<User> 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<User> executeSpecWithSort(Sort sort) {
flushTestUsers();

View File

@@ -184,4 +184,10 @@ public interface UserRepository extends JpaRepository<User, Integer>,
List<User> findByLastnameNot(String lastname);
List<User> findByManagerLastname(String name);
List<User> findByColleaguesLastname(String lastname);
}