From 692b2382bf91a504c595b8549283b77a9a6e3f43 Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Wed, 4 Dec 2013 17:32:20 +0100 Subject: [PATCH] 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. --- pom.xml | 65 +++++- .../data/jpa/domain/JpaSort.java | 114 +++++++++++ .../support/QueryDslJpaRepository.java | 6 +- .../data/jpa/repository/support/Querydsl.java | 115 ++++++++++- .../jpa/support/JpaMetaModelPathBuilder.java | 110 ++++++++++ .../data/jpa/domain/JpaSortTests.java | 188 ++++++++++++++++++ .../data/jpa/domain/sample/MailMessage.java | 59 ++++++ .../data/jpa/domain/sample/MailSender.java | 86 ++++++++ .../data/jpa/domain/sample/User.java | 11 +- .../config/InfrastructureConfig.java | 2 +- .../sample/MailMessageRepository.java | 33 +++ .../JpaMetaModelRepositoryUnitTests.java | 92 +++++++++ .../support/QueryDslJpaRepositoryTests.java | 58 ++++++ ...yDslRepositorySupportIntegrationTests.java | 130 +++++++++++- .../QueryDslRepositorySupportTests.java | 6 +- src/test/resources/META-INF/persistence.xml | 12 ++ src/test/resources/META-INF/persistence2.xml | 4 + src/test/resources/querydsl.xml | 18 -- 18 files changed, 1057 insertions(+), 52 deletions(-) create mode 100644 src/main/java/org/springframework/data/jpa/domain/JpaSort.java create mode 100644 src/main/java/org/springframework/data/jpa/support/JpaMetaModelPathBuilder.java create mode 100644 src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java create mode 100755 src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java create mode 100644 src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/support/JpaMetaModelRepositoryUnitTests.java delete mode 100644 src/test/resources/querydsl.xml diff --git a/pom.xml b/pom.xml index 303a521e3..fa6a9fef7 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,7 @@ 1.8.0.10 2.0.0 2.2.1 - 1.7.0.M1 + 1.7.0.BUILD-SNAPSHOT @@ -413,6 +413,69 @@ org.codehaus.mojo wagon-maven-plugin + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + + default-compile + + compile + + + + + compile + + + + org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor + + ${project.build.directory}/generated-sources/test + + only + + + + + + + + org.hibernate + hibernate-jpamodelgen + 1.2.0.Final + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.8 + + + add-test-source + generate-test-sources + + add-test-source + + + + ${project.build.directory}/generated-sources/test + + + + + diff --git a/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/src/main/java/org/springframework/data/jpa/domain/JpaSort.java new file mode 100644 index 000000000..e9953bd2b --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -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> 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> jpaPaths) { + super(direction, toPropertyPaths(jpaPaths)); + } + + /** + * @param jpaPaths must not be {@literal null} or empty. + * @return + */ + private static List toPropertyPaths(List> jpaPaths) { + + Assert.notEmpty(jpaPaths, "Jpa orders must not be null or empty!"); + + List propertyPaths = new ArrayList(); + + 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(); + } +} diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java index cf2d4996b..d7695d854 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java @@ -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 extends SimpleJpa * @see org.springframework.data.querydsl.QueryDslPredicateExecutor#findAll(com.mysema.query.types.Predicate, com.mysema.query.types.OrderSpecifier[]) */ public List 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); } /* diff --git a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 6efedc31d..c75cf725d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -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> 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> modifiedOrderSpecifiers = new ArrayList>(); + + 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(attribute.getJavaType(), attribute.getName()); - query.leftJoin((EntityPath) builder.get(attribute.getName()), associationPathRoot); - PathBuilder attributePathBuilder = new PathBuilder(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(parentAttribute.getJavaType(), + parentAttribute.getName()); + query.leftJoin((EntityPath) builder.get(parentAttribute.getName()), associationPathRoot); + PathBuilder attributePathBuilder = new PathBuilder(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); } diff --git a/src/main/java/org/springframework/data/jpa/support/JpaMetaModelPathBuilder.java b/src/main/java/org/springframework/data/jpa/support/JpaMetaModelPathBuilder.java new file mode 100644 index 000000000..bd288e8c4 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/support/JpaMetaModelPathBuilder.java @@ -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 { + + private final Class rootType; + private final List> 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 JpaMetaModelPathBuilder path(SingularAttribute attribute) { + return new JpaMetaModelPathBuilder(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 attribute) { + this(rootType, attribute, new ArrayList>()); + } + + /** + * 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 attribute, + List> 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 JpaMetaModelPathBuilder get(SingularAttribute nestedAttribute) { + return new JpaMetaModelPathBuilder(rootType, nestedAttribute, new ArrayList>( + currentPathSegments)); + } + + /** + * Constructs a {@link Path} from the collected {@link SingularAttribute}s. + * + * @param em must not be {@literal null}. + * @return + */ + @SuppressWarnings("unchecked") + public Path 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) path; + } +} diff --git a/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java b/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java new file mode 100644 index 000000000..ca2f172d3 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java @@ -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>) null); + } + + /** + * @see DATAJPA-12 + */ + @Test + public void sortBySinglePropertyWithDefaultSortDirection() { + + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery q = cb.createQuery(User.class); + Root 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 q = cb.createQuery(User.class); + Root 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 q = cb.createQuery(User.class); + Root 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 q = cb.createQuery(User.class); + Root 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 q = cb.createQuery(User.class); + Root 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 q = cb.createQuery(User.class); + Root 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"))); + } +} diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java b/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java new file mode 100755 index 000000000..055ffcf89 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/MailMessage.java @@ -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; + } +} diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java b/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java new file mode 100644 index 000000000..fc3051ef0 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/domain/sample/MailSender.java @@ -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; + } +} 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 63d4218ed..210fd60fa 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 @@ -64,10 +64,7 @@ public class User { * Creates a new empty instance of {@code User}. */ public User() { - - this.roles = new HashSet(); - this.colleagues = new HashSet(); - 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(); + this.colleagues = new HashSet(); + this.createdAt = new Date(); } /** diff --git a/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java b/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java index 132301a1b..56ce58387 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/InfrastructureConfig.java @@ -35,7 +35,7 @@ import org.springframework.transaction.annotation.EnableTransactionManagement; */ @Configuration @EnableTransactionManagement -class InfrastructureConfig { +public class InfrastructureConfig { @Bean public DataSource dataSource() { diff --git a/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java b/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java new file mode 100644 index 000000000..d2885db0b --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/sample/MailMessageRepository.java @@ -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, QueryDslPredicateExecutor { + + List findAll(Predicate predicate, OrderSpecifier... orders); +} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaMetaModelRepositoryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetaModelRepositoryUnitTests.java new file mode 100644 index 000000000..90df20830 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaMetaModelRepositoryUnitTests.java @@ -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 results = mailMessageRepository.findAll(new PageRequest(0, 20, // + new JpaSort(Direction.ASC, path(jmail.mailSender).get(jsender.name).build(em)))); + List messages = results.getContent(); + + assertThat(messages, hasSize(2)); + assertThat(messages.get(0).getMailSender(), is(nullValue())); + assertThat(messages.get(1).getMailSender(), is(sender1)); + } +} diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepositoryTests.java index 06c24d4ad..0ef5d2914 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepositoryTests.java @@ -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 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 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 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)); + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportIntegrationTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportIntegrationTests.java index a34fc000b..f1627ab1a 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportIntegrationTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportIntegrationTests.java @@ -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 results = mailMessageRepository.findAll(qmail.content.eq("abc"), new QPageRequest(0, 20, + qsender.name.asc())); + List 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 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; } } diff --git a/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportTests.java b/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportTests.java index 4aef7df47..4f6930299 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportTests.java @@ -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; diff --git a/src/test/resources/META-INF/persistence.xml b/src/test/resources/META-INF/persistence.xml index 6406f85f2..4d7abaabb 100644 --- a/src/test/resources/META-INF/persistence.xml +++ b/src/test/resources/META-INF/persistence.xml @@ -28,16 +28,22 @@ org.springframework.data.jpa.domain.sample.Customer org.springframework.data.jpa.domain.sample.Order org.springframework.data.jpa.domain.sample.Address + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender true org.springframework.data.jpa.domain.sample.User + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender true org.hibernate.ejb.HibernatePersistence org.springframework.data.jpa.domain.sample.User org.springframework.data.jpa.repository.cdi.Person + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender true @@ -54,6 +60,8 @@ org.hibernate.ejb.HibernatePersistence org.springframework.data.jpa.domain.sample.User + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample true @@ -63,12 +71,16 @@ org.eclipse.persistence.jpa.PersistenceProvider org.springframework.data.jpa.domain.sample.User + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample true org.apache.openjpa.persistence.PersistenceProviderImpl org.springframework.data.jpa.domain.sample.User + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample true diff --git a/src/test/resources/META-INF/persistence2.xml b/src/test/resources/META-INF/persistence2.xml index 05b0db857..d7caad1cc 100644 --- a/src/test/resources/META-INF/persistence2.xml +++ b/src/test/resources/META-INF/persistence2.xml @@ -6,6 +6,8 @@ org.springframework.data.jpa.domain.sample.User org.springframework.data.jpa.domain.sample.SpecialUser org.springframework.data.jpa.domain.sample.Role + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender true @@ -14,6 +16,8 @@ org.springframework.data.jpa.domain.sample.Role org.springframework.data.jpa.domain.sample.AuditableUser org.springframework.data.jpa.domain.sample.AuditableRole + org.springframework.data.jpa.domain.sample.MailMessage + org.springframework.data.jpa.domain.sample.MailSender true diff --git a/src/test/resources/querydsl.xml b/src/test/resources/querydsl.xml deleted file mode 100644 index 226050002..000000000 --- a/src/test/resources/querydsl.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - -