DATAJPA-71 - CriteriaQueries created from derived queries are cached now if possible.

We now create the CriteriaQuery instance for derived queries only once in case the query method does not contain a dynamic sort parameter (either through a plain Sort or via a Pageable).
This commit is contained in:
Oliver Gierke
2011-07-19 09:04:39 +02:00
parent 05e91c3af0
commit c164ac3f20
5 changed files with 143 additions and 34 deletions

View File

@@ -22,7 +22,6 @@ import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.parser.PartTree;
@@ -39,13 +38,13 @@ public class JpaCountQueryCreator extends JpaQueryCreator {
*
* @param tree
* @param domainClass
* @param accessor
* @param parameters
* @param em
*/
public JpaCountQueryCreator(PartTree tree, Class<?> domainClass,
ParameterAccessor accessor, Parameters parameters, EntityManager em) {
Parameters parameters, EntityManager em) {
super(tree, domainClass, accessor, parameters, em);
super(tree, domainClass, parameters, em);
}

View File

@@ -66,9 +66,9 @@ public class JpaQueryCreator extends
* @param em
*/
public JpaQueryCreator(PartTree tree, Class<?> domainClass,
ParameterAccessor accessor, Parameters parameters, EntityManager em) {
Parameters parameters, EntityManager em) {
super(tree, accessor);
super(tree);
this.builder = em.getCriteriaBuilder();
this.query = builder.createQuery().distinct(tree.isDistinct());
@@ -80,6 +80,8 @@ public class JpaQueryCreator extends
/**
* Returns all {@link ParameterExpression} created when creating the query.
*
* @return the parameterExpressions
*/
public List<ParameterExpression<?>> getParameterExpressions() {

View File

@@ -23,10 +23,11 @@ import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.query.Parameters;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;
/**
@@ -40,6 +41,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
private final PartTree tree;
private final Parameters parameters;
private final QueryPreparer query;
private final QueryPreparer countQuery;
/**
* Creates a new {@link PartTreeJpaQuery}.
@@ -54,6 +58,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
this.domainClass = method.getEntityInformation().getJavaType();
this.tree = new PartTree(method.getName(), domainClass);
this.parameters = method.getParameters();
this.query = new QueryPreparer(tree, domainClass, parameters);
this.countQuery = new CountQueryPreparer(tree, domainClass, parameters);
}
@@ -68,18 +75,7 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
@Override
public Query createQuery(Object[] values) {
ParameterAccessor accessor =
new ParametersParameterAccessor(parameters, values);
JpaQueryCreator creator =
new JpaQueryCreator(tree, domainClass, accessor, parameters,
getEntityManager());
CriteriaQuery<?> source = creator.createQuery();
TypedQuery<?> jpaQuery = getEntityManager().createQuery(source);
getBinder(values, creator.getParameterExpressions()).bindAndPrepare(
jpaQuery);
return jpaQuery;
return query.createQuery(values);
}
@@ -92,24 +88,133 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
@Override
public Query createCountQuery(Object[] values) {
ParameterAccessor accessor =
new ParametersParameterAccessor(parameters, values);
JpaCountQueryCreator creator =
new JpaCountQueryCreator(tree, domainClass, accessor,
parameters, getEntityManager());
CriteriaQuery<?> source = creator.createQuery();
TypedQuery<?> jpaQuery = getEntityManager().createQuery(source);
getBinder(values, creator.getParameterExpressions()).bind(jpaQuery);
return jpaQuery;
return countQuery.createQuery(values);
}
/**
* Query preparer to create {@link CriteriaQuery} instances and potentially
* cache them.
*
* @author Oliver Gierke
*/
private class QueryPreparer {
private ParameterBinder getBinder(Object[] values,
List<ParameterExpression<?>> expressions) {
private CriteriaQuery<?> query;
private final JpaQueryCreator creator;
return new CriteriaQueryParameterBinder(parameters, values, expressions);
/**
* Creates a new {@link QueryPreparer} from the given {@link PartTree},
* domain class and {@link Parameters}.
*
* @param tree
* @param domainClass
* @param parameters
*/
public QueryPreparer(PartTree tree, Class<?> domainClass,
Parameters parameters) {
this(new JpaQueryCreator(tree, domainClass, parameters,
getEntityManager()));
}
/**
* Creates a new {@link QueryPreparer} from the given
* {@link JpaQueryCreator}.
*
* @param creator must not be {@literl null}.
*/
protected QueryPreparer(JpaQueryCreator creator) {
Assert.notNull(creator);
this.creator = creator;
this.query = null;
}
/**
* Creates a new {@link Query} for the given parameter values.
*
* @param values
* @return
*/
public Query createQuery(Object[] values) {
if (parameters.potentiallySortsDynamically() || query == null) {
query = creator.createQuery(getDynamicSort(values));
}
TypedQuery<?> jpaQuery = getEntityManager().createQuery(query);
return invokeBinding(
getBinder(values, creator.getParameterExpressions()),
jpaQuery);
}
/**
* Invokes parameter binding on the given {@link TypedQuery}.
*
* @param binder
* @param query
* @return
*/
protected Query invokeBinding(ParameterBinder binder,
TypedQuery<?> query) {
return binder.bindAndPrepare(query);
}
private ParameterBinder getBinder(Object[] values,
List<ParameterExpression<?>> expressions) {
return new CriteriaQueryParameterBinder(parameters, values,
expressions);
}
private Sort getDynamicSort(Object[] values) {
return parameters.hasSortParameter() ? new ParametersParameterAccessor(
parameters, values).getSort() : null;
}
}
/**
* Special {@link QueryPreparer} to create count queries.
*
* @author Oliver Gierke
*/
private class CountQueryPreparer extends QueryPreparer {
/**
* Creates a new {@link CountQueryPreparer} from the given
* {@link PartTree}, domain class and {@link Parameters}. Will use a
* {@link JpaCountQueryCreator} to create the query.
*
* @param tree
* @param domainClass
* @param parameters
*/
public CountQueryPreparer(PartTree tree, Class<?> domainClass,
Parameters parameters) {
super(new JpaCountQueryCreator(tree, domainClass, parameters,
getEntityManager()));
}
/**
* Customizes binding by skipping the pagination.
*
* @see org.springframework.data.jpa.repository.query.PartTreeJpaQuery.QueryPreparer#invokeBinding(org.springframework.data.jpa.repository.query.ParameterBinder,
* javax.persistence.TypedQuery)
*/
protected Query invokeBinding(ParameterBinder binder,
javax.persistence.TypedQuery<?> query) {
return binder.bind(query);
}
}
}

View File

@@ -3,6 +3,7 @@
<persistence-unit name="hades">
<class>org.springframework.data.jpa.domain.AbstractPersistable</class>
<class>org.springframework.data.jpa.domain.sample.User</class>
<class>org.springframework.data.jpa.domain.sample.SpecialUser</class>
<class>org.springframework.data.jpa.domain.sample.Role</class>
<class>org.springframework.data.jpa.domain.sample.Account</class>
<class>org.springframework.data.jpa.domain.AbstractAuditable</class>

View File

@@ -4,11 +4,13 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd">
<persistence-unit name="first">
<class>org.springframework.data.jpa.domain.sample.User</class>
<class>org.springframework.data.jpa.domain.sample.SpecialUser</class>
<class>org.springframework.data.jpa.domain.sample.Role</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
<persistence-unit name="second">
<class>org.springframework.data.jpa.domain.sample.User</class>
<class>org.springframework.data.jpa.domain.sample.SpecialUser</class>
<class>org.springframework.data.jpa.domain.sample.Role</class>
<class>org.springframework.data.jpa.domain.sample.AuditableUser</class>
<class>org.springframework.data.jpa.domain.sample.AuditableRole</class>