DATAJPA-8 - Introduced support for QueryDsl specifications
Extracted methods taking a Specification from JpaRepository into JpaSpecificationExecutor. Introduced QueryDslPredicateExecutor that takes QueryDsl Predicate objects to execute them. Introduced QueryDsl specific subclass to execute Predicate instances. Extended JpaRepositoryFactory to rather use the QueryDsl specific implementation if the actual user repository implements QueryDslPredicateExecutor. Added test case to ensure detection of query class. Added QueryDslRepositorySupport to ease implementation of repositories. Added constructor to QueryDslJpaRepository to take an EntityPathResolver to allow plugging in a different implementation. Extracted actual EntityPath resolution tests into unit test for SimpleEntityPathResolver. Check for QueryDsl classpath presence to prevent ClassNotFoundExceptions.
This commit is contained in:
44
pom.xml
44
pom.xml
@@ -1,7 +1,6 @@
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-jpa</artifactId>
|
||||
<version>1.0.0.BUILD-SNAPSHOT</version>
|
||||
@@ -58,6 +57,7 @@
|
||||
<openjpa.version>2.0.0</openjpa.version>
|
||||
<eclipselink.version>2.2.0</eclipselink.version>
|
||||
<aspectj.version>1.6.8</aspectj.version>
|
||||
<querydsl.version>2.1.1</querydsl.version>
|
||||
<junit.version>4.8.1</junit.version>
|
||||
<jpa.version>2.0.0</jpa.version>
|
||||
<slf4j.version>1.6.1</slf4j.version>
|
||||
@@ -385,6 +385,20 @@
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- QueryDsl -->
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-apt</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.mysema.querydsl</groupId>
|
||||
<artifactId>querydsl-jpa</artifactId>
|
||||
<version>${querydsl.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -502,6 +516,24 @@
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>com.mysema.maven</groupId>
|
||||
<artifactId>maven-apt-plugin</artifactId>
|
||||
<version>1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>generate-test-sources</phase>
|
||||
<goals>
|
||||
<goal>test-process</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/generated-sources/test-annotations</outputDirectory>
|
||||
<processor>com.mysema.query.apt.jpa.JPAAnnotationProcessor</processor>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -566,6 +598,11 @@
|
||||
<name>EclipseLink Repo</name>
|
||||
<url>http://www.eclipse.org/downloads/download.php?r=1&nf=1&file=/rt/eclipselink/maven.repo</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>querydsl</id>
|
||||
<name>QueryDsl</name>
|
||||
<url>http://source.mysema.com/maven2/releases</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<pluginRepositories>
|
||||
@@ -575,5 +612,10 @@
|
||||
<name>Spring Framework Maven Release Repository</name>
|
||||
<url>http://maven.springframework.org/release</url>
|
||||
</pluginRepository>
|
||||
<pluginRepository>
|
||||
<id>querydsl</id>
|
||||
<name>QueryDsl</name>
|
||||
<url>http://source.mysema.com/maven2/releases</url>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
</project>
|
||||
@@ -148,35 +148,6 @@ public interface JpaRepository<T, ID extends Serializable> extends
|
||||
Page<T> findAll(Pageable pageable);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a single entity matching the given {@link Specification}.
|
||||
*
|
||||
* @param spec
|
||||
* @return
|
||||
*/
|
||||
T findOne(Specification<T> spec);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all entities matching the given {@link Specification}.
|
||||
*
|
||||
* @param spec
|
||||
* @return
|
||||
*/
|
||||
List<T> findAll(Specification<T> spec);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link Page} of entities matching the given
|
||||
* {@link Specification}.
|
||||
*
|
||||
* @param spec
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
Page<T> findAll(Specification<T> spec, Pageable pageable);
|
||||
|
||||
|
||||
/**
|
||||
* Flushes all pending changes to the database.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
|
||||
/**
|
||||
* Interface to allow execution of {@link Specification}s based on the JPA
|
||||
* criteria API.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface JpaSpecificationExecutor<T> {
|
||||
|
||||
/**
|
||||
* Returns a single entity matching the given {@link Specification}.
|
||||
*
|
||||
* @param spec
|
||||
* @return
|
||||
*/
|
||||
T findOne(Specification<T> spec);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all entities matching the given {@link Specification}.
|
||||
*
|
||||
* @param spec
|
||||
* @return
|
||||
*/
|
||||
List<T> findAll(Specification<T> spec);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link Page} of entities matching the given
|
||||
* {@link Specification}.
|
||||
*
|
||||
* @param spec
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
Page<T> findAll(Specification<T> spec, Pageable pageable);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of instances that the given {@link Specification} will
|
||||
* return.
|
||||
*
|
||||
* @param spec the {@link Specification} to count instances for
|
||||
* @return the number of instances
|
||||
*/
|
||||
Long count(Specification<T> spec);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
|
||||
import com.mysema.query.types.OrderSpecifier;
|
||||
import com.mysema.query.types.Predicate;
|
||||
|
||||
|
||||
/**
|
||||
* Interface to allow execution of QueryDsl {@link Predicate} instances.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface QueryDslPredicateExecutor<T> {
|
||||
|
||||
/**
|
||||
* Returns a single entity matching the given {@link Predicate}.
|
||||
*
|
||||
* @param spec
|
||||
* @return
|
||||
*/
|
||||
T findOne(Predicate predicate);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all entities matching the given {@link Predicate}.
|
||||
*
|
||||
* @param spec
|
||||
* @return
|
||||
*/
|
||||
List<T> findAll(Predicate predicate);
|
||||
|
||||
|
||||
/**
|
||||
* Returns all entities matching the given {@link Predicate} applying the
|
||||
* given {@link OrderSpecifier}s.
|
||||
*
|
||||
* @param predicate
|
||||
* @param orders
|
||||
* @return
|
||||
*/
|
||||
List<T> findAll(Predicate predicate, OrderSpecifier<?>... orders);
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link Page} of entities matching the given {@link Predicate}.
|
||||
*
|
||||
* @param predicate
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
Page<T> findAll(Predicate predicate, Pageable pageable);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of instances that the given {@link Predicate} will
|
||||
* return.
|
||||
*
|
||||
* @param predicate the {@link Predicate} to count instances for
|
||||
* @return the number of instances
|
||||
*/
|
||||
Long count(Predicate predicate);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import java.io.Serializable;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.springframework.data.jpa.repository.QueryDslPredicateExecutor;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy;
|
||||
import org.springframework.data.jpa.repository.query.QueryExtractor;
|
||||
import org.springframework.data.jpa.repository.utils.JpaClassUtils;
|
||||
@@ -27,6 +28,7 @@ import org.springframework.data.repository.query.QueryLookupStrategy.Key;
|
||||
import org.springframework.data.repository.support.RepositoryFactorySupport;
|
||||
import org.springframework.data.repository.support.RepositoryMetadata;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
|
||||
/**
|
||||
@@ -36,6 +38,10 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
|
||||
private static final boolean QUERY_DSL_PRESENT = ClassUtils.isPresent(
|
||||
"com.mysema.query.types.Predicate",
|
||||
JpaRepositoryFactory.class.getClassLoader());
|
||||
|
||||
private final EntityManager entityManager;
|
||||
private final QueryExtractor extractor;
|
||||
|
||||
@@ -81,9 +87,15 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
protected <T, ID extends Serializable> Object getTargetRepository(
|
||||
RepositoryMetadata metadata, EntityManager entityManager) {
|
||||
|
||||
JpaEntityInformation<T, ID> entityMetadata =
|
||||
getEntityInformation((Class<T>) metadata.getDomainClass());
|
||||
return new SimpleJpaRepository(entityMetadata, entityManager);
|
||||
Class<?> repositoryInterface = metadata.getRepositoryInterface();
|
||||
JpaEntityInformation<?, Serializable> entityInformation =
|
||||
getEntityInformation(metadata.getDomainClass());
|
||||
|
||||
if (isQueryDslExecutor(repositoryInterface)) {
|
||||
return new QueryDslJpaRepository(entityInformation, entityManager);
|
||||
} else {
|
||||
return new SimpleJpaRepository(entityInformation, entityManager);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -97,7 +109,26 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
|
||||
@Override
|
||||
protected Class<?> getRepositoryBaseClass(Class<?> repositoryInterface) {
|
||||
|
||||
return SimpleJpaRepository.class;
|
||||
if (isQueryDslExecutor(repositoryInterface)) {
|
||||
return QueryDslJpaRepository.class;
|
||||
} else {
|
||||
return SimpleJpaRepository.class;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the given repository interface requires a QueryDsl
|
||||
* specific implementation to be chosen.
|
||||
*
|
||||
* @param repositoryInterface
|
||||
* @return
|
||||
*/
|
||||
private boolean isQueryDslExecutor(Class<?> repositoryInterface) {
|
||||
|
||||
return QUERY_DSL_PRESENT
|
||||
&& QueryDslPredicateExecutor.class
|
||||
.isAssignableFrom(repositoryInterface);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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 java.io.Serializable;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.jpa.repository.QueryDslPredicateExecutor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
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;
|
||||
|
||||
|
||||
/**
|
||||
* QueryDsl specific extension of {@link SimpleJpaRepository} which adds
|
||||
* implementation for {@link QueryDslPredicateExecutor}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class QueryDslJpaRepository<T, ID extends Serializable> extends
|
||||
SimpleJpaRepository<T, ID> implements QueryDslPredicateExecutor<T> {
|
||||
|
||||
private final EntityManager em;
|
||||
private final EntityPath<T> path;
|
||||
private final PathBuilder<T> builder;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryDslJpaRepository} from the given domain class
|
||||
* and {@link EntityManager}. This will use the
|
||||
* {@link SimpleEntityPathResolver} to translate the given domain class into
|
||||
* an {@link EntityPath}.
|
||||
*
|
||||
* @param domainClass
|
||||
* @param entityManager
|
||||
*/
|
||||
public QueryDslJpaRepository(JpaEntityInformation<T, ID> entityMetadata,
|
||||
EntityManager entityManager) {
|
||||
|
||||
this(entityMetadata, entityManager, SimpleEntityPathResolver.INSTANCE);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryDslJpaRepository} from the given domain class
|
||||
* and {@link EntityManager} and uses the given {@link EntityPathResolver}
|
||||
* to translate the domain class into an {@link EntityPath}.
|
||||
*
|
||||
* @param domainClass
|
||||
* @param entityManager
|
||||
* @param resolver
|
||||
*/
|
||||
public QueryDslJpaRepository(JpaEntityInformation<T, ID> entityMetadata,
|
||||
EntityManager entityManager, EntityPathResolver resolver) {
|
||||
|
||||
super(entityMetadata, entityManager);
|
||||
this.em = entityManager;
|
||||
this.path = resolver.createPath(entityMetadata.getJavaType());
|
||||
this.builder = new PathBuilder<T>(path.getType(), path.getMetadata());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.querydsl.
|
||||
* QueryDslSpecificationExecutor#findOne(com.mysema.query.types.Predicate)
|
||||
*/
|
||||
public T findOne(Predicate predicate) {
|
||||
|
||||
return createQuery(predicate).uniqueResult(path);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.querydsl.
|
||||
* QueryDslSpecificationExecutor#findAll(com.mysema.query.types.Predicate)
|
||||
*/
|
||||
public List<T> findAll(Predicate predicate) {
|
||||
|
||||
return createQuery(predicate).list(path);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.querydsl.
|
||||
* QueryDslSpecificationExecutor#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);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.querydsl.
|
||||
* QueryDslSpecificationExecutor#findAll(com.mysema.query.types.Predicate,
|
||||
* org.springframework.data.domain.Pageable)
|
||||
*/
|
||||
public Page<T> findAll(Predicate predicate, Pageable pageable) {
|
||||
|
||||
JPQLQuery countQuery = createQuery(predicate);
|
||||
JPQLQuery query = applyPagination(createQuery(predicate), pageable);
|
||||
|
||||
return new PageImpl<T>(query.list(path), pageable, countQuery.count());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.querydsl.
|
||||
* QueryDslSpecificationExecutor#count(com.mysema.query.types.Predicate)
|
||||
*/
|
||||
public Long count(Predicate predicate) {
|
||||
|
||||
return createQuery(predicate).count();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link JPQLQuery} for the given {@link Predicate}.
|
||||
*
|
||||
* @param predicate
|
||||
* @return
|
||||
*/
|
||||
private JPQLQuery createQuery(Predicate... predicate) {
|
||||
|
||||
return new JPAQuery(em).from(path).where(predicate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies the given {@link Pageable} to the given {@link JPQLQuery}.
|
||||
*
|
||||
* @param query
|
||||
* @param pageable
|
||||
* @return
|
||||
*/
|
||||
private JPQLQuery applyPagination(JPQLQuery query, Pageable pageable) {
|
||||
|
||||
if (pageable == null) {
|
||||
return query;
|
||||
}
|
||||
|
||||
query.offset(pageable.getOffset());
|
||||
query.limit(pageable.getPageSize());
|
||||
|
||||
return applySorting(query, pageable.getSort());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies sorting to the given {@link JPQLQuery}.
|
||||
*
|
||||
* @param query
|
||||
* @param sort
|
||||
* @return
|
||||
*/
|
||||
private JPQLQuery applySorting(JPQLQuery query, Sort sort) {
|
||||
|
||||
if (sort == null) {
|
||||
return query;
|
||||
}
|
||||
|
||||
for (Order order : sort) {
|
||||
query.orderBy(toOrder(order));
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transforms a plain {@link Order} into a QueryDsl specific
|
||||
* {@link OrderSpecifier}.
|
||||
*
|
||||
* @param order
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||
private OrderSpecifier<?> toOrder(Order order) {
|
||||
|
||||
Expression<Object> property = builder.get(order.getProperty());
|
||||
|
||||
return new OrderSpecifier(
|
||||
order.isAscending() ? com.mysema.query.types.Order.ASC
|
||||
: com.mysema.query.types.Order.DESC, property);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy interface to abstract the ways to translate an plain domain
|
||||
* class into a {@link EntityPath}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public static interface EntityPathResolver {
|
||||
|
||||
<T> EntityPath<T> createPath(Class<T> domainClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link EntityPathResolver} to lookup a query
|
||||
* class by reflection and using the static field of the same type.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
static enum SimpleEntityPathResolver implements EntityPathResolver {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
private static final String NO_CLASS_FOUND_TEMPLATE =
|
||||
"Did not find a query class %s for domain class %s!";
|
||||
private static final String NO_FIELD_FOUND_TEMPLATE =
|
||||
"Did not find a static field of the same type in %s!";
|
||||
|
||||
|
||||
/**
|
||||
* Creates an {@link EntityPath} instance for the given domain class.
|
||||
* Tries to lookup a class matching the naming convention (prepend Q to
|
||||
* the simple name of the class, same package) and find a static field
|
||||
* of the same type in it.
|
||||
*
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> EntityPath<T> createPath(Class<T> domainClass) {
|
||||
|
||||
String pathClassName = getQueryClassName(domainClass);
|
||||
|
||||
try {
|
||||
Class<?> pathClass =
|
||||
ClassUtils.forName(pathClassName,
|
||||
QueryDslJpaRepository.class.getClassLoader());
|
||||
Field field = getStaticFieldOfType(pathClass);
|
||||
|
||||
if (field == null) {
|
||||
throw new IllegalStateException(String.format(
|
||||
NO_FIELD_FOUND_TEMPLATE, pathClass));
|
||||
} else {
|
||||
return (EntityPath<T>) ReflectionUtils
|
||||
.getField(field, null);
|
||||
}
|
||||
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException(String.format(
|
||||
NO_CLASS_FOUND_TEMPLATE, pathClassName,
|
||||
domainClass.getName()), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the first static field of the given type inside the given
|
||||
* type.
|
||||
*
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
private Field getStaticFieldOfType(Class<?> type) {
|
||||
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
|
||||
boolean isStatic = Modifier.isStatic(field.getModifiers());
|
||||
boolean hasSameType = type.equals(field.getType());
|
||||
|
||||
if (isStatic && hasSameType) {
|
||||
return field;
|
||||
}
|
||||
}
|
||||
|
||||
Class<?> superclass = type.getSuperclass();
|
||||
return Object.class.equals(superclass) ? null
|
||||
: getStaticFieldOfType(superclass);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the name of the query class for the given domain class.
|
||||
*
|
||||
* @param domainClass
|
||||
* @return
|
||||
*/
|
||||
private String getQueryClassName(Class<?> domainClass) {
|
||||
|
||||
String simpleClassName = ClassUtils.getShortName(domainClass);
|
||||
return String.format("%s.Q%s%s",
|
||||
domainClass.getPackage().getName(),
|
||||
getClassBase(simpleClassName), domainClass.getSimpleName());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Analyzes the short class name and potentially returns the outer
|
||||
* class.
|
||||
*
|
||||
* @param shortName
|
||||
* @return
|
||||
*/
|
||||
private String getClassBase(String shortName) {
|
||||
|
||||
String[] parts = shortName.split("\\.");
|
||||
|
||||
if (parts.length < 2) {
|
||||
return "";
|
||||
}
|
||||
|
||||
return parts[0] + "_";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
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;
|
||||
import com.mysema.query.types.path.PathBuilder;
|
||||
import com.mysema.query.types.path.PathBuilderFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for implementing repositories using QueryDsl library.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@Repository
|
||||
public abstract class QueryDslRepositorySupport {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
private PathBuilderFactory builderFactory = new PathBuilderFactory();
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a {@link PathBuilder} for the given type.
|
||||
*
|
||||
* @param <T>
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
protected <T> PathBuilder<T> getBuilder(Class<T> type) {
|
||||
|
||||
return builderFactory.create(type);
|
||||
}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -50,7 +51,7 @@ import org.springframework.util.Assert;
|
||||
*/
|
||||
@org.springframework.stereotype.Repository
|
||||
public class SimpleJpaRepository<T, ID extends Serializable> implements
|
||||
JpaRepository<T, ID> {
|
||||
JpaRepository<T, ID>, JpaSpecificationExecutor<T> {
|
||||
|
||||
private final JpaEntityInformation<T, ID> entityInformation;
|
||||
private final EntityManager em;
|
||||
@@ -282,6 +283,19 @@ public class SimpleJpaRepository<T, ID extends Serializable> implements
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.JpaSpecificationExecutor#count
|
||||
* (org.springframework.data.jpa.domain.Specification)
|
||||
*/
|
||||
public Long count(Specification<T> spec) {
|
||||
|
||||
return getCountQuery(spec).getSingleResult();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
|
||||
@@ -11,6 +11,7 @@ Changes in version 1.0.0.M2
|
||||
* @Query annotated queries get validated on Query meta-model creation (DATAJPA-14)
|
||||
* Fixed dependency scopes and missing repository declarations (DATAJPA-33, DATAJPA-26)
|
||||
* Adapted meta-model API from Commons module (DATAJPA-32)
|
||||
* Added support for QueryDsl (DATAJPA-8)
|
||||
|
||||
Changes in version 1.0.0.M1 (2011-02-10) - https://jira.springsource.org/browse/DATAJPA/fixforversion/11786
|
||||
----------------------------------------
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.jpa.repository.QueryHints;
|
||||
@@ -39,7 +40,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface UserRepository extends JpaRepository<User, Integer>,
|
||||
UserRepositoryCustom {
|
||||
JpaSpecificationExecutor<User>, UserRepositoryCustom {
|
||||
|
||||
/**
|
||||
* Retrieve users by their lastname. The finder
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package org.springframework.data.jpa.repository.support;
|
||||
|
||||
import static junit.framework.Assert.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
@@ -27,10 +28,13 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Persistable;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.QueryDslPredicateExecutor;
|
||||
import org.springframework.data.jpa.repository.custom.CustomGenericJpaRepositoryFactory;
|
||||
import org.springframework.data.jpa.repository.custom.UserCustomExtendedRepository;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
|
||||
/**
|
||||
@@ -46,7 +50,7 @@ public class JpaRepositoryFactoryUnitTests {
|
||||
@Mock
|
||||
EntityManager entityManager;
|
||||
@Mock
|
||||
JpaEntityInformation<Object, Serializable> metadata;
|
||||
JpaEntityInformation metadata;
|
||||
|
||||
|
||||
@Before
|
||||
@@ -60,7 +64,7 @@ public class JpaRepositoryFactoryUnitTests {
|
||||
public <T, ID extends Serializable> JpaEntityInformation<T, ID> getEntityInformation(
|
||||
Class<T> domainClass) {
|
||||
|
||||
return (JpaEntityInformation<T, ID>) metadata;
|
||||
return metadata;
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -134,7 +138,7 @@ public class JpaRepositoryFactoryUnitTests {
|
||||
|
||||
|
||||
@Test(expected = UnsupportedOperationException.class)
|
||||
public void createsProxyWithCustomBaseClass() throws Exception {
|
||||
public void createsProxyWithCustomBaseClass() {
|
||||
|
||||
JpaRepositoryFactory factory =
|
||||
new CustomGenericJpaRepositoryFactory(entityManager);
|
||||
@@ -144,9 +148,25 @@ public class JpaRepositoryFactoryUnitTests {
|
||||
repository.customMethod(1);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void usesQueryDslRepositoryIfInterfaceImplementsExecutor() {
|
||||
|
||||
when(metadata.getJavaType()).thenReturn(User.class);
|
||||
assertEquals(QueryDslJpaRepository.class,
|
||||
factory.getRepositoryBaseClass(QueryDslSampleRepository.class));
|
||||
|
||||
QueryDslSampleRepository repository =
|
||||
factory.getRepository(QueryDslSampleRepository.class);
|
||||
assertEquals(QueryDslJpaRepository.class,
|
||||
((Advised) repository).getTargetClass());
|
||||
}
|
||||
|
||||
private interface SimpleSampleRepository extends
|
||||
JpaRepository<User, Integer> {
|
||||
|
||||
@Transactional
|
||||
User findOne(Integer id);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -186,13 +206,8 @@ public class JpaRepositoryFactoryUnitTests {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class to make the factory use {@link PersistableMetadata} .
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
private static abstract class User implements Persistable<Long> {
|
||||
private interface QueryDslSampleRepository extends SimpleSampleRepository,
|
||||
QueryDslPredicateExecutor<User> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2011 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;
|
||||
|
||||
/**
|
||||
* Stub for a generated QueryDsl query class.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class QSimpleEntityPathResolverUnitTests_Sample {
|
||||
|
||||
public QSimpleEntityPathResolverUnitTests_Sample field;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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 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;
|
||||
|
||||
import com.mysema.query.types.expr.BooleanExpression;
|
||||
import com.mysema.query.types.path.PathBuilder;
|
||||
import com.mysema.query.types.path.PathBuilderFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Integration test for {@link QueryDslJpaRepository}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@ContextConfiguration({ "classpath:infrastructure.xml" })
|
||||
@Transactional
|
||||
public class QueryDslJpaRepositoryTests {
|
||||
|
||||
@PersistenceContext
|
||||
EntityManager em;
|
||||
|
||||
QueryDslJpaRepository<User, Integer> repository;
|
||||
QUser user = new QUser("user");
|
||||
User dave, carter;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
|
||||
JpaEntityInformation<User, Integer> information =
|
||||
new JpaMetamodelEntityInformation<User, Integer>(User.class,
|
||||
em.getMetamodel());
|
||||
|
||||
repository = new QueryDslJpaRepository<User, Integer>(information, em);
|
||||
dave =
|
||||
repository.save(new User("Dave", "Matthews",
|
||||
"dave@matthews.com"));
|
||||
carter =
|
||||
repository.save(new User("Carter", "Beauford",
|
||||
"carter@beauford.com"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void executesPredicatesCorrectly() throws Exception {
|
||||
|
||||
BooleanExpression isCalledDave = user.firstname.eq("Dave");
|
||||
BooleanExpression isBeauford = user.lastname.eq("Beauford");
|
||||
|
||||
List<User> result = repository.findAll(isCalledDave.or(isBeauford));
|
||||
|
||||
assertThat(result.size(), is(2));
|
||||
assertThat(result, hasItems(carter, dave));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void executesStringBasedPredicatesCorrectly() throws Exception {
|
||||
|
||||
PathBuilder<User> builder = new PathBuilderFactory().create(User.class);
|
||||
|
||||
BooleanExpression isCalledDave =
|
||||
builder.getString("firstname").eq("Dave");
|
||||
BooleanExpression isBeauford =
|
||||
builder.getString("lastname").eq("Beauford");
|
||||
|
||||
List<User> result = repository.findAll(isCalledDave.or(isBeauford));
|
||||
|
||||
assertThat(result.size(), is(2));
|
||||
assertThat(result, hasItems(carter, dave));
|
||||
}
|
||||
}
|
||||
@@ -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,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2008-2011 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 org.junit.Test;
|
||||
import org.springframework.data.jpa.domain.sample.QUser;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.support.QueryDslJpaRepository.EntityPathResolver;
|
||||
import org.springframework.data.jpa.repository.support.QueryDslJpaRepository.SimpleEntityPathResolver;
|
||||
import org.springframework.data.jpa.repository.util.JpaClassUtilsUnitTests.NamedUser;
|
||||
import org.springframework.data.jpa.repository.util.QJpaClassUtilsUnitTests_NamedUser;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link SimpleEntityPathResolver}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class SimpleEntityPathResolverUnitTests {
|
||||
|
||||
EntityPathResolver resolver =
|
||||
QueryDslJpaRepository.SimpleEntityPathResolver.INSTANCE;
|
||||
|
||||
|
||||
@Test
|
||||
public void createsRepositoryFromDomainClassCorrectly() throws Exception {
|
||||
|
||||
assertThat(resolver.createPath(User.class), is(QUser.class));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void resolvesEntityPathForInnerClassCorrectly() throws Exception {
|
||||
|
||||
assertThat(resolver.createPath(NamedUser.class),
|
||||
is(QJpaClassUtilsUnitTests_NamedUser.class));
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void rejectsFoundClassWithoutStaticFieldOfSameType()
|
||||
throws Exception {
|
||||
|
||||
resolver.createPath(Sample.class);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsClassWithoutQueryClassConfrmingToTheNamingScheme()
|
||||
throws Exception {
|
||||
|
||||
resolver.createPath(QSimpleEntityPathResolverUnitTests_Sample.class);
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ public class JpaClassUtilsUnitTests {
|
||||
}
|
||||
|
||||
@Entity(name = "AnotherNamedUser")
|
||||
static class NamedUser {
|
||||
public static class NamedUser {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
10
template.mf
10
template.mf
@@ -6,15 +6,17 @@ Bundle-Version: ${project.version}
|
||||
Bundle-RequiredExecutionEnvironment: J2SE-1.5
|
||||
Export-Template:
|
||||
org.springframework.data.jpa.*;version="${project.version}"
|
||||
Import-Template:
|
||||
javax.persistence.*;version="${jpa.version:[=.=.=,+1.0.0)}",
|
||||
Import-Template:
|
||||
com.mysema.query.*;version="${querydsl.version:[=.=.=,+1.0.0)}",
|
||||
javax.persistence.*;version="${jpa.version:[=.=.=,+1.0.0)}",
|
||||
javax.annotation.*;version="[1.3.0,2.0.0)",
|
||||
org.aopalliance.*;version="[1.0.0,2.0.0)",
|
||||
org.slf4j.*;version="${slf4j.version:[=.=.=,+1.0.0)}",
|
||||
org.apache.openjpa.persistence.*;version="${openjpa.version:[=.=.=,+1.0.0)}";resolution:=optional,
|
||||
org.aspectj.*;version="${aspectj.version:[=.=.=,+1.0.0)}";resolution:=optional,
|
||||
org.eclipse.persistence.*;version="${eclipselink.version:[=.=.=,+1.0.0)}";resolution:=optional,
|
||||
org.hibernate.*;version="[3.5.3,4.0.0)";resolution:=optional,
|
||||
org.apache.openjpa.persistence.*;version="${openjpa.version:[=.=.=,+1.0.0)}";resolution:=optional,
|
||||
org.joda.time.*;version="[1.5.0,2.0.0)";resolution:=optional,
|
||||
org.slf4j.*;version="${slf4j.version:[=.=.=,+1.0.0)}",
|
||||
org.springframework.*;version="${spring.version:[=.=.=.=,+1.0.0)}",
|
||||
org.springframework.beans.factory.aspectj;version="${spring.version:[=.=.=.=,+1.0.0)}";resolution:=optional,
|
||||
org.w3c.*;version="0.0.0"
|
||||
|
||||
Reference in New Issue
Block a user