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:
committed by
Oliver Gierke
parent
3735464ec2
commit
b6fbda9770
@@ -89,13 +89,13 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
|||||||
*/
|
*/
|
||||||
private class QueryPreparer {
|
private class QueryPreparer {
|
||||||
|
|
||||||
private final CriteriaQuery<?> query;
|
private final CriteriaQuery<?> cachedCriteriaQuery;
|
||||||
private final List<ParameterMetadata<?>> expressions;
|
private final List<ParameterMetadata<?>> expressions;
|
||||||
|
|
||||||
public QueryPreparer(boolean recreateQueries) {
|
public QueryPreparer(boolean recreateQueries) {
|
||||||
|
|
||||||
JpaQueryCreator creator = createCreator(null);
|
JpaQueryCreator creator = createCreator(null);
|
||||||
this.query = recreateQueries ? null : creator.createQuery();
|
this.cachedCriteriaQuery = recreateQueries ? null : creator.createQuery();
|
||||||
this.expressions = recreateQueries ? null : creator.getParameterExpressions();
|
this.expressions = recreateQueries ? null : creator.getParameterExpressions();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,20 +107,40 @@ public class PartTreeJpaQuery extends AbstractJpaQuery {
|
|||||||
*/
|
*/
|
||||||
public Query createQuery(Object[] values) {
|
public Query createQuery(Object[] values) {
|
||||||
|
|
||||||
CriteriaQuery<?> criteriaQuery = query;
|
CriteriaQuery<?> criteriaQuery = cachedCriteriaQuery;
|
||||||
List<ParameterMetadata<?>> expressions = this.expressions;
|
List<ParameterMetadata<?>> expressions = this.expressions;
|
||||||
ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
|
ParametersParameterAccessor accessor = new ParametersParameterAccessor(parameters, values);
|
||||||
|
|
||||||
if (query == null || accessor.hasBindableNullValue()) {
|
if (cachedCriteriaQuery == null || accessor.hasBindableNullValue()) {
|
||||||
JpaQueryCreator creator = createCreator(accessor);
|
JpaQueryCreator creator = createCreator(accessor);
|
||||||
criteriaQuery = creator.createQuery(getDynamicSort(values));
|
criteriaQuery = creator.createQuery(getDynamicSort(values));
|
||||||
expressions = creator.getParameterExpressions();
|
expressions = creator.getParameterExpressions();
|
||||||
}
|
}
|
||||||
|
|
||||||
TypedQuery<?> jpaQuery = getEntityManager().createQuery(criteriaQuery);
|
TypedQuery<?> jpaQuery = createQuery(criteriaQuery);
|
||||||
return invokeBinding(getBinder(values, expressions), jpaQuery);
|
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) {
|
protected JpaQueryCreator createCreator(ParametersParameterAccessor accessor) {
|
||||||
|
|
||||||
EntityManager entityManager = getEntityManager();
|
EntityManager entityManager = getEntityManager();
|
||||||
|
|||||||
Reference in New Issue
Block a user