DATAJPA-911 - More reflection to adapt to changes in Hibernate 5.2.

We now workaround a JPA specification violation introduced in 5.2 [0] in parameter binding. This involves removing a workaround for a bug in older Hibernate variants that returned the parameter index as String for parameters that shouldn't return a name at all. We now solve that by explicitly not considering parameter names that are purely numeric as named parameters in the first place. This also avoids producing an exception in the case of the usage of named parameters in the first place.

org.hibernate.Query was deprecated in Hibernate 5.2 but at the same time a couple of methods on it were changed in a binary incompatible way. We now explicitly detect that change in the execution of stream queries and fall back to reflection based invocation of the methods we need to call.

[0] https://hibernate.atlassian.net/browse/HHH-10803
This commit is contained in:
Oliver Gierke
2016-06-05 13:02:43 +02:00
parent fdede85931
commit 883cb81420
3 changed files with 34 additions and 12 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.data.jpa.provider;
import static org.springframework.data.jpa.provider.JpaClassUtils.*;
import static org.springframework.data.jpa.provider.PersistenceProvider.Constants.*;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
@@ -43,7 +44,9 @@ import org.hibernate.proxy.HibernateProxy;
import org.springframework.data.util.CloseableIterator;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ReflectionUtils;
/**
* Enumeration representing persistence providers to be used.
@@ -401,6 +404,11 @@ public enum PersistenceProvider implements QueryExtractor,ProxyIdAccessor {
*/
private static class HibernateScrollableResultsIterator implements CloseableIterator<Object> {
private static final Method READ_ONLY_METHOD = ClassUtils.getMethod(org.hibernate.Query.class, "setReadOnly",
boolean.class);
private static final Method SCROLL_METHOD = ClassUtils.getMethod(READ_ONLY_METHOD.getReturnType(), "scroll",
ScrollMode.class);
private final ScrollableResults scrollableResults;
/**
@@ -411,9 +419,18 @@ public enum PersistenceProvider implements QueryExtractor,ProxyIdAccessor {
public HibernateScrollableResultsIterator(Query jpaQuery) {
org.hibernate.Query query = jpaQuery.unwrap(org.hibernate.Query.class);
boolean isReadOnly = TransactionSynchronizationManager.isCurrentTransactionReadOnly();
this.scrollableResults = query.setReadOnly(TransactionSynchronizationManager.isCurrentTransactionReadOnly())
.scroll(ScrollMode.FORWARD_ONLY);
if (READ_ONLY_METHOD.getReturnType().equals(org.hibernate.Query.class)) {
this.scrollableResults = query.setReadOnly(isReadOnly).scroll(ScrollMode.FORWARD_ONLY);
} else {
Object intermediate = ReflectionUtils.invokeMethod(READ_ONLY_METHOD, jpaQuery, isReadOnly);
this.scrollableResults = (ScrollableResults) ReflectionUtils.invokeMethod(SCROLL_METHOD, intermediate,
ScrollMode.FORWARD_ONLY);
}
}
/*

View File

@@ -81,6 +81,7 @@ public abstract class QueryUtils {
private static final Pattern ALIAS_MATCH;
private static final Pattern COUNT_MATCH;
private static final Pattern NO_DIGITS = Pattern.compile("\\D+");
private static final String IDENTIFIER = "[\\p{Lu}\\P{InBASIC_LATIN}\\p{Alnum}._$]+";
private static final String IDENTIFIER_GROUP = String.format("(%s)", IDENTIFIER);
@@ -401,7 +402,11 @@ public abstract class QueryUtils {
public static boolean hasNamedParameter(Query query) {
for (Parameter<?> parameter : query.getParameters()) {
if (parameter.getName() != null) {
String name = parameter.getName();
// Hibernate 3 specific hack as it returns the index as String for the name.
if (name != null && NO_DIGITS.matcher(name).find()) {
return true;
}
}

View File

@@ -71,6 +71,10 @@ public class StringQueryParameterBinder extends ParameterBinder {
*/
private ParameterBinding getBindingFor(Query jpaQuery, int position, Parameter methodParameter) {
if (hasNamedParameter(jpaQuery)) {
return query.getBindingFor(methodParameter.getName());
}
try {
jpaQuery.getParameter(position);
@@ -78,15 +82,11 @@ public class StringQueryParameterBinder extends ParameterBinder {
} catch (IllegalArgumentException o_O) {
if (hasNamedParameter(jpaQuery)) {
return query.getBindingFor(methodParameter.getName());
}
// We should actually reject parameters unavailable, but as EclipseLink doesn't implement ….getParameter(int) for
// native queries correctly we need to fall back to an indexed parameter
// @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=427892
return new ParameterBinding(position);
}
// We should actually reject parameters unavailable, but as EclipseLink doesn't implement ….getParameter(int) for
// native queries correctly we need to fall back to an indexed parameter
// @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=427892
return new ParameterBinding(position);
}
}