Permalink
Browse files

Further improvements to QueryDsl support.

- use correct Maven phase to generate query classes
- use target folder that compiler plugin automatically picks up
- added constructor taking an EntityPath to QueryDslJpaRepository
- create PathBuilder upfront and reuse it
- use Pageable.getOffset()
- let test case use QUser directly
- added test case to ensure detection of query class
- added QueryDslRepositorySupport to ease implementation of repositories
  • Loading branch information...
1 parent 8389371 commit 2d781dfe3767efaea55033f7fbfe9ee3134accde @olivergierke olivergierke committed Dec 30, 2010
View
4 pom.xml
@@ -190,12 +190,12 @@
<version>1.0</version>
<executions>
<execution>
- <phase>process-test-sources</phase>
+ <phase>generate-test-sources</phase>
<goals>
<goal>test-process</goal>
</goals>
<configuration>
- <outputDirectory>target/generated-test-sources/java</outputDirectory>
+ <outputDirectory>target/generated-sources/test-annotations</outputDirectory>
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
View
36 src/main/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepository.java
@@ -35,6 +35,7 @@
import com.mysema.query.jpa.JPQLQuery;
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.Predicate;
import com.mysema.query.types.path.PathBuilder;
@@ -51,20 +52,38 @@
private final EntityManager em;
private final EntityPath<T> path;
+ private final PathBuilder<T> builder;
/**
- * Creates a new {@link QueryDslJpaRepository}.
+ * Creates a new {@link QueryDslJpaRepository} from the given domain class
+ * and {@link EntityManager}. This will reflectively lookup an
+ * {@link EntityPath} based on the given domain class.
*
* @param domainClass
* @param entityManager
*/
public QueryDslJpaRepository(Class<T> domainClass,
EntityManager entityManager) {
- super(domainClass, entityManager);
+ this(createPath(domainClass), entityManager);
+ }
+
+
+ /**
+ * Creates a new {@link QueryDslJpaRepository} from the given
+ * {@link EntityPath} and {@link EntityManager}.
+ *
+ * @param path
+ * @param entityManager
+ */
+ @SuppressWarnings("unchecked")
+ public QueryDslJpaRepository(EntityPath<T> path, EntityManager entityManager) {
+
+ super((Class<T>) path.getType(), entityManager);
this.em = entityManager;
- this.path = createPath(domainClass);
+ this.path = path;
+ this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
}
@@ -143,14 +162,13 @@ public Long count(Predicate spec) {
* @return
*/
@SuppressWarnings("unchecked")
- private EntityPath<T> createPath(Class<T> domainClass) {
+ private static <T> EntityPath<T> createPath(Class<T> domainClass) {
String pathClassName =
String.format("%s.Q%s", domainClass.getPackage().getName(),
domainClass.getSimpleName());
try {
-
Class<?> pathClass =
ClassUtils.forName(pathClassName,
QueryDslJpaRepository.class.getClassLoader());
@@ -188,7 +206,7 @@ private JPQLQuery applyPagination(JPQLQuery query, Pageable pageable) {
return query;
}
- query.offset(pageable.getFirstItem());
+ query.offset(pageable.getOffset());
query.limit(pageable.getPageSize());
return applySorting(query, pageable.getSort());
@@ -226,12 +244,10 @@ private JPQLQuery applySorting(JPQLQuery query, Sort sort) {
@SuppressWarnings({ "rawtypes", "unchecked" })
private OrderSpecifier<?> toOrder(Order order) {
- PathBuilder<T> path =
- new PathBuilder<T>(this.path.getType(), this.path.getMetadata());
- PathBuilder<Object> builder = path.get(order.getProperty());
+ Expression<Object> property = builder.get(order.getProperty());
return new OrderSpecifier(
order.isAscending() ? com.mysema.query.types.Order.ASC
- : com.mysema.query.types.Order.DESC, builder);
+ : com.mysema.query.types.Order.DESC, property);
}
}
View
88 src/main/java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupport.java
@@ -0,0 +1,88 @@
+package org.springframework.data.jpa.repository.support;
+
+import javax.annotation.PostConstruct;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+
+import org.springframework.beans.factory.annotation.Required;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.Assert;
+
+import com.mysema.query.dml.DeleteClause;
+import com.mysema.query.dml.UpdateClause;
+import com.mysema.query.jpa.JPQLQuery;
+import com.mysema.query.jpa.impl.JPADeleteClause;
+import com.mysema.query.jpa.impl.JPAQuery;
+import com.mysema.query.jpa.impl.JPAUpdateClause;
+import com.mysema.query.types.EntityPath;
+
+
+/**
+ * Base class for implementing repositories using QueryDsl library.
+ *
+ * @author Oliver Gierke
+ */
+@Repository
+public abstract class QueryDslRepositorySupport {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+
+ /**
+ * Setter to inject {@link EntityManager}.
+ *
+ * @param entityManager must not be {@literal null}
+ */
+ @Required
+ public void setEntityManager(EntityManager entityManager) {
+
+ Assert.notNull(entityManager);
+ this.entityManager = entityManager;
+ }
+
+
+ /**
+ * Callback to verify configuration. Used by containers.
+ */
+ @PostConstruct
+ public void validate() {
+
+ Assert.notNull(entityManager, "EntityManager must not be null!");
+ }
+
+
+ /**
+ * Returns a fresh {@link JPQLQuery}.
+ *
+ * @return
+ */
+ protected JPQLQuery from(EntityPath<?>... paths) {
+
+ return new JPAQuery(entityManager).from(paths);
+ }
+
+
+ /**
+ * Returns a fresh {@link DeleteClause}.
+ *
+ * @param path
+ * @return
+ */
+ protected DeleteClause<JPADeleteClause> delete(EntityPath<?> path) {
+
+ return new JPADeleteClause(entityManager, path);
+ }
+
+
+ /**
+ * Returns a fresh {@link UpdateClause}.
+ *
+ * @param path
+ * @return
+ */
+ protected UpdateClause<JPAUpdateClause> update(EntityPath<?> path) {
+
+ return new JPAUpdateClause(entityManager, path);
+ }
+}
View
15 ...test/java/org/springframework/data/jpa/repository/support/QueryDslJpaRepositoryTests.java
@@ -28,7 +28,6 @@
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.QUser;
import org.springframework.data.jpa.domain.sample.User;
-import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
@@ -37,7 +36,7 @@
/**
- * Integration test for {@link JpaRepository}.
+ * Integration test for {@link QueryDslJpaRepository}.
*
* @author Oliver Gierke
*/
@@ -51,14 +50,13 @@
QueryDslJpaRepository<User, Integer> repository;
QUser user = new QUser("user");
-
User dave, carter;
@Before
public void setUp() {
- repository = new QueryDslJpaRepository<User, Integer>(User.class, em);
+ repository = new QueryDslJpaRepository<User, Integer>(user, em);
dave =
repository.save(new User("Dave", "Matthews",
"dave@matthews.com"));
@@ -69,7 +67,7 @@ public void setUp() {
@Test
- public void testCrudOperationsForCompoundKeyEntity() throws Exception {
+ public void executesPredicatesCorrectly() throws Exception {
BooleanExpression isCalledDave = user.firstname.eq("Dave");
BooleanExpression isBeauford = user.lastname.eq("Beauford");
@@ -79,4 +77,11 @@ public void testCrudOperationsForCompoundKeyEntity() throws Exception {
assertThat(result.size(), is(2));
assertThat(result, hasItems(carter, dave));
}
+
+
+ @Test
+ public void createsRepositoryFromDomainClassCorrectly() throws Exception {
+
+ new QueryDslJpaRepository<User, Integer>(User.class, em);
+ }
}
View
142 .../java/org/springframework/data/jpa/repository/support/QueryDslRepositorySupportTests.java
@@ -0,0 +1,142 @@
+package org.springframework.data.jpa.repository.support;
+
+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.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.data.jpa.domain.sample.QUser;
+import org.springframework.data.jpa.domain.sample.User;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.transaction.annotation.Transactional;
+
+
+/**
+ * Integration test for {@link QueryDslRepositorySupport}.
+ *
+ * @author Oliver Gierke
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration({ "classpath:infrastructure.xml" })
+@Transactional
+public class QueryDslRepositorySupportTests {
+
+ @PersistenceContext
+ EntityManager em;
+
+ UserRepository repository;
+ User dave, carter;
+
+
+ @Before
+ public void setup() {
+
+ dave = new User("Dave", "Matthews", "dave@matthews.com");
+ em.persist(dave);
+
+ carter = new User("Carter", "Beauford", "carter@beauford.com");
+ em.persist(carter);
+
+ UserRepositoryImpl repository = new UserRepositoryImpl();
+ repository.setEntityManager(em);
+ repository.validate();
+
+ this.repository = repository;
+ }
+
+
+ @Test
+ public void readsUsersCorrectly() throws Exception {
+
+ List<User> result = repository.findUsersByLastname("Matthews");
+ assertThat(result.size(), is(1));
+ assertThat(result.get(0), is(dave));
+
+ result = repository.findUsersByLastname("Beauford");
+ assertThat(result.size(), is(1));
+ assertThat(result.get(0), is(carter));
+ }
+
+
+ @Test
+ public void updatesUsersCorrectly() throws Exception {
+
+ long updates = repository.updateLastnamesTo("Foo");
+ assertThat(updates, is(2L));
+
+ List<User> result = repository.findUsersByLastname("Matthews");
+ assertThat(result.size(), is(0));
+
+ result = repository.findUsersByLastname("Beauford");
+ assertThat(result.size(), is(0));
+
+ result = repository.findUsersByLastname("Foo");
+ assertThat(result.size(), is(2));
+ assertThat(result, hasItems(dave, carter));
+ }
+
+
+ @Test
+ public void deletesAllWithLastnameCorrectly() throws Exception {
+
+ long updates = repository.deleteAllWithLastname("Matthews");
+ assertThat(updates, is(1L));
+
+ List<User> result = repository.findUsersByLastname("Matthews");
+ assertThat(result.size(), is(0));
+
+ result = repository.findUsersByLastname("Beauford");
+ assertThat(result.size(), is(1));
+ assertThat(result.get(0), is(carter));
+ }
+
+
+ @Test(expected = IllegalArgumentException.class)
+ public void rejectsUnsetEntityManager() throws Exception {
+
+ UserRepositoryImpl repositoryImpl = new UserRepositoryImpl();
+ repositoryImpl.validate();
+ }
+
+ private static interface UserRepository {
+
+ List<User> findUsersByLastname(String firstname);
+
+
+ long updateLastnamesTo(String lastname);
+
+
+ long deleteAllWithLastname(String lastname);
+ }
+
+ private static class UserRepositoryImpl extends QueryDslRepositorySupport
+ implements UserRepository {
+
+ private static final QUser user = QUser.user;
+
+
+ public List<User> findUsersByLastname(String lastname) {
+
+ return from(user).where(user.lastname.eq(lastname)).list(user);
+ }
+
+
+ public long updateLastnamesTo(String lastname) {
+
+ return update(user).set(user.lastname, lastname).execute();
+ }
+
+
+ public long deleteAllWithLastname(String lastname) {
+
+ return delete(user).where(user.lastname.eq(lastname)).execute();
+ }
+ }
+}

0 comments on commit 2d781df

Please sign in to comment.