DATAJPA-396 - Fixed potential multi-threading issues in PartTreeJpaQuery.

The CriteriaQuery implementations unfortunately is not thread-safe for some persistence providers. E.g. Hibernate changes the state of it's implementation during the first attempt to use the instance. This causes multi-threading issues if we cache the query instance for later usage (as the instance might be used concurrently).

We now synchronize the first usage of the cached CriteriaQuery instance to guard against this situation.

Original pull request: #42.
This commit is contained in:
Thomas Darimont
2013-09-08 20:48:04 +02:00
committed by Oliver Gierke
parent 3735464ec2
commit b6fbda9770

View File

@@ -89,13 +89,13 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
*/
private class QueryPreparer {
private final CriteriaQuery<?> query;
private final CriteriaQuery<?> cachedCriteriaQuery;
private final List<ParameterMetadata<?>> expressions;
public QueryPreparer(boolean recreateQueries) {
JpaQueryCreator creator = createCreator(null);
this.query = recreateQueries ? null : creator.createQuery();
this.cachedCriteriaQuery = recreateQueries ? null : creator.createQuery();
this.expressions = recreateQueries ? null : creator.getParameterExpressions();
}
@@ -107,20 +107,40 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
*/
public Query createQuery(Object[] values) {
CriteriaQuery<?> criteriaQuery = query;
CriteriaQuery<?> criteriaQuery = cachedCriteriaQuery;
List<ParameterMetadata<?>> expressions = this.expressions;
ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
if (query == null || accessor.hasBindableNullValue()) {
if (cachedCriteriaQuery == null || accessor.hasBindableNullValue()) {
JpaQueryCreator creator = createCreator(accessor);
criteriaQuery = creator.createQuery(getDynamicSort(values));
expressions = creator.getParameterExpressions();
}
TypedQuery<?> jpaQuery = getEntityManager().createQuery(criteriaQuery);
TypedQuery<?> jpaQuery = createQuery(criteriaQuery);
return invokeBinding(getBinder(values, expressions), jpaQuery);
}
/**
* Checks whether we are working with a cached {@link CriteriaQuery} and snychronizes the creation of a
* {@link TypedQuery} instance from it. This is due to non-thread-safety in the {@link CriteriaQuery} implementation
* of some persistence providers (i.e. Hibernate in this case).
*
* @see DATAJPA-396
* @param criteriaQuery must not be {@literal null}.
* @return
*/
private TypedQuery<?> createQuery(CriteriaQuery<?> criteriaQuery) {
if (this.cachedCriteriaQuery != null) {
synchronized (this.cachedCriteriaQuery) {
return getEntityManager().createQuery(criteriaQuery);
}
}
return getEntityManager().createQuery(criteriaQuery);
}
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor) {
EntityManager entityManager = getEntityManager();