DATAJPA-12 - Added Sort implementations for JPA meta-model API and Querydsl.
Introduced JpaSort for sorting by JPA meta-model attribute paths. Introduced JpaMetaModelPathBuilder that can be used to ease the construction of Jpa meta-model attribute paths by the provided static factory method. Added new testing scenario (MailMessage and MailSender) to avoid to mess up the existing sample classes. Enabled static JPA meta-model generation in pom.xml. Enhanced Querydsl to generate appropriate left joins when sorting by nested (singular) association properties. Converted XML configuration for QueryDslRepositorySupportIntegrationTests into JavaConfig. Original pull request: #54.
This commit is contained in:
committed by
Oliver Gierke
parent
e51add8c76
commit
692b2382bf
65
pom.xml
65
pom.xml
@@ -27,7 +27,7 @@
|
||||
<hsqldb1>1.8.0.10</hsqldb1>
|
||||
<jpa>2.0.0</jpa>
|
||||
<openjpa>2.2.1</openjpa>
|
||||
<springdata.commons>1.7.0.M1</springdata.commons>
|
||||
<springdata.commons>1.7.0.BUILD-SNAPSHOT</springdata.commons>
|
||||
|
||||
</properties>
|
||||
|
||||
@@ -413,6 +413,69 @@
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>wagon-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!-- configuration for JPA Metamodel generation
|
||||
as described here: http://stackoverflow.com/questions/18853585/maven-build-with-annotationprocessor-that-parses-files-in-src-main-java-and-gene
|
||||
-->
|
||||
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<!-- normal compile and generation of other classes to standard location (implicit, you shouldn't need that) -->
|
||||
<id>default-compile</id>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<annotationProcessors>
|
||||
<annotationProcessor>org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor</annotationProcessor>
|
||||
</annotationProcessors>
|
||||
<generatedSourcesDirectory>${project.build.directory}/generated-sources/test</generatedSourcesDirectory>
|
||||
<!-- generated class depends on test-scope libs, so don't compile now: proc:only DISABLES compilation of generated classes-->
|
||||
<proc>only</proc>
|
||||
</configuration>
|
||||
</execution>
|
||||
<!-- implicit test-compile:testCompile -->
|
||||
</executions>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-jpamodelgen</artifactId>
|
||||
<version>1.2.0.Final</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<!-- adds source-dir during generate-test-sources:add-test-source
|
||||
so that the path to our generated class is now known to the
|
||||
compiler during test-compile:testCompile -->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<version>1.8</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-test-source</id>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>add-test-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>${project.build.directory}/generated-sources/test</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
114
src/main/java/org/springframework/data/jpa/domain/JpaSort.java
Normal file
114
src/main/java/org/springframework/data/jpa/domain/JpaSort.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.domain;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Sort option for queries that wraps JPA MetaModel {@link Expression}s for sorting.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class JpaSort extends Sort {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaSort} instance with the given {@link Path}s.
|
||||
*
|
||||
* @param jpaPaths must not be {@literal null} or empty.
|
||||
*/
|
||||
public JpaSort(Path<?>... jpaPaths) {
|
||||
this(Arrays.asList(jpaPaths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaSort} instance with the given {@link Path}s.
|
||||
*
|
||||
* @param direction
|
||||
* @param jpaPaths must not be {@literal null} or empty.
|
||||
*/
|
||||
public JpaSort(Direction direction, Path<?>... jpaPaths) {
|
||||
this(direction, Arrays.asList(jpaPaths));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaSort} instance with the given {@link Path}s.
|
||||
*
|
||||
* @param jpaPaths must not be {@literal null} or empty.
|
||||
*/
|
||||
public JpaSort(List<Path<?>> jpaPaths) {
|
||||
this(DEFAULT_DIRECTION, jpaPaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaSort} instance with the given {@link Path}s.
|
||||
*
|
||||
* @param direction
|
||||
* @param jpaPaths must not be {@literal null} or empty.
|
||||
*/
|
||||
public JpaSort(Direction direction, List<Path<?>> jpaPaths) {
|
||||
super(direction, toPropertyPaths(jpaPaths));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jpaPaths must not be {@literal null} or empty.
|
||||
* @return
|
||||
*/
|
||||
private static List<String> toPropertyPaths(List<Path<?>> jpaPaths) {
|
||||
|
||||
Assert.notEmpty(jpaPaths, "Jpa orders must not be null or empty!");
|
||||
|
||||
List<String> propertyPaths = new ArrayList<String>();
|
||||
|
||||
for (Path<?> path : jpaPaths) {
|
||||
propertyPaths.add(toPropertyPath(path));
|
||||
}
|
||||
|
||||
return propertyPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static String toPropertyPath(Path path) {
|
||||
|
||||
StringBuilder attributePath = new StringBuilder();
|
||||
Path current = path;
|
||||
while (!(current instanceof Root)) {
|
||||
String attributePathSegment = ((SingularAttribute) current.getModel()).getName();
|
||||
if (attributePath.length() > 0) {
|
||||
attributePath.insert(0, ".");
|
||||
}
|
||||
attributePath.insert(0, attributePathSegment);
|
||||
current = current.getParentPath();
|
||||
}
|
||||
|
||||
return attributePath.toString();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.querydsl.EntityPathResolver;
|
||||
import org.springframework.data.querydsl.QSort;
|
||||
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
|
||||
import org.springframework.data.querydsl.SimpleEntityPathResolver;
|
||||
|
||||
@@ -99,7 +100,10 @@ public class QueryDslJpaRepository<T, ID extends Serializable> extends SimpleJpa
|
||||
* @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, com.mysema.query.types.OrderSpecifier<?>[])
|
||||
*/
|
||||
public List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders) {
|
||||
return createQuery(predicate).orderBy(orders).list(path);
|
||||
|
||||
JPQLQuery query = createQuery(predicate);
|
||||
query = querydsl.applySorting(new QSort(orders), query);
|
||||
return query.list(path);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2012 the original author or authors.
|
||||
* Copyright 2012-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,7 +15,9 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
@@ -25,6 +27,7 @@ import javax.persistence.metamodel.EntityType;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.querydsl.QSort;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import com.mysema.query.jpa.EclipseLinkTemplates;
|
||||
@@ -35,6 +38,7 @@ import com.mysema.query.jpa.impl.JPAQuery;
|
||||
import com.mysema.query.types.EntityPath;
|
||||
import com.mysema.query.types.Expression;
|
||||
import com.mysema.query.types.OrderSpecifier;
|
||||
import com.mysema.query.types.Path;
|
||||
import com.mysema.query.types.path.EntityPathBase;
|
||||
import com.mysema.query.types.path.PathBuilder;
|
||||
|
||||
@@ -42,6 +46,7 @@ import com.mysema.query.types.path.PathBuilder;
|
||||
* Helper instance to ease access to Querydsl JPA query API.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class Querydsl {
|
||||
|
||||
@@ -126,8 +131,87 @@ public class Querydsl {
|
||||
return query;
|
||||
}
|
||||
|
||||
if (sort instanceof QSort) {
|
||||
return addOrderByFrom((QSort) sort, query);
|
||||
}
|
||||
|
||||
return addOrderByFrom(sort, query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the given {@link OrderSpecifier}s to the given {@link JPQLQuery}. Potentially transforms the given
|
||||
* {@code OrderSpecifier}s to be able to injection potentially necessary left-joins.
|
||||
*
|
||||
* @param qsort must not be {@literal null}.
|
||||
* @param query must not be {@literal null}.
|
||||
*/
|
||||
|
||||
private JPQLQuery addOrderByFrom(QSort qsort, JPQLQuery query) {
|
||||
return query.orderBy(adjustOrderSpecifierIfNecessary(qsort.getOrderSpecifiers(), query));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the given {@link OrderSpecifier} if necessary, e.g. generates proper aliases and left-joins to be created
|
||||
* if we detect ordering by an nested attribute.
|
||||
*
|
||||
* @param originalOrderSpecifiers must not be {@literal null}.
|
||||
* @param query must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private OrderSpecifier<?>[] adjustOrderSpecifierIfNecessary(List<OrderSpecifier<?>> originalOrderSpecifiers,
|
||||
JPQLQuery query) {
|
||||
|
||||
Assert.notNull(originalOrderSpecifiers, "Original order specifiers must not be null!");
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
boolean orderModificationNecessary = false;
|
||||
List<OrderSpecifier<?>> modifiedOrderSpecifiers = new ArrayList<OrderSpecifier<?>>();
|
||||
|
||||
for (OrderSpecifier<?> order : originalOrderSpecifiers) {
|
||||
|
||||
Path targetPath = ((Path) order.getTarget()).getMetadata().getParent();
|
||||
|
||||
boolean targetPathRootIsEntityRoot = targetPath.getRoot().equals(builder.getRoot());
|
||||
boolean targetPathEqualsRootEnityPath = targetPath.toString().equals(builder.toString());
|
||||
|
||||
if (!targetPathRootIsEntityRoot) {
|
||||
|
||||
query.leftJoin((EntityPath) builder.get((String) targetPath.getMetadata().getElement()), targetPath);
|
||||
} else if (targetPathRootIsEntityRoot && !targetPathEqualsRootEnityPath) {
|
||||
|
||||
PathBuilder joinPathBuilder = new PathBuilder(targetPath.getType(), targetPath.getMetadata().getElement()
|
||||
.toString());
|
||||
query.leftJoin((EntityPath) targetPath, joinPathBuilder);
|
||||
OrderSpecifier<?> modifiedOrder = new OrderSpecifier(order.getOrder(), joinPathBuilder.get(((Path) order
|
||||
.getTarget()).getMetadata().getElement().toString()), order.getNullHandling());
|
||||
modifiedOrderSpecifiers.add(modifiedOrder);
|
||||
orderModificationNecessary = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
modifiedOrderSpecifiers.add(order);
|
||||
}
|
||||
|
||||
return orderModificationNecessary ? modifiedOrderSpecifiers.toArray(new OrderSpecifier<?>[modifiedOrderSpecifiers
|
||||
.size()]) : originalOrderSpecifiers.toArray(new OrderSpecifier<?>[originalOrderSpecifiers.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the {@link Order} items of the given {@link Sort} into {@link OrderSpecifier} and attaches those to the
|
||||
* given {@link JPQLQuery}.
|
||||
*
|
||||
* @param sort must not be {@literal null}.
|
||||
* @param query must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private JPQLQuery addOrderByFrom(Sort sort, JPQLQuery query) {
|
||||
|
||||
Assert.notNull(sort, "Sort must not be null!");
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
for (Order order : sort) {
|
||||
query.orderBy(toOrder(order, query));
|
||||
query.orderBy(toOrderSpecifier(order, query));
|
||||
}
|
||||
|
||||
return query;
|
||||
@@ -140,7 +224,7 @@ public class Querydsl {
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private OrderSpecifier<?> toOrder(Order order, JPQLQuery query) {
|
||||
private OrderSpecifier<?> toOrderSpecifier(Order order, JPQLQuery query) {
|
||||
|
||||
Expression<?> property = createExpressionAndPotentionallyAddLeftJoinForReferencedAssociation(order, query);
|
||||
|
||||
@@ -190,20 +274,29 @@ public class Querydsl {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param attribute
|
||||
* @param order
|
||||
* @param query
|
||||
* Adds a left-join to the given {@link JPQLQuery} with a proper alias for the property referenced on the given
|
||||
* {@link Order} relative to the given parent {@link Attribute}.
|
||||
*
|
||||
* @param parentAttribute must not be {@literal null}.
|
||||
* @param order must not be {@literal null}.
|
||||
* @param query must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private Expression<?> createLeftJoinForAttributeInOrderBy(Attribute<?, ?> attribute, Order order, JPQLQuery query) {
|
||||
private Expression<?> createLeftJoinForAttributeInOrderBy(Attribute<?, ?> parentAttribute, Order order,
|
||||
JPQLQuery query) {
|
||||
|
||||
EntityPathBase<?> associationPathRoot = new EntityPathBase<Object>(attribute.getJavaType(), attribute.getName());
|
||||
query.leftJoin((EntityPath) builder.get(attribute.getName()), associationPathRoot);
|
||||
PathBuilder<Object> attributePathBuilder = new PathBuilder<Object>(attribute.getJavaType(),
|
||||
Assert.notNull(parentAttribute, "Attribute must not be null!");
|
||||
Assert.notNull(order, "Order must not be null!");
|
||||
Assert.notNull(query, "Query must not be null!");
|
||||
|
||||
EntityPathBase<?> associationPathRoot = new EntityPathBase<Object>(parentAttribute.getJavaType(),
|
||||
parentAttribute.getName());
|
||||
query.leftJoin((EntityPath) builder.get(parentAttribute.getName()), associationPathRoot);
|
||||
PathBuilder<Object> attributePathBuilder = new PathBuilder<Object>(parentAttribute.getJavaType(),
|
||||
associationPathRoot.getMetadata());
|
||||
|
||||
String nestedAttributePath = order.getProperty().substring(attribute.getName().length() + 1); // exclude "."
|
||||
String nestedAttributePath = order.getProperty().substring(parentAttribute.getName().length() + 1); // exclude "."
|
||||
return order.isIgnoreCase() ? attributePathBuilder.getString(nestedAttributePath).lower() : attributePathBuilder
|
||||
.get(nestedAttributePath);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.support;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Root;
|
||||
import javax.persistence.metamodel.SingularAttribute;
|
||||
|
||||
import org.springframework.data.jpa.domain.JpaSort;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Builder for building JPA meta-model access {@link Path}s that can be used as Path expressions e.g. in {@link JpaSort}
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public class JpaMetaModelPathBuilder<T, A> {
|
||||
|
||||
private final Class<?> rootType;
|
||||
private final List<SingularAttribute<?, ?>> currentPathSegments;
|
||||
|
||||
/**
|
||||
* Static factory method to create a {@link JpaMetaModelPathBuilder} from the given root {@link SingularAttribute}.
|
||||
*
|
||||
* @param attribute the root attribute to use must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public static <T, A> JpaMetaModelPathBuilder<T, A> path(SingularAttribute<T, A> attribute) {
|
||||
return new JpaMetaModelPathBuilder<T, A>(attribute.getDeclaringType().getJavaType(), attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaMetaModelPathBuilder}.
|
||||
*
|
||||
* @param rootType the root type of the expression must not be {@literal null}.
|
||||
* @param attribute must not be {@literal null}.
|
||||
*/
|
||||
private JpaMetaModelPathBuilder(Class<?> rootType, SingularAttribute<T, A> attribute) {
|
||||
this(rootType, attribute, new ArrayList<SingularAttribute<?, ?>>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaMetaModelPathBuilder}.
|
||||
*
|
||||
* @param rootType the root type of the expression must not be {@literal null}.
|
||||
* @param attribute must not be {@literal null}
|
||||
* @param pathSegments must not be {@literal null}
|
||||
*/
|
||||
private JpaMetaModelPathBuilder(Class<?> rootType, SingularAttribute<T, A> attribute,
|
||||
List<SingularAttribute<?, ?>> pathSegments) {
|
||||
|
||||
Assert.notNull(rootType, "Root type must not be null!");
|
||||
Assert.notNull(attribute, "Attribute must not be null!");
|
||||
Assert.notNull(pathSegments, "Path segments must not be null!");
|
||||
|
||||
this.rootType = rootType;
|
||||
this.currentPathSegments = pathSegments;
|
||||
this.currentPathSegments.add(attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new {@link JpaMetaModelPathBuilder} instance with the given nested {@link SingularAttribute} attached.
|
||||
*
|
||||
* @param nestedAttribute must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
public <NA> JpaMetaModelPathBuilder<A, NA> get(SingularAttribute<A, NA> nestedAttribute) {
|
||||
return new JpaMetaModelPathBuilder<A, NA>(rootType, nestedAttribute, new ArrayList<SingularAttribute<?, ?>>(
|
||||
currentPathSegments));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a {@link Path} from the collected {@link SingularAttribute}s.
|
||||
*
|
||||
* @param em must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Path<A> build(EntityManager em) {
|
||||
|
||||
Assert.notNull(em, "EntityManager must nut be null");
|
||||
|
||||
Root<?> root = em.getCriteriaBuilder().createQuery().from(rootType);
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
Path path = root;
|
||||
for (SingularAttribute<?, ?> attribute : currentPathSegments) {
|
||||
path = path.get(attribute);
|
||||
}
|
||||
|
||||
return (Path<A>) path;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.domain;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.jpa.support.JpaMetaModelPathBuilder.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Path;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.ImportResource;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.jpa.domain.sample.MailMessage_;
|
||||
import org.springframework.data.jpa.domain.sample.MailSender_;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.domain.sample.User_;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
/**
|
||||
* Unit test for {@link JpaSort}.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration
|
||||
public class JpaSortTests {
|
||||
|
||||
@Configuration
|
||||
@ImportResource("classpath:infrastructure.xml")
|
||||
static class Config {}
|
||||
|
||||
@PersistenceContext EntityManager em;
|
||||
|
||||
private static final MailMessage_ jmail = null;
|
||||
private static final MailSender_ jsender = null;
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowIfNoOrderSpecifiersAreGiven() {
|
||||
new JpaSort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void shouldThrowIfNullIsGiven() {
|
||||
new JpaSort((List<Path<?>>) null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void sortBySinglePropertyWithDefaultSortDirection() {
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<User> q = cb.createQuery(User.class);
|
||||
Root<User> c = q.from(User.class);
|
||||
|
||||
JpaSort sort = new JpaSort(c.get("firstname"));
|
||||
|
||||
assertThat(sort, hasItems(new Sort.Order("firstname")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void sortByMultiplePropertiesWithDefaultSortDirection() {
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<User> q = cb.createQuery(User.class);
|
||||
Root<User> c = q.from(User.class);
|
||||
|
||||
JpaSort sort = new JpaSort(c.get("firstname"), c.get("lastname"));
|
||||
assertThat(sort, hasItems(new Sort.Order("firstname"), new Sort.Order("lastname")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void sortByMultiplePropertiesWithDescSortDirection() {
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<User> q = cb.createQuery(User.class);
|
||||
Root<User> c = q.from(User.class);
|
||||
|
||||
JpaSort sort = new JpaSort(Direction.DESC, c.get("firstname"), c.get("lastname"));
|
||||
|
||||
assertThat(sort, hasItems(new Sort.Order(Direction.DESC, "firstname"), new Sort.Order(Direction.DESC, "lastname")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void combiningSortByMultipleProperties() {
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<User> q = cb.createQuery(User.class);
|
||||
Root<User> c = q.from(User.class);
|
||||
|
||||
Sort sort = new JpaSort(c.get("firstname")).and(new JpaSort(c.get("lastname")));
|
||||
|
||||
assertThat(sort, hasItems(new Sort.Order("firstname"), new Sort.Order("lastname")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void combiningSortByMultiplePropertiesWithDifferentSort() {
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<User> q = cb.createQuery(User.class);
|
||||
Root<User> c = q.from(User.class);
|
||||
|
||||
Sort sort = new JpaSort(c.get("firstname")).and(new JpaSort(Direction.DESC, c.get("lastname")));
|
||||
|
||||
assertThat(sort, hasItems(new Sort.Order("firstname"), new Sort.Order(Direction.DESC, "lastname")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void combiningSortByNestedEmbeddedProperty() {
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<User> q = cb.createQuery(User.class);
|
||||
Root<User> c = q.from(User.class);
|
||||
|
||||
Sort sort = new JpaSort(c.get("address").get("streetName"));
|
||||
|
||||
assertThat(sort, hasItems(new Sort.Order("address.streetName")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void buildJpaSortFromJpaMetaModelSingleAttribute() {
|
||||
|
||||
Sort sort = new JpaSort(Direction.ASC, path(User_.firstname).build(em));
|
||||
|
||||
assertThat(sort, hasItems(new Sort.Order("firstname")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void buildJpaSortFromJpaMetaModelNestedAttribute() {
|
||||
|
||||
Sort sort = new JpaSort(Direction.ASC, path(MailMessage_.mailSender).get(MailSender_.name).build(em));
|
||||
|
||||
assertThat(sort, hasItems(new Sort.Order("mailSender.name")));
|
||||
}
|
||||
}
|
||||
59
src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java
Executable file
59
src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java
Executable file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.domain.sample;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@Entity
|
||||
public class MailMessage {
|
||||
|
||||
@Id @GeneratedValue private Long id;
|
||||
|
||||
@OneToOne(cascade = CascadeType.ALL) private MailSender mailSender;
|
||||
|
||||
private String content;
|
||||
|
||||
public MailSender getMailSender() {
|
||||
return mailSender;
|
||||
}
|
||||
|
||||
public void setMailSender(MailSender sender) {
|
||||
this.mailSender = sender;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.domain.sample;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@Entity
|
||||
public class MailSender {
|
||||
|
||||
@Id @GeneratedValue private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public MailSender() {}
|
||||
|
||||
public MailSender(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#hashCode()
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((name == null) ? 0 : name.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
MailSender other = (MailSender) obj;
|
||||
if (name == null) {
|
||||
if (other.name != null)
|
||||
return false;
|
||||
} else if (!name.equals(other.name))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -64,10 +64,7 @@ public class User {
|
||||
* Creates a new empty instance of {@code User}.
|
||||
*/
|
||||
public User() {
|
||||
|
||||
this.roles = new HashSet<Role>();
|
||||
this.colleagues = new HashSet<User>();
|
||||
this.createdAt = new Date();
|
||||
this(null, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,13 +74,15 @@ public class User {
|
||||
* @param lastname
|
||||
* @param emailAddress
|
||||
*/
|
||||
public User(final String firstname, final String lastname, final String emailAddress) {
|
||||
public User(String firstname, String lastname, String emailAddress) {
|
||||
|
||||
this();
|
||||
this.firstname = firstname;
|
||||
this.lastname = lastname;
|
||||
this.emailAddress = emailAddress;
|
||||
this.active = true;
|
||||
this.roles = new HashSet<Role>();
|
||||
this.colleagues = new HashSet<User>();
|
||||
this.createdAt = new Date();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
*/
|
||||
@Configuration
|
||||
@EnableTransactionManagement
|
||||
class InfrastructureConfig {
|
||||
public class InfrastructureConfig {
|
||||
|
||||
@Bean
|
||||
public DataSource dataSource() {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.sample;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.jpa.domain.sample.MailMessage;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
|
||||
|
||||
import com.mysema.query.types.OrderSpecifier;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
public interface MailMessageRepository extends JpaRepository<MailMessage, Long>, QueryDslPredicateExecutor<MailMessage> {
|
||||
|
||||
List<MailMessage> findAll(Predicate predicate, OrderSpecifier<?>... orders);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.support;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.data.jpa.support.JpaMetaModelPathBuilder.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort.Direction;
|
||||
import org.springframework.data.jpa.domain.JpaSort;
|
||||
import org.springframework.data.jpa.domain.sample.MailMessage;
|
||||
import org.springframework.data.jpa.domain.sample.MailMessage_;
|
||||
import org.springframework.data.jpa.domain.sample.MailSender;
|
||||
import org.springframework.data.jpa.domain.sample.MailSender_;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.data.jpa.repository.config.InfrastructureConfig;
|
||||
import org.springframework.data.jpa.repository.sample.MailMessageRepository;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@Transactional
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class JpaMetaModelRepositoryUnitTests {
|
||||
|
||||
@Configuration
|
||||
@Import(InfrastructureConfig.class)
|
||||
@EnableJpaRepositories(basePackageClasses = MailMessageRepository.class)
|
||||
static class Config {}
|
||||
|
||||
private static final MailMessage_ jmail = null;
|
||||
private static final MailSender_ jsender = null;
|
||||
|
||||
@PersistenceContext EntityManager em;
|
||||
|
||||
@Autowired MailMessageRepository mailMessageRepository;
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void shouldSortMailWithPageRequestAndJpaSortCriteriaNullsFirst() {
|
||||
|
||||
MailMessage message1 = new MailMessage();
|
||||
message1.setContent("abc");
|
||||
MailSender sender1 = new MailSender("foo");
|
||||
message1.setMailSender(sender1);
|
||||
|
||||
MailMessage message2 = new MailMessage();
|
||||
message2.setContent("abc");
|
||||
|
||||
mailMessageRepository.save(message1);
|
||||
mailMessageRepository.save(message2);
|
||||
|
||||
Page<MailMessage> results = mailMessageRepository.findAll(new PageRequest(0, 20, //
|
||||
new JpaSort(Direction.ASC, path(jmail.mailSender).get(jsender.name).build(em))));
|
||||
List<MailMessage> messages = results.getContent();
|
||||
|
||||
assertThat(messages, hasSize(2));
|
||||
assertThat(messages.get(0).getMailSender(), is(nullValue()));
|
||||
assertThat(messages.get(1).getMailSender(), is(sender1));
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,8 @@ import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.jpa.domain.sample.Address;
|
||||
import org.springframework.data.jpa.domain.sample.QUser;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.querydsl.QPageRequest;
|
||||
import org.springframework.data.querydsl.QSort;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@@ -215,4 +217,60 @@ public class QueryDslJpaRepositoryTests {
|
||||
assertThat(page.getContent(), hasItems(dave, carter, oliver));
|
||||
assertThat(page.getContent().get(2), is(oliver));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void findBySpecificationWithSortByQueryDslOrderSpecifierWithQPageRequestAndQSort() {
|
||||
|
||||
QUser user = QUser.user;
|
||||
|
||||
Page<User> page = repository.findAll(user.firstname.isNotNull(),
|
||||
new QPageRequest(0, 10, new QSort(user.firstname.asc())));
|
||||
|
||||
assertThat(page.getContent(), hasSize(3));
|
||||
assertThat(page.getContent(), hasItems(carter, dave, oliver));
|
||||
assertThat(page.getContent().get(0), is(carter));
|
||||
assertThat(page.getContent().get(1), is(dave));
|
||||
assertThat(page.getContent().get(2), is(oliver));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void findBySpecificationWithSortByQueryDslOrderSpecifierWithQPageRequest() {
|
||||
|
||||
QUser user = QUser.user;
|
||||
|
||||
Page<User> page = repository.findAll(user.firstname.isNotNull(), new QPageRequest(0, 10, user.firstname.asc()));
|
||||
|
||||
assertThat(page.getContent(), hasSize(3));
|
||||
assertThat(page.getContent(), hasItems(carter, dave, oliver));
|
||||
assertThat(page.getContent().get(0), is(carter));
|
||||
assertThat(page.getContent().get(1), is(dave));
|
||||
assertThat(page.getContent().get(2), is(oliver));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void findBySpecificationWithSortByQueryDslOrderSpecifierForAssociationShouldGenerateLeftJoinWithQPageRequest() {
|
||||
|
||||
oliver.setManager(dave);
|
||||
dave.setManager(carter);
|
||||
|
||||
QUser user = QUser.user;
|
||||
|
||||
Page<User> page = repository.findAll(user.firstname.isNotNull(),
|
||||
new QPageRequest(0, 10, user.manager.firstname.asc()));
|
||||
|
||||
assertThat(page.getContent(), hasSize(3));
|
||||
assertThat(page.getContent(), hasItems(carter, dave, oliver));
|
||||
assertThat(page.getContent().get(0), is(carter));
|
||||
assertThat(page.getContent().get(1), is(dave));
|
||||
assertThat(page.getContent().get(2), is(oliver));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,37 +15,96 @@
|
||||
*/
|
||||
package org.springframework.data.jpa.repository.support;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan.Filter;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.FilterType;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.jpa.domain.sample.MailMessage;
|
||||
import org.springframework.data.jpa.domain.sample.MailSender;
|
||||
import org.springframework.data.jpa.domain.sample.QMailMessage;
|
||||
import org.springframework.data.jpa.domain.sample.QMailSender;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
|
||||
import org.springframework.data.jpa.repository.config.InfrastructureConfig;
|
||||
import org.springframework.data.jpa.repository.sample.MailMessageRepository;
|
||||
import org.springframework.data.jpa.repository.support.QueryDslRepositorySupportTests.UserRepository;
|
||||
import org.springframework.data.jpa.repository.support.QueryDslRepositorySupportTests.UserRepositoryImpl;
|
||||
import org.springframework.data.querydsl.QPageRequest;
|
||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.transaction.annotation.EnableTransactionManagement;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* Integration test for the setup of beans extending {@link QueryDslRepositorySupport}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@Transactional
|
||||
@ContextConfiguration
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration("classpath:querydsl.xml")
|
||||
public class QueryDslRepositorySupportIntegrationTests {
|
||||
|
||||
@Autowired
|
||||
UserRepository repository;
|
||||
@Configuration
|
||||
@EnableJpaRepositories(basePackageClasses = MailMessageRepository.class, includeFilters = @Filter(
|
||||
type = FilterType.ASSIGNABLE_TYPE, value = { MailMessageRepository.class }))
|
||||
@EnableTransactionManagement
|
||||
static class Config extends InfrastructureConfig {
|
||||
@Bean
|
||||
public UserRepositoryImpl userRepositoryImpl() {
|
||||
|
||||
@Autowired
|
||||
ReconfiguringUserRepositoryImpl reconfiguredRepo;
|
||||
return new UserRepositoryImpl() {
|
||||
@Override
|
||||
@PersistenceContext(unitName = "querydsl")
|
||||
public void setEntityManager(EntityManager entityManager) {
|
||||
super.setEntityManager(entityManager);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@PersistenceContext(unitName = "querydsl")
|
||||
EntityManager em;
|
||||
@Bean
|
||||
public ReconfiguringUserRepositoryImpl reconfiguringUserRepositoryImpl() {
|
||||
return new ReconfiguringUserRepositoryImpl();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EntityManagerContainer entityManagerContainer() {
|
||||
return new EntityManagerContainer();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
|
||||
|
||||
LocalContainerEntityManagerFactoryBean emf = super.entityManagerFactory();
|
||||
emf.setPersistenceUnitName("querydsl");
|
||||
return emf;
|
||||
}
|
||||
}
|
||||
|
||||
@Autowired UserRepository repository;
|
||||
|
||||
@Autowired ReconfiguringUserRepositoryImpl reconfiguredRepo;
|
||||
|
||||
@Autowired MailMessageRepository mailMessageRepository;
|
||||
|
||||
@PersistenceContext(unitName = "querydsl") EntityManager em;
|
||||
|
||||
static final QMailMessage qmail = QMailMessage.mailMessage;
|
||||
static final QMailSender qsender = QMailSender.mailSender;
|
||||
|
||||
@Test
|
||||
public void createsRepoCorrectly() {
|
||||
@@ -62,6 +121,56 @@ public class QueryDslRepositorySupportIntegrationTests {
|
||||
assertThat(reconfiguredRepo.getEntityManager().getEntityManagerFactory(), is(em.getEntityManagerFactory()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void shouldSortMailWithQueryDslRepositoryAndQPageRequestDslSortCriteriaNullsFirst() {
|
||||
|
||||
MailMessage message1 = new MailMessage();
|
||||
message1.setContent("abc");
|
||||
MailSender sender1 = new MailSender("foo");
|
||||
message1.setMailSender(sender1);
|
||||
|
||||
MailMessage message2 = new MailMessage();
|
||||
message2.setContent("abc");
|
||||
|
||||
mailMessageRepository.save(message1);
|
||||
mailMessageRepository.save(message2);
|
||||
|
||||
Page<MailMessage> results = mailMessageRepository.findAll(qmail.content.eq("abc"), new QPageRequest(0, 20,
|
||||
qsender.name.asc()));
|
||||
List<MailMessage> messages = results.getContent();
|
||||
|
||||
assertThat(messages, hasSize(2));
|
||||
assertThat(messages.get(0).getMailSender(), is(nullValue()));
|
||||
assertThat(messages.get(1).getMailSender(), is(sender1));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-12
|
||||
*/
|
||||
@Test
|
||||
public void shouldSortMailWithQueryDslRepositoryAndDslSortCriteriaNullsFirst() {
|
||||
|
||||
MailMessage message1 = new MailMessage();
|
||||
message1.setContent("abc");
|
||||
MailSender sender1 = new MailSender("foo");
|
||||
message1.setMailSender(sender1);
|
||||
|
||||
MailMessage message2 = new MailMessage();
|
||||
message2.setContent("abc");
|
||||
|
||||
mailMessageRepository.save(message1);
|
||||
mailMessageRepository.save(message2);
|
||||
|
||||
List<MailMessage> messages = mailMessageRepository.findAll(qmail.content.eq("abc"), qmail.mailSender.name.asc());
|
||||
|
||||
assertThat(messages, hasSize(2));
|
||||
assertThat(messages.get(0).getMailSender(), is(nullValue()));
|
||||
assertThat(messages.get(1).getMailSender(), is(sender1));
|
||||
}
|
||||
|
||||
static class ReconfiguringUserRepositoryImpl extends QueryDslRepositorySupport {
|
||||
|
||||
public ReconfiguringUserRepositoryImpl() {
|
||||
@@ -77,7 +186,6 @@ public class QueryDslRepositorySupportIntegrationTests {
|
||||
|
||||
static class EntityManagerContainer {
|
||||
|
||||
@PersistenceContext(unitName = "querydsl")
|
||||
EntityManager em;
|
||||
@PersistenceContext(unitName = "querydsl") EntityManager em;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2011-2013 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -36,14 +36,14 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
* Integration test for {@link QueryDslRepositorySupport}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration({ "classpath:infrastructure.xml" })
|
||||
@Transactional
|
||||
public class QueryDslRepositorySupportTests {
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager em;
|
||||
@PersistenceContext EntityManager em;
|
||||
|
||||
UserRepository repository;
|
||||
User dave, carter;
|
||||
|
||||
@@ -28,16 +28,22 @@
|
||||
<class>org.springframework.data.jpa.domain.sample.Customer</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.Order</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.Address</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
</persistence-unit>
|
||||
<persistence-unit name="querydsl">
|
||||
<class>org.springframework.data.jpa.domain.sample.User</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
</persistence-unit>
|
||||
<persistence-unit name="cdi">
|
||||
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
||||
<class>org.springframework.data.jpa.domain.sample.User</class>
|
||||
<class>org.springframework.data.jpa.repository.cdi.Person</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<properties>
|
||||
<property name="hibernate.connection.username" value="sa" />
|
||||
@@ -54,6 +60,8 @@
|
||||
<persistence-unit name="metadata">
|
||||
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
||||
<class>org.springframework.data.jpa.domain.sample.User</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<properties>
|
||||
@@ -63,12 +71,16 @@
|
||||
<persistence-unit name="metadata_el">
|
||||
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
|
||||
<class>org.springframework.data.jpa.domain.sample.User</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
</persistence-unit>
|
||||
<persistence-unit name="metadata_oj">
|
||||
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>
|
||||
<class>org.springframework.data.jpa.domain.sample.User</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
<properties>
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
<class>org.springframework.data.jpa.domain.sample.User</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.SpecialUser</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.Role</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
</persistence-unit>
|
||||
<persistence-unit name="second">
|
||||
@@ -14,6 +16,8 @@
|
||||
<class>org.springframework.data.jpa.domain.sample.Role</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.AuditableUser</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.AuditableRole</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
|
||||
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
|
||||
<exclude-unlisted-classes>true</exclude-unlisted-classes>
|
||||
</persistence-unit>
|
||||
</persistence>
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<import resource="infrastructure.xml" />
|
||||
|
||||
<bean class="org.springframework.data.jpa.repository.support.QueryDslRepositorySupportTests.UserRepositoryImpl" />
|
||||
|
||||
<bean class="org.springframework.data.jpa.repository.support.QueryDslRepositorySupportIntegrationTests.ReconfiguringUserRepositoryImpl" />
|
||||
|
||||
<bean class="org.springframework.data.jpa.repository.support.QueryDslRepositorySupportIntegrationTests.EntityManagerContainer" />
|
||||
|
||||
<bean id="alternate" parent="entityManagerFactory">
|
||||
<property name="persistenceUnitName" value="querydsl" />
|
||||
</bean>
|
||||
|
||||
</beans>
|
||||
Reference in New Issue
Block a user