Migrated query-from-methodname algorithm to PartTree infrastructure.
Query creation is not String based anymore but rather leverages the PartTree infrastructure and builds a JPA criteria API query instead. Changed query lookup and execution accordingly.
This commit is contained in:
@@ -19,7 +19,6 @@ import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -57,65 +56,37 @@ public abstract class AbstractJpaQuery implements RepositoryQuery {
|
||||
|
||||
|
||||
/**
|
||||
* Creates a JPA {@link Query} with the given {@link ParameterBinder}.
|
||||
*
|
||||
* @param binder
|
||||
* @return
|
||||
* @return the parameters
|
||||
*/
|
||||
public Query createQuery(ParameterBinder binder) {
|
||||
public Parameters getParameters() {
|
||||
|
||||
return createQuery(em, binder);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a JPA {@link Query} to count the instances of the
|
||||
* {@link HadesQuery} to be returned.
|
||||
*
|
||||
* @param binder
|
||||
* @return
|
||||
* @return the em
|
||||
*/
|
||||
public Query createCountQuery() {
|
||||
public EntityManager getEntityManager() {
|
||||
|
||||
return createCountQuery(em);
|
||||
return em;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the {@link javax.persistence.Query} backing the
|
||||
* {@link QueryMethod} with the given parameters.
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @param em
|
||||
* @param parameters
|
||||
* @return
|
||||
* @see
|
||||
* org.springframework.data.repository.query.RepositoryQuery#execute(java
|
||||
* .lang.Object[])
|
||||
*/
|
||||
public Object execute(Object[] parameters) {
|
||||
|
||||
ParameterBinder binder =
|
||||
new ParameterBinder(this.parameters, parameters);
|
||||
|
||||
return execution.execute(this, binder);
|
||||
return doExecute(execution, parameters);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the actual {@link Query} to be executed. Has to return a fresh
|
||||
* instance on each call.
|
||||
*
|
||||
* @param em
|
||||
* @param binder
|
||||
* @return
|
||||
*/
|
||||
protected abstract Query createQuery(EntityManager em,
|
||||
ParameterBinder binder);
|
||||
protected abstract Object doExecute(JpaQueryExecution execution,
|
||||
Object[] parameters);
|
||||
|
||||
|
||||
/**
|
||||
* Returns the projecting count {@link Query} to be executed. Has to return
|
||||
* a fresh instance on each call.
|
||||
*
|
||||
* @param em
|
||||
* @return
|
||||
*/
|
||||
protected abstract Query createCountQuery(EntityManager em);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
|
||||
|
||||
/**
|
||||
* Base class for {@link String} based JPA queries.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public abstract class AbstractStringBasedJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
/**
|
||||
* Creates a new {@link AbstractStringBasedJpaQuery}.
|
||||
*
|
||||
* @param method
|
||||
* @param em
|
||||
*/
|
||||
public AbstractStringBasedJpaQuery(JpaQueryMethod method, EntityManager em) {
|
||||
|
||||
super(method, em);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.AbstractJpaQuery#doExecute
|
||||
* (org.springframework.data.jpa.repository.query.JpaQueryExecution,
|
||||
* java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
protected Object doExecute(JpaQueryExecution execution, Object[] parameters) {
|
||||
|
||||
ParameterBinder binder =
|
||||
new ParameterBinder(getParameters(), parameters);
|
||||
return execution.execute(this, binder);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a {@link Query} with the given {@link ParameterBinder}.
|
||||
*
|
||||
* @param binder
|
||||
* @return
|
||||
*/
|
||||
protected abstract Query createQuery(ParameterBinder binder);
|
||||
|
||||
|
||||
/**
|
||||
* Create a count {@link Query} with the given {@link ParameterBinder}.
|
||||
*
|
||||
* @param binder
|
||||
* @return
|
||||
*/
|
||||
protected abstract Query createCountQuery(ParameterBinder binder);
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.query.SimpleParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
|
||||
/**
|
||||
* Special {@link JpaQueryCreator} that creates a count projecting query.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class JpaCountQueryCreator extends JpaQueryCreator {
|
||||
|
||||
private final Class<?> domainClass;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaCountQueryCreator}.
|
||||
*
|
||||
* @param tree
|
||||
* @param parameters
|
||||
* @param domainClass
|
||||
* @param em
|
||||
*/
|
||||
public JpaCountQueryCreator(PartTree tree,
|
||||
SimpleParameterAccessor parameters, Class<?> domainClass,
|
||||
EntityManager em) {
|
||||
|
||||
super(tree, parameters, domainClass, em);
|
||||
this.domainClass = domainClass;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.JpaQueryCreator#finalize
|
||||
* (javax.persistence.criteria.Predicate,
|
||||
* org.springframework.data.domain.Sort,
|
||||
* javax.persistence.criteria.CriteriaQuery,
|
||||
* javax.persistence.criteria.CriteriaBuilder)
|
||||
*/
|
||||
@Override
|
||||
protected CriteriaQuery<Object> finalize(Predicate predicate, Sort sort,
|
||||
CriteriaQuery<Object> query, CriteriaBuilder builder) {
|
||||
|
||||
return query.select(builder.count(query.from(domainClass)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.repository.query.SimpleParameterAccessor;
|
||||
import org.springframework.data.repository.query.SimpleParameterAccessor.BindableParameterIterator;
|
||||
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
|
||||
import org.springframework.data.repository.query.parser.Part;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
/**
|
||||
* Query creator to create a {@link CriteriaQuery} from a {@link PartTree}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class JpaQueryCreator extends
|
||||
AbstractQueryCreator<CriteriaQuery<Object>, Predicate> {
|
||||
|
||||
private final CriteriaBuilder builder;
|
||||
private final Root<?> root;
|
||||
private final CriteriaQuery<Object> query;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link JpaQueryCreator}.
|
||||
*
|
||||
* @param tree
|
||||
* @param parameters
|
||||
* @param domainClass
|
||||
* @param em
|
||||
*/
|
||||
public JpaQueryCreator(PartTree tree, SimpleParameterAccessor parameters,
|
||||
Class<?> domainClass, EntityManager em) {
|
||||
|
||||
super(tree, parameters);
|
||||
|
||||
this.builder = em.getCriteriaBuilder();
|
||||
this.query = builder.createQuery();
|
||||
this.root = query.from(domainClass);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.parser.AbstractQueryCreator
|
||||
* #create(org.springframework.data.repository.query.parser.Part,
|
||||
* org.springframework
|
||||
* .data.repository.query.SimpleParameterAccessor.BindableParameterIterator)
|
||||
*/
|
||||
@Override
|
||||
protected Predicate create(Part part, BindableParameterIterator iterator) {
|
||||
|
||||
return toPredicate(part, root, iterator);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.parser.AbstractQueryCreator
|
||||
* #and(org.springframework.data.repository.query.parser.Part,
|
||||
* java.lang.Object,
|
||||
* org.springframework.data.repository.query.SimpleParameterAccessor
|
||||
* .BindableParameterIterator)
|
||||
*/
|
||||
@Override
|
||||
protected Predicate and(Part part, Predicate base,
|
||||
BindableParameterIterator iterator) {
|
||||
|
||||
return builder.and(base, toPredicate(part, root, iterator));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.repository.query.parser.AbstractQueryCreator
|
||||
* #or(java.lang.Object, java.lang.Object)
|
||||
*/
|
||||
@Override
|
||||
protected Predicate or(Predicate base, Predicate predicate) {
|
||||
|
||||
return builder.or(base, predicate);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finalizes the given {@link Predicate} and applies the given sort.
|
||||
* Delegates to
|
||||
* {@link #finalize(Predicate, Sort, CriteriaQuery, CriteriaBuilder)} and
|
||||
* hands it the current {@link CriteriaQuery} and {@link CriteriaBuilder}.
|
||||
*/
|
||||
@Override
|
||||
protected final CriteriaQuery<Object> finalize(Predicate predicate,
|
||||
Sort sort) {
|
||||
|
||||
return finalize(predicate, sort, query, builder);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Template method to finalize the given {@link Predicate} using the given
|
||||
* {@link CriteriaQuery} and {@link CriteriaBuilder}.
|
||||
*
|
||||
* @param predicate
|
||||
* @param sort
|
||||
* @param query
|
||||
* @param builder
|
||||
* @return
|
||||
*/
|
||||
protected CriteriaQuery<Object> finalize(Predicate predicate, Sort sort,
|
||||
CriteriaQuery<Object> query, CriteriaBuilder builder) {
|
||||
|
||||
return this.query.select(root).where(predicate)
|
||||
.orderBy(QueryUtils.toOrders(sort, root, builder));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a {@link Predicate} from the given {@link Part}.
|
||||
*
|
||||
* @param part
|
||||
* @param root
|
||||
* @param iterator
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private Predicate toPredicate(Part part, Root<?> root,
|
||||
BindableParameterIterator iterator) {
|
||||
|
||||
Expression<Object> path = root.get(part.getProperty());
|
||||
|
||||
switch (part.getType()) {
|
||||
|
||||
case BETWEEN:
|
||||
return builder.between(root.<Comparable> get(part.getProperty()),
|
||||
nextAsComparable(iterator), nextAsComparable(iterator));
|
||||
case GREATER_THAN:
|
||||
return builder.greaterThan(getComparablePath(root, part),
|
||||
nextAsComparable(iterator));
|
||||
case LESS_THAN:
|
||||
return builder.lessThan(getComparablePath(root, part),
|
||||
nextAsComparable(iterator));
|
||||
case IS_NULL:
|
||||
return root.isNull();
|
||||
case IS_NOT_NULL:
|
||||
return root.isNotNull();
|
||||
case LIKE:
|
||||
return builder.like(root.<String> get(part.getProperty()), iterator
|
||||
.next().toString());
|
||||
case NOT_LIKE:
|
||||
return builder.not(builder.like(root.<String> get(part
|
||||
.getProperty()), iterator.next().toString()));
|
||||
case SIMPLE_PROPERTY:
|
||||
return builder.equal(path, iterator.next());
|
||||
case NEGATING_SIMPLE_PROPERTY:
|
||||
return builder.notEqual(path, iterator.next());
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported keyword + "
|
||||
+ part.getType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns a path to a {@link Comparable}.
|
||||
*
|
||||
* @param root
|
||||
* @param part
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Expression<? extends Comparable> getComparablePath(Root<?> root,
|
||||
Part part) {
|
||||
|
||||
return root.get(part.getProperty());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next parameter from the given
|
||||
* {@link BindableParameterIterator} and expects it to be a
|
||||
* {@link Comparable}.
|
||||
*
|
||||
* @param iterator
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Comparable nextAsComparable(BindableParameterIterator iterator) {
|
||||
|
||||
Object next = iterator.next();
|
||||
Assert.isInstanceOf(Comparable.class, next,
|
||||
"Parameter has to implement Comparable to be bound correctly!");
|
||||
|
||||
return (Comparable<?>) next;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import javax.persistence.Query;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.SimpleParameterAccessor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
|
||||
@@ -44,7 +45,8 @@ public abstract class JpaQueryExecution {
|
||||
* @return
|
||||
*/
|
||||
|
||||
public Object execute(AbstractJpaQuery query, ParameterBinder binder) {
|
||||
public Object execute(AbstractStringBasedJpaQuery query,
|
||||
ParameterBinder binder) {
|
||||
|
||||
Assert.notNull(query);
|
||||
Assert.notNull(binder);
|
||||
@@ -57,6 +59,19 @@ public abstract class JpaQueryExecution {
|
||||
}
|
||||
|
||||
|
||||
public Object execute(PartTreeJpaQuery query, Object[] parameters) {
|
||||
|
||||
Assert.notNull(query);
|
||||
Assert.notNull(parameters);
|
||||
|
||||
try {
|
||||
return doExecute(query, parameters);
|
||||
} catch (NoResultException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Method to implement {@link AbstractHadesQuery} executions by single enum
|
||||
* values.
|
||||
@@ -65,8 +80,12 @@ public abstract class JpaQueryExecution {
|
||||
* @param binder
|
||||
* @return
|
||||
*/
|
||||
protected abstract Object doExecute(AbstractJpaQuery query,
|
||||
ParameterBinder binder);
|
||||
protected abstract Object doExecute(AbstractStringBasedJpaQuery query,
|
||||
ParameterBinder parameters);
|
||||
|
||||
|
||||
protected abstract Object doExecute(PartTreeJpaQuery query,
|
||||
Object[] parameters);
|
||||
|
||||
/**
|
||||
* Executes the {@link HadesQuery} to return a simple collection of
|
||||
@@ -75,12 +94,26 @@ public abstract class JpaQueryExecution {
|
||||
static class CollectionExecution extends JpaQueryExecution {
|
||||
|
||||
@Override
|
||||
protected Object doExecute(AbstractJpaQuery query,
|
||||
protected Object doExecute(AbstractStringBasedJpaQuery query,
|
||||
ParameterBinder binder) {
|
||||
|
||||
return binder.bindAndPrepare(query.createQuery(binder))
|
||||
.getResultList();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute
|
||||
* (org.springframework.data.jpa.repository.query.DerivedJpaQuery)
|
||||
*/
|
||||
@Override
|
||||
protected Object doExecute(PartTreeJpaQuery query, Object[] parameters) {
|
||||
|
||||
return query.createQuery(parameters).getResultList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,13 +122,23 @@ public abstract class JpaQueryExecution {
|
||||
*/
|
||||
static class PagedExecution extends JpaQueryExecution {
|
||||
|
||||
private final Parameters parameters;
|
||||
|
||||
|
||||
public PagedExecution(Parameters parameters) {
|
||||
|
||||
this.parameters = parameters;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object doExecute(AbstractJpaQuery repositoryQuery,
|
||||
protected Object doExecute(AbstractStringBasedJpaQuery repositoryQuery,
|
||||
ParameterBinder binder) {
|
||||
|
||||
// Execute query to compute total
|
||||
Query projection = binder.bind(repositoryQuery.createCountQuery());
|
||||
Query projection =
|
||||
binder.bind(repositoryQuery.createCountQuery(binder));
|
||||
Long total = (Long) projection.getSingleResult();
|
||||
|
||||
Query query =
|
||||
@@ -104,6 +147,30 @@ public abstract class JpaQueryExecution {
|
||||
return new PageImpl<Object>(query.getResultList(),
|
||||
binder.getPageable(), total);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute
|
||||
* (org.springframework.data.jpa.repository.query.DerivedJpaQuery,
|
||||
* java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object doExecute(PartTreeJpaQuery query, Object[] parameters) {
|
||||
|
||||
SimpleParameterAccessor accessor =
|
||||
new SimpleParameterAccessor(this.parameters, parameters);
|
||||
|
||||
Query countQuery = query.createCountQuery(parameters);
|
||||
Long total = (Long) countQuery.getSingleResult();
|
||||
|
||||
Query jpaQuery = query.createQuery(parameters);
|
||||
return new PageImpl<Object>(jpaQuery.getResultList(),
|
||||
accessor.getPageable(), total);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,11 +179,26 @@ public abstract class JpaQueryExecution {
|
||||
static class SingleEntityExecution extends JpaQueryExecution {
|
||||
|
||||
@Override
|
||||
protected Object doExecute(AbstractJpaQuery query,
|
||||
protected Object doExecute(AbstractStringBasedJpaQuery query,
|
||||
ParameterBinder binder) {
|
||||
|
||||
return binder.bind(query.createQuery(binder)).getSingleResult();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.JpaQueryExecution#doExecute
|
||||
* (org.springframework.data.jpa.repository.query.DerivedJpaQuery,
|
||||
* java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
protected Object doExecute(PartTreeJpaQuery query, Object[] parameters) {
|
||||
|
||||
return query.createQuery(parameters).getSingleResult();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -158,7 +240,7 @@ public abstract class JpaQueryExecution {
|
||||
* org.springframework.data.repository.query.ParameterBinder)
|
||||
*/
|
||||
@Override
|
||||
protected Object doExecute(AbstractJpaQuery query,
|
||||
protected Object doExecute(AbstractStringBasedJpaQuery query,
|
||||
ParameterBinder binder) {
|
||||
|
||||
int result = binder.bind(query.createQuery(binder)).executeUpdate();
|
||||
@@ -169,5 +251,12 @@ public abstract class JpaQueryExecution {
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object doExecute(PartTreeJpaQuery query, Object[] parameters) {
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +90,7 @@ public class JpaQueryLookupStrategy {
|
||||
protected RepositoryQuery resolveQuery(JpaQueryMethod method,
|
||||
EntityManager em) {
|
||||
|
||||
return SimpleJpaQuery.construct(method, em);
|
||||
return new PartTreeJpaQuery(method, em);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ public class JpaQueryMethod extends QueryMethod {
|
||||
}
|
||||
|
||||
if (isPageQuery()) {
|
||||
return new PagedExecution();
|
||||
return new PagedExecution(getParameters());
|
||||
}
|
||||
|
||||
if (isModifyingQuery()) {
|
||||
|
||||
@@ -32,7 +32,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
final class NamedQuery extends AbstractJpaQuery {
|
||||
final class NamedQuery extends AbstractStringBasedJpaQuery {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(NamedQuery.class);
|
||||
|
||||
@@ -121,9 +121,9 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
* .EntityManager, org.synyx.hades.dao.query.ParameterBinder)
|
||||
*/
|
||||
@Override
|
||||
protected Query createQuery(EntityManager em, ParameterBinder binder) {
|
||||
protected Query createQuery(ParameterBinder binder) {
|
||||
|
||||
return em.createNamedQuery(queryName);
|
||||
return getEntityManager().createNamedQuery(queryName);
|
||||
}
|
||||
|
||||
|
||||
@@ -134,11 +134,12 @@ final class NamedQuery extends AbstractJpaQuery {
|
||||
* persistence.EntityManager)
|
||||
*/
|
||||
@Override
|
||||
protected Query createCountQuery(EntityManager em) {
|
||||
protected Query createCountQuery(ParameterBinder binder) {
|
||||
|
||||
Query query = createQuery(em, null);
|
||||
Query query = createQuery(binder);
|
||||
String queryString = extractor.extractQueryString(query);
|
||||
|
||||
return em.createQuery(QueryUtils.createCountQueryFor(queryString));
|
||||
return getEntityManager().createQuery(
|
||||
QueryUtils.createCountQueryFor(queryString));
|
||||
}
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class ParameterBinder {
|
||||
if (hasNamedParameter(query) && parameter.isNamedParameter()) {
|
||||
query.setParameter(parameter.getName(), value);
|
||||
} else {
|
||||
query.setParameter(parameter.getParameterPosition(), value);
|
||||
query.setParameter(parameter.getIndex() + 1, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.springframework.data.jpa.repository.query;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
|
||||
import org.springframework.data.repository.query.SimpleParameterAccessor;
|
||||
import org.springframework.data.repository.query.parser.PartTree;
|
||||
|
||||
|
||||
/**
|
||||
* A {@link AbstractJpaQuery} implementation based on a {@link PartTree}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class PartTreeJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
private final PartTree tree;
|
||||
private final Class<?> domainClass;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link PartTreeJpaQuery}.
|
||||
*
|
||||
* @param method
|
||||
* @param em
|
||||
*/
|
||||
public PartTreeJpaQuery(JpaQueryMethod method, EntityManager em) {
|
||||
|
||||
super(method, em);
|
||||
|
||||
this.tree = new PartTree(method.getName(), method.getDomainClass());
|
||||
this.domainClass = method.getDomainClass();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.AbstractJpaQuery#createQuery
|
||||
* (javax.persistence.EntityManager,
|
||||
* org.springframework.data.jpa.repository.query.ParameterBinder)
|
||||
*/
|
||||
public Query createQuery(Object[] parameters) {
|
||||
|
||||
JpaQueryCreator jpaQueryCreator =
|
||||
new JpaQueryCreator(tree, new SimpleParameterAccessor(
|
||||
getParameters(), parameters), domainClass,
|
||||
getEntityManager());
|
||||
|
||||
return getEntityManager().createQuery(jpaQueryCreator.createQuery());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.springframework.data.jpa.repository.query.AbstractJpaQuery#
|
||||
* createCountQuery(javax.persistence.EntityManager)
|
||||
*/
|
||||
public Query createCountQuery(Object[] parameters) {
|
||||
|
||||
CriteriaQuery<Object> createQuery =
|
||||
new JpaCountQueryCreator(tree, new SimpleParameterAccessor(
|
||||
getParameters(), parameters), domainClass,
|
||||
getEntityManager()).createQuery();
|
||||
return getEntityManager().createQuery(createQuery);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.AbstractJpaQuery#doExecute
|
||||
* (org.springframework.data.jpa.repository.query.JpaQueryExecution,
|
||||
* java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
protected Object doExecute(JpaQueryExecution execution, Object[] parameters) {
|
||||
|
||||
return execution.execute(this, parameters);
|
||||
}
|
||||
}
|
||||
@@ -1,166 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008-2010 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.query;
|
||||
|
||||
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
import org.springframework.data.jpa.repository.utils.JpaClassUtils;
|
||||
import org.springframework.data.repository.query.Parameter;
|
||||
import org.springframework.data.repository.query.ParameterOutOfBoundsException;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
import org.springframework.data.repository.query.QueryCreationException;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.parser.OrderBySource;
|
||||
import org.springframework.data.repository.query.parser.PartSource;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
||||
/**
|
||||
* Class to encapsulate query creation logic for {@link QueryMethod}s.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
class QueryCreator {
|
||||
|
||||
private static final Logger LOG = LoggerFactory
|
||||
.getLogger(QueryCreator.class);
|
||||
private static final String INVALID_PARAMETER_SIZE =
|
||||
"You have to provide method arguments for each query "
|
||||
+ "criteria to construct the query correctly!";
|
||||
private static final String AND = "And";
|
||||
private static final String OR = "Or";
|
||||
|
||||
private JpaQueryMethod method;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link QueryCreator} for the given {@link QueryMethod}.
|
||||
*
|
||||
* @param queryMethod
|
||||
*/
|
||||
public QueryCreator(JpaQueryMethod queryMethod) {
|
||||
|
||||
Assert.isTrue(!queryMethod.isModifyingQuery());
|
||||
this.method = queryMethod;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a query from the underlying {@link QueryMethod}.
|
||||
*
|
||||
* @return the query string
|
||||
* @throws QueryCreationException in case the query can't be created
|
||||
*/
|
||||
public String constructQuery() {
|
||||
|
||||
StringBuilder queryBuilder = new StringBuilder();
|
||||
|
||||
Parameters parameters = method.getParameters().getBindableParameters();
|
||||
String methodName = method.getName();
|
||||
Class<?> domainClass = method.getDomainClass();
|
||||
|
||||
try {
|
||||
int parametersBound =
|
||||
doCreateQuery(new PartSource(methodName), domainClass,
|
||||
queryBuilder, parameters);
|
||||
|
||||
if (!method.isCorrectNumberOfParameters(parametersBound)) {
|
||||
throw QueryCreationException.create(method,
|
||||
INVALID_PARAMETER_SIZE);
|
||||
}
|
||||
} catch (ParameterOutOfBoundsException e) {
|
||||
throw QueryCreationException.create(method, e);
|
||||
}
|
||||
|
||||
String query = queryBuilder.toString();
|
||||
|
||||
LOG.debug("Created query '%s' from method %s", query, method.getName());
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
|
||||
private int doCreateQuery(PartSource source, Class<?> domainClass,
|
||||
StringBuilder builder, Parameters parameters) {
|
||||
|
||||
builder.append(getQueryString(READ_ALL_QUERY,
|
||||
JpaClassUtils.getEntityName(domainClass)));
|
||||
builder.append(" where ");
|
||||
|
||||
Iterator<PartSource> orParts = source.getParts(OR);
|
||||
int parametersBound = 0;
|
||||
|
||||
while (orParts.hasNext()) {
|
||||
|
||||
Iterator<PartSource> andParts = orParts.next().getParts(AND);
|
||||
|
||||
while (andParts.hasNext()) {
|
||||
PartSource andPart = andParts.next();
|
||||
|
||||
JpaQueryPart part =
|
||||
new JpaQueryPart(andPart.cleanedUp(), domainClass);
|
||||
Parameter parameter =
|
||||
part.getParameterRequired() ? parameters
|
||||
.getParameter(parametersBound) : null;
|
||||
|
||||
builder.append(part.getQueryPart(parameter));
|
||||
|
||||
if (andParts.hasNext()) {
|
||||
builder.append(" and ");
|
||||
}
|
||||
parametersBound += part.getNumberOfArguments();
|
||||
}
|
||||
|
||||
if (orParts.hasNext()) {
|
||||
builder.append(" or ");
|
||||
}
|
||||
}
|
||||
|
||||
if (source.hasOrderByClause()) {
|
||||
builder.append(" ").append(getClause(source.getOrderBySource()));
|
||||
}
|
||||
|
||||
return parametersBound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the final JPA order by clause.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getClause(OrderBySource source) {
|
||||
|
||||
List<String> parts = new ArrayList<String>();
|
||||
|
||||
for (Order order : source.toSort()) {
|
||||
parts.add(String.format("x.%s %s", order.getProperty(),
|
||||
QueryUtils.toJpaDirection(order)));
|
||||
|
||||
}
|
||||
|
||||
return "order by "
|
||||
+ StringUtils.collectionToDelimitedString(parts, ", ");
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,9 @@ package org.springframework.data.jpa.repository.query;
|
||||
import static java.util.regex.Pattern.*;
|
||||
import static org.springframework.data.jpa.repository.utils.JpaClassUtils.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -26,6 +28,9 @@ import java.util.regex.Pattern;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Parameter;
|
||||
import javax.persistence.Query;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.domain.Sort.Order;
|
||||
@@ -107,7 +112,7 @@ public abstract class QueryUtils {
|
||||
* @param clazzName
|
||||
* @return
|
||||
*/
|
||||
public static String getQueryString(String template, String clazzName) {
|
||||
private static String getQueryString(String template, String clazzName) {
|
||||
|
||||
Assert.hasText(clazzName, "Classname must not be null or empty!");
|
||||
|
||||
@@ -265,4 +270,48 @@ public abstract class QueryUtils {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turns the given {@link Sort} into
|
||||
* {@link javax.persistence.criteria.Order}s.
|
||||
*
|
||||
* @param sort
|
||||
* @param root
|
||||
* @param cb
|
||||
* @return
|
||||
*/
|
||||
public static List<javax.persistence.criteria.Order> toOrders(Sort sort,
|
||||
Root<?> root, CriteriaBuilder cb) {
|
||||
|
||||
List<javax.persistence.criteria.Order> orders =
|
||||
new ArrayList<javax.persistence.criteria.Order>();
|
||||
|
||||
if (sort == null) {
|
||||
return orders;
|
||||
}
|
||||
|
||||
for (org.springframework.data.domain.Sort.Order order : sort) {
|
||||
orders.add(toJpaOrder(order, root, cb));
|
||||
}
|
||||
|
||||
return orders;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a criteria API {@link javax.persistence.criteria.Order} from the
|
||||
* given {@link Order}.
|
||||
*
|
||||
* @param order
|
||||
* @param root
|
||||
* @param cb
|
||||
* @return
|
||||
*/
|
||||
private static javax.persistence.criteria.Order toJpaOrder(Order order,
|
||||
Root<?> root, CriteriaBuilder cb) {
|
||||
|
||||
Expression<?> expression = root.get(order.getProperty());
|
||||
return order.isAscending() ? cb.asc(expression) : cb.desc(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import javax.persistence.QueryHint;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.data.repository.query.QueryCreationException;
|
||||
import org.springframework.data.repository.query.QueryMethod;
|
||||
import org.springframework.data.repository.query.RepositoryQuery;
|
||||
|
||||
@@ -35,7 +34,7 @@ import org.springframework.data.repository.query.RepositoryQuery;
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
final class SimpleJpaQuery extends AbstractJpaQuery {
|
||||
final class SimpleJpaQuery extends AbstractStringBasedJpaQuery {
|
||||
|
||||
private static final Logger LOG = LoggerFactory
|
||||
.getLogger(SimpleJpaQuery.class);
|
||||
@@ -63,19 +62,6 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link SimpleJpaQuery} that constructs the query from the
|
||||
* given {@link QueryMethod}.
|
||||
*
|
||||
* @param method
|
||||
* @param em
|
||||
*/
|
||||
SimpleJpaQuery(JpaQueryMethod method, EntityManager em) {
|
||||
|
||||
this(method, em, new QueryCreator(method).constructQuery());
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -84,25 +70,27 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
|
||||
* .EntityManager, org.synyx.hades.dao.query.ParameterBinder)
|
||||
*/
|
||||
@Override
|
||||
protected Query createQuery(EntityManager em, ParameterBinder binder) {
|
||||
public Query createQuery(ParameterBinder binder) {
|
||||
|
||||
String query =
|
||||
QueryUtils.applySorting(queryString, binder.getSort(), alias);
|
||||
|
||||
return applyHints(em.createQuery(query));
|
||||
return applyHints(getEntityManager().createQuery(query));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.synyx.hades.dao.query.AbstractHadesQuery#createCountQuery(javax.
|
||||
* persistence.EntityManager)
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.AbstractStringBasedJpaQuery
|
||||
* #createCountQuery(org.springframework.data.jpa.repository.query.
|
||||
* ParameterBinder)
|
||||
*/
|
||||
@Override
|
||||
protected Query createCountQuery(EntityManager em) {
|
||||
protected Query createCountQuery(ParameterBinder binder) {
|
||||
|
||||
return applyHints(em.createQuery(countQuery));
|
||||
return applyHints(getEntityManager().createQuery(countQuery));
|
||||
}
|
||||
|
||||
|
||||
@@ -138,30 +126,7 @@ final class SimpleJpaQuery extends AbstractJpaQuery {
|
||||
|
||||
String query = queryMethod.getAnnotatedQuery();
|
||||
|
||||
return query == null ? null : new SimpleJpaQuery(queryMethod, em,
|
||||
query);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a {@link HadesQuery} from the given {@link QueryMethod}.
|
||||
*
|
||||
* @param queryMethod
|
||||
* @param em
|
||||
* @return
|
||||
*/
|
||||
public static RepositoryQuery construct(JpaQueryMethod queryMethod,
|
||||
EntityManager em) {
|
||||
|
||||
if (queryMethod.isModifyingQuery()) {
|
||||
throw QueryCreationException
|
||||
.create(queryMethod,
|
||||
"Cannot create query from method name "
|
||||
+ "for modifying query. Use @Query or @NamedQuery to "
|
||||
+ "declare the query to execute. Do not use CREATE as "
|
||||
+ "strategy to lookup queries!");
|
||||
}
|
||||
|
||||
return new SimpleJpaQuery(queryMethod, em);
|
||||
return query == null ? null
|
||||
: new SimpleJpaQuery(queryMethod, em, query);
|
||||
}
|
||||
}
|
||||
@@ -26,8 +26,6 @@ import javax.persistence.EntityManager;
|
||||
import javax.persistence.TypedQuery;
|
||||
import javax.persistence.criteria.CriteriaBuilder;
|
||||
import javax.persistence.criteria.CriteriaQuery;
|
||||
import javax.persistence.criteria.Expression;
|
||||
import javax.persistence.criteria.Order;
|
||||
import javax.persistence.criteria.Predicate;
|
||||
import javax.persistence.criteria.Root;
|
||||
|
||||
@@ -464,28 +462,4 @@ public class SimpleJpaRepository<T, ID extends Serializable> extends
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
private List<Order> toOrders(Sort sort, Root<T> root, CriteriaBuilder cb) {
|
||||
|
||||
List<Order> orders = new ArrayList<Order>();
|
||||
|
||||
if (sort == null) {
|
||||
return orders;
|
||||
}
|
||||
|
||||
for (org.springframework.data.domain.Sort.Order order : sort) {
|
||||
orders.add(toJpaOrder(order, root, cb));
|
||||
}
|
||||
|
||||
return orders;
|
||||
}
|
||||
|
||||
|
||||
private Order toJpaOrder(org.springframework.data.domain.Sort.Order order,
|
||||
Root<T> root, CriteriaBuilder cb) {
|
||||
|
||||
Expression<?> expression = root.get(order.getProperty());
|
||||
return order.isAscending() ? cb.asc(expression) : cb.desc(expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration(value = "classpath:openjpa.xml", inheritLocations = true)
|
||||
public class OpenJpaNamespaceUserDaoTests extends
|
||||
NamespaceUserRepositoryTests {
|
||||
public class OpenJpaNamespaceUserDaoTests extends NamespaceUserRepositoryTests {
|
||||
|
||||
}
|
||||
@@ -255,8 +255,9 @@ public class UserRepositoryTests {
|
||||
flushTestUsers();
|
||||
repository.renameAllUsersTo("newLastname");
|
||||
|
||||
assertEquals(repository.count().intValue(),
|
||||
repository.findByLastname("newLastname").size());
|
||||
Integer expected = repository.count().intValue();
|
||||
assertThat(repository.findByLastname("newLastname").size(),
|
||||
is(expected));
|
||||
}
|
||||
|
||||
|
||||
@@ -546,8 +547,10 @@ public class UserRepositoryTests {
|
||||
firstUser = repository.save(firstUser);
|
||||
secondUser = repository.save(secondUser);
|
||||
|
||||
assertTrue(repository.findByFirstnameOrLastname("Oliver", "Arrasz")
|
||||
.containsAll(Arrays.asList(firstUser, secondUser)));
|
||||
List<User> result =
|
||||
repository.findByFirstnameOrLastname("Oliver", "Arrasz");
|
||||
assertThat(result.size(), is(2));
|
||||
assertThat(result, hasItems(firstUser, secondUser));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,6 +25,5 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration(locations = "classpath:config/namespace-autoconfig-context.xml")
|
||||
public class RepositoryAutoConfigTests extends
|
||||
AbstractRepositoryConfigTests {
|
||||
public class RepositoryAutoConfigTests extends AbstractRepositoryConfigTests {
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration(locations = "classpath:config/namespace-autoconfig-typefilter-context.xml")
|
||||
public class TypeFilterConfigTest extends
|
||||
AbstractRepositoryConfigTests {
|
||||
public class TypeFilterConfigTest extends AbstractRepositoryConfigTests {
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
|
||||
@@ -25,13 +25,12 @@ import org.springframework.data.repository.support.RepositorySupport;
|
||||
|
||||
|
||||
/**
|
||||
* Sample implementation of a custom {@link JpaRepositoryFactory} to use
|
||||
* a custom repository base class.
|
||||
* Sample implementation of a custom {@link JpaRepositoryFactory} to use a
|
||||
* custom repository base class.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public class CustomGenericJpaRepositoryFactory extends
|
||||
JpaRepositoryFactory {
|
||||
public class CustomGenericJpaRepositoryFactory extends JpaRepositoryFactory {
|
||||
|
||||
/**
|
||||
* @param entityManager
|
||||
|
||||
@@ -24,10 +24,10 @@ import org.springframework.data.repository.NoRepositoryBean;
|
||||
|
||||
|
||||
/**
|
||||
* Extension of {@link Repository} to be added on a custom repository
|
||||
* base class. This tests the facility to implement custom base class
|
||||
* functionality for all repository instances derived from this interface and
|
||||
* implementation base class.
|
||||
* Extension of {@link Repository} to be added on a custom repository base
|
||||
* class. This tests the facility to implement custom base class functionality
|
||||
* for all repository instances derived from this interface and implementation
|
||||
* base class.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
|
||||
@@ -19,13 +19,13 @@ package org.springframework.data.jpa.repository.custom;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Custom Extended DAO interface for a {@code User}. This relies on the custom
|
||||
* intermediate DAO interface {@link CustomGenericRepository}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
public interface UserCustomExtendedRepository extends CustomGenericRepository<User, Integer> {
|
||||
public interface UserCustomExtendedRepository extends
|
||||
CustomGenericRepository<User, Integer> {
|
||||
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public class JpaQueryExecutionUnitTests {
|
||||
@Mock
|
||||
EntityManager em;
|
||||
@Mock
|
||||
AbstractJpaQuery jpaQuery;
|
||||
AbstractStringBasedJpaQuery jpaQuery;
|
||||
@Mock
|
||||
ParameterBinder binder;
|
||||
@Mock
|
||||
@@ -81,11 +81,28 @@ public class JpaQueryExecutionUnitTests {
|
||||
assertThat(new JpaQueryExecution() {
|
||||
|
||||
@Override
|
||||
protected Object doExecute(AbstractJpaQuery query,
|
||||
protected Object doExecute(AbstractStringBasedJpaQuery query,
|
||||
ParameterBinder binder) {
|
||||
|
||||
throw new NoResultException();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see
|
||||
* org.springframework.data.jpa.repository.query.JpaQueryExecution
|
||||
* #doExecute
|
||||
* (org.springframework.data.jpa.repository.query.PartTreeJpaQuery,
|
||||
* java.lang.Object[])
|
||||
*/
|
||||
@Override
|
||||
protected Object doExecute(PartTreeJpaQuery query,
|
||||
Object[] parameters) {
|
||||
|
||||
return null;
|
||||
}
|
||||
}.execute(jpaQuery, binder), is(nullValue()));
|
||||
}
|
||||
|
||||
@@ -123,11 +140,18 @@ public class JpaQueryExecutionUnitTests {
|
||||
static class StubQueryExecution extends JpaQueryExecution {
|
||||
|
||||
@Override
|
||||
protected Object doExecute(AbstractJpaQuery query,
|
||||
protected Object doExecute(AbstractStringBasedJpaQuery query,
|
||||
ParameterBinder binder) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Object doExecute(PartTreeJpaQuery query, Object[] parameters) {
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static interface Dummy {
|
||||
|
||||
@@ -57,7 +57,7 @@ public class JpaQueryMethodUnitTests {
|
||||
EntityManager em;
|
||||
|
||||
Method daoMethod, invalidReturnType, pageableAndSort, pageableTwice,
|
||||
sortableTwice, modifyingMethod;
|
||||
sortableTwice, modifyingMethod;
|
||||
|
||||
|
||||
/**
|
||||
@@ -67,24 +67,24 @@ public class JpaQueryMethodUnitTests {
|
||||
public void setUp() throws Exception {
|
||||
|
||||
daoMethod =
|
||||
UserRepository.class.getMethod("findByLastname", String.class);
|
||||
UserRepository.class.getMethod("findByLastname", String.class);
|
||||
|
||||
invalidReturnType =
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Pageable.class);
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Pageable.class);
|
||||
pageableAndSort =
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Pageable.class, Sort.class);
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Pageable.class, Sort.class);
|
||||
pageableTwice =
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Pageable.class, Pageable.class);
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Pageable.class, Pageable.class);
|
||||
|
||||
sortableTwice =
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Sort.class, Sort.class);
|
||||
InvalidDao.class.getMethod(METHOD_NAME, String.class,
|
||||
Sort.class, Sort.class);
|
||||
modifyingMethod =
|
||||
UserRepository.class
|
||||
.getMethod("renameAllUsersTo", String.class);
|
||||
UserRepository.class
|
||||
.getMethod("renameAllUsersTo", String.class);
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +95,6 @@ public class JpaQueryMethodUnitTests {
|
||||
|
||||
assertEquals("User.findByLastname", method.getNamedQueryName());
|
||||
assertThat(method.getExecution(), is(CollectionExecution.class));
|
||||
assertEquals("select x from User x where x.lastname = ?1",
|
||||
new QueryCreator(method).constructQuery());
|
||||
}
|
||||
|
||||
|
||||
@@ -137,11 +135,11 @@ public class JpaQueryMethodUnitTests {
|
||||
assertNull(method.getAnnotatedQuery());
|
||||
|
||||
Method daoMethod =
|
||||
UserRepository.class
|
||||
.getMethod("findByHadesQuery", String.class);
|
||||
UserRepository.class
|
||||
.getMethod("findByHadesQuery", String.class);
|
||||
|
||||
assertNotNull(new JpaQueryMethod(daoMethod, extractor, em)
|
||||
.getAnnotatedQuery());
|
||||
.getAnnotatedQuery());
|
||||
}
|
||||
|
||||
|
||||
@@ -192,11 +190,11 @@ public class JpaQueryMethodUnitTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsPageablesOnPersistenceProvidersNotExtractingQueries()
|
||||
throws Exception {
|
||||
throws Exception {
|
||||
|
||||
Method method =
|
||||
UserRepository.class.getMethod("findByFirstname",
|
||||
Pageable.class, String.class);
|
||||
UserRepository.class.getMethod("findByFirstname",
|
||||
Pageable.class, String.class);
|
||||
|
||||
when(extractor.canExtractQuery()).thenReturn(false);
|
||||
|
||||
@@ -208,7 +206,7 @@ public class JpaQueryMethodUnitTests {
|
||||
public void recognizesModifyingMethod() {
|
||||
|
||||
JpaQueryMethod method =
|
||||
new JpaQueryMethod(modifyingMethod, extractor, em);
|
||||
new JpaQueryMethod(modifyingMethod, extractor, em);
|
||||
assertTrue(method.isModifyingQuery());
|
||||
}
|
||||
|
||||
@@ -217,8 +215,8 @@ public class JpaQueryMethodUnitTests {
|
||||
public void rejectsModifyingMethodWithPageable() throws Exception {
|
||||
|
||||
Method method =
|
||||
InvalidDao.class.getMethod("updateMethod", String.class,
|
||||
Pageable.class);
|
||||
InvalidDao.class.getMethod("updateMethod", String.class,
|
||||
Pageable.class);
|
||||
|
||||
new JpaQueryMethod(method, extractor, em);
|
||||
}
|
||||
@@ -228,8 +226,8 @@ public class JpaQueryMethodUnitTests {
|
||||
public void rejectsModifyingMethodWithSort() throws Exception {
|
||||
|
||||
Method method =
|
||||
InvalidDao.class.getMethod("updateMethod", String.class,
|
||||
Sort.class);
|
||||
InvalidDao.class.getMethod("updateMethod", String.class,
|
||||
Sort.class);
|
||||
|
||||
new JpaQueryMethod(method, extractor, em);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -31,7 +32,6 @@ import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.data.jpa.repository.query.QueryCreatorUnitTests.SampleEmbeddable;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.data.repository.query.Parameters;
|
||||
|
||||
@@ -183,8 +183,8 @@ public class ParameterBinderUnitTests {
|
||||
public void bindsEmbeddableCorrectly() throws Exception {
|
||||
|
||||
Method method =
|
||||
QueryCreatorUnitTests.class.getMethod("findByEmbeddable",
|
||||
SampleEmbeddable.class);
|
||||
getClass()
|
||||
.getMethod("findByEmbeddable", SampleEmbeddable.class);
|
||||
Parameters parameters = new Parameters(method);
|
||||
SampleEmbeddable embeddable = new SampleEmbeddable();
|
||||
|
||||
@@ -204,4 +204,24 @@ public class ParameterBinderUnitTests {
|
||||
new Object[] { "name", sort });
|
||||
assertThat(binder.getSort(), is(sort));
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByEmbeddable(SampleEmbeddable embeddable) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
static class SampleEntity {
|
||||
|
||||
private SampleEmbeddable embeddable;
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
@SuppressWarnings("unused")
|
||||
static class SampleEmbeddable {
|
||||
|
||||
private String foo;
|
||||
private String bar;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,327 +0,0 @@
|
||||
/*
|
||||
* Copyright 2008-2010 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.query;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.jpa.domain.sample.User;
|
||||
import org.springframework.data.jpa.repository.query.JpaQueryMethodUnitTests.InvalidDao;
|
||||
import org.springframework.data.repository.query.QueryCreationException;
|
||||
|
||||
|
||||
/**
|
||||
* Unit test for {@link QueryCreator}.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class QueryCreatorUnitTests {
|
||||
|
||||
private Method method;
|
||||
|
||||
@Mock
|
||||
QueryExtractor extractor;
|
||||
@Mock
|
||||
EntityManager em;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws SecurityException, NoSuchMethodException {
|
||||
|
||||
method =
|
||||
QueryCreatorUnitTests.class.getMethod(
|
||||
"findByFirstnameAndMethod", String.class);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = QueryCreationException.class)
|
||||
public void rejectsInvalidProperty() throws Exception {
|
||||
|
||||
JpaQueryMethod finderMethod = new JpaQueryMethod(method, extractor, em);
|
||||
new QueryCreator(finderMethod).constructQuery();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void splitsKeywordsCorrectly() throws SecurityException,
|
||||
NoSuchMethodException {
|
||||
|
||||
method =
|
||||
QueryCreatorUnitTests.class.getMethod(
|
||||
"findByNameOrOrganization", String.class, String.class);
|
||||
|
||||
assertCreatesQueryForMethod(
|
||||
"where x.name = :name or x.organization = :organization",
|
||||
method);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws NoSuchMethodException
|
||||
* @throws SecurityException
|
||||
* @see #265
|
||||
* @throws Exception
|
||||
*/
|
||||
@Test
|
||||
public void createsQueryWithEmbeddableCorrectly() throws SecurityException,
|
||||
NoSuchMethodException {
|
||||
|
||||
method =
|
||||
getClass()
|
||||
.getMethod("findByEmbeddable", SampleEmbeddable.class);
|
||||
|
||||
assertCreatesQueryForMethod("where x.embeddable = :embeddable", method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void createsQueryWithBetweenKeywordCorrectly() throws Exception {
|
||||
|
||||
method =
|
||||
getClass().getMethod("findByStartDateBetweenAndName",
|
||||
Date.class, Date.class, String.class);
|
||||
|
||||
assertCreatesQueryForMethod(
|
||||
"where x.startDate between :first and :second and x.name = :name",
|
||||
method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void createsQueryWithLessThanKeywordCorrectly() throws Exception {
|
||||
|
||||
method = getClass().getMethod("findByAgeLessThan", int.class);
|
||||
|
||||
assertCreatesQueryForMethod("where x.age < :age", method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void createsQueryWithGreaterThanKeywordCorrectly() throws Exception {
|
||||
|
||||
method = getClass().getMethod("findByAgeGreaterThan", int.class);
|
||||
|
||||
assertCreatesQueryForMethod("where x.age > :age", method);
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void rejectsModifyingMethodWithoutBacking()
|
||||
throws SecurityException, NoSuchMethodException {
|
||||
|
||||
Method invalidModifyingMethod =
|
||||
InvalidDao.class.getMethod("updateMethod", String.class);
|
||||
|
||||
JpaQueryMethod method =
|
||||
new JpaQueryMethod(invalidModifyingMethod, extractor, em);
|
||||
|
||||
new QueryCreator(method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parsesLikeOperatorCorrectly() throws Exception {
|
||||
|
||||
method = getClass().getMethod("findByNameLike", String.class);
|
||||
assertCreatesQueryForMethod("where x.name like :name", method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parsesNotOperatorCorrectly() throws Exception {
|
||||
|
||||
method = getClass().getMethod("findByNameNot", String.class);
|
||||
assertCreatesQueryForMethod("where x.name <> :name", method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parsesNotNullOperatorCorrectly() throws Exception {
|
||||
|
||||
method = getClass().getMethod("findByNameNotNull");
|
||||
assertCreatesQueryForMethod("where x.name is not null", method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parsesLikeCorrectly() throws Exception {
|
||||
|
||||
method = getClass().getMethod("findByNameLike", String.class);
|
||||
assertCreatesQueryForMethod("where x.name like :name", method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parsesNotLikeCorrectly() throws Exception {
|
||||
|
||||
method = getClass().getMethod("findByNameNotLike", String.class);
|
||||
assertCreatesQueryForMethod("where x.name not like :name", method);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void parsesOrderByClauseCorrectly() throws Exception {
|
||||
|
||||
method =
|
||||
getClass().getMethod("findByNameOrderByOrganizationDesc",
|
||||
String.class);
|
||||
assertCreatesQueryForMethod(
|
||||
"where x.name = :name order by x.organization desc", method);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that the query created for the given {@link Method} results in a
|
||||
* query ending with the given {@link String}.
|
||||
*
|
||||
* @param queryEnd
|
||||
* @param method
|
||||
*/
|
||||
private void assertCreatesQueryForMethod(String queryEnd, Method method) {
|
||||
|
||||
JpaQueryMethod queryMethod = new JpaQueryMethod(method, extractor, em);
|
||||
String result = new QueryCreator(queryMethod).constructQuery();
|
||||
assertThat(result, endsWith(queryEnd));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sample method to test failing query creation.
|
||||
*
|
||||
* @param firstname
|
||||
* @return
|
||||
*/
|
||||
public User findByFirstnameAndMethod(String firstname) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A method to check that query keyowrds are considered correctly. The
|
||||
* {@link QueryCreator} must not detect the {@code Or} in
|
||||
* {@code Organization} as keyword.
|
||||
*
|
||||
* @param name
|
||||
* @param organization
|
||||
* @return
|
||||
*/
|
||||
public SampleEntity findByNameOrOrganization(String name,
|
||||
String organization) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sample method to create a finder query for that references an
|
||||
* {@link Embeddable}.
|
||||
*
|
||||
* @see #265
|
||||
* @param embeddable
|
||||
* @return
|
||||
*/
|
||||
public SampleEntity findByEmbeddable(SampleEmbeddable embeddable) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByStartDateBetweenAndName(Date first, Date second,
|
||||
String name) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByAgeLessThan(int age) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByAgeGreaterThan(int age) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByNameLike(String name) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByNameNotLike(String name) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByNameNot(String name) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByNameNotNull() {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public SampleEntity findByNameOrderByOrganizationDesc(String name) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample class for keyword split check.
|
||||
*
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
static class SampleEntity {
|
||||
|
||||
private String organization;
|
||||
private String name;
|
||||
|
||||
private Date startDate;
|
||||
private int age;
|
||||
|
||||
private SampleEmbeddable embeddable;
|
||||
}
|
||||
|
||||
@Embeddable
|
||||
@SuppressWarnings("unused")
|
||||
static class SampleEmbeddable {
|
||||
|
||||
private String foo;
|
||||
private String bar;
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ public class SimpleJpaQueryUnitTests {
|
||||
public void appliesHintsCorrectly() throws Exception {
|
||||
|
||||
SimpleJpaQuery hadesQuery = new SimpleJpaQuery(method, em, "foobar");
|
||||
hadesQuery.createQuery(em, new ParameterBinder(method.getParameters(),
|
||||
hadesQuery.createQuery(new ParameterBinder(method.getParameters(),
|
||||
new Object[] { "gierke" }));
|
||||
|
||||
verify(query).setHint("foo", "bar");
|
||||
@@ -86,6 +86,6 @@ public class SimpleJpaQueryUnitTests {
|
||||
SimpleJpaQuery hadesQuery =
|
||||
new SimpleJpaQuery(method, em, "select u from User u");
|
||||
|
||||
assertThat(hadesQuery.createCountQuery(em), is(query));
|
||||
assertThat(hadesQuery.createCountQuery(null), is(query));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,8 +72,8 @@ public class JpaRepositoryFactoryBeanUnitTests {
|
||||
|
||||
// Setup standard factory configuration
|
||||
factory =
|
||||
JpaRepositoryFactoryBean.create(
|
||||
SimpleSampleRepository.class, entityManager);
|
||||
JpaRepositoryFactoryBean.create(SimpleSampleRepository.class,
|
||||
entityManager);
|
||||
factory.setEntityManager(entityManager);
|
||||
}
|
||||
|
||||
@@ -139,8 +139,7 @@ public class JpaRepositoryFactoryBeanUnitTests {
|
||||
|
||||
JpaRepositoryFactoryBean<SampleRepository> factory =
|
||||
|
||||
JpaRepositoryFactoryBean.create(SampleRepository.class,
|
||||
entityManager);
|
||||
JpaRepositoryFactoryBean.create(SampleRepository.class, entityManager);
|
||||
|
||||
try {
|
||||
factory.afterPropertiesSet();
|
||||
@@ -166,8 +165,8 @@ public class JpaRepositoryFactoryBeanUnitTests {
|
||||
void someSampleMethod();
|
||||
}
|
||||
|
||||
private interface SampleRepository extends
|
||||
JpaRepository<User, Integer>, SampleCustomDao {
|
||||
private interface SampleRepository extends JpaRepository<User, Integer>,
|
||||
SampleCustomDao {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,8 +131,7 @@ public class JpaRepositoryFactoryUnitTests {
|
||||
dao.customMethod(1);
|
||||
}
|
||||
|
||||
private interface SimpleSampleDao extends
|
||||
JpaRepository<User, Integer> {
|
||||
private interface SimpleSampleDao extends JpaRepository<User, Integer> {
|
||||
|
||||
@Transactional
|
||||
User readByPrimaryKey(Integer primaryKey);
|
||||
|
||||
Reference in New Issue
Block a user