Skip to content

Commit

Permalink
Further improvements to QueryDsl support.
Browse files Browse the repository at this point in the history
- 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
odrotbohm committed Dec 30, 2010
1 parent 8389371 commit 2d781df
Show file tree
Hide file tree
Showing 5 changed files with 268 additions and 17 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -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>
Expand Down
Expand Up @@ -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;
Expand All @@ -51,20 +52,38 @@ public class QueryDslJpaRepository<T, ID extends Serializable> extends

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());
}


Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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);
}
}
@@ -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);
}
}
Expand Up @@ -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;
Expand All @@ -37,7 +36,7 @@


/**
* Integration test for {@link JpaRepository}.
* Integration test for {@link QueryDslJpaRepository}.
*
* @author Oliver Gierke
*/
Expand All @@ -51,14 +50,13 @@ public class QueryDslJpaRepositoryTests {

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"));
Expand All @@ -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");
Expand All @@ -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);
}
}
@@ -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.