Skip to content

Commit 0b5a9ea

Browse files
committed
Adapt query proxy if necessary (for compatibility with Hibernate 8.0)
Closes gh-36878
1 parent 298e1db commit 0b5a9ea

1 file changed

Lines changed: 34 additions & 6 deletions

File tree

spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,19 @@ protected Object invokeMethod(Method method, @Nullable Object[] args, Object tar
239239
Object result = method.invoke(target, args);
240240
if (result instanceof Query query) {
241241
if (newTarget) {
242+
InvocationHandler ih = new DeferredQueryInvocationHandler(query, target);
242243
Class<?>[] ifcs = cachedQueryInterfaces.computeIfAbsent(query.getClass(), key ->
243244
ClassUtils.getAllInterfacesForClass(key, this.proxyClassLoader));
244-
result = Proxy.newProxyInstance(this.proxyClassLoader, ifcs,
245-
new DeferredQueryInvocationHandler(query, target));
245+
try {
246+
result = Proxy.newProxyInstance(this.proxyClassLoader, ifcs, ih);
247+
}
248+
catch (IllegalArgumentException ex) {
249+
// Hibernate 8.0 NativeMutationOrSelectionQueryImpl multi-interface mismatch?
250+
// Fall back to single declared interface from method signature.
251+
Class<?>[] singleIfc = new Class<?>[] {method.getReturnType()};
252+
cachedQueryInterfaces.put(query.getClass(), singleIfc);
253+
result = Proxy.newProxyInstance(this.proxyClassLoader, singleIfc, ih);
254+
}
246255
close = false;
247256
}
248257
else {
@@ -503,7 +512,7 @@ private static class DeferredQueryInvocationHandler implements InvocationHandler
503512

504513
private @Nullable Map<@Nullable Object, @Nullable Object> outputParameters;
505514

506-
public DeferredQueryInvocationHandler(Query target, Object entityHandler) {
515+
public DeferredQueryInvocationHandler(Query target, @Nullable Object entityHandler) {
507516
this.target = target;
508517
this.entityHandler = entityHandler;
509518
}
@@ -531,7 +540,11 @@ else if (targetClass.isInstance(proxy)) {
531540
return proxy;
532541
}
533542
else {
534-
return this.target.unwrap(targetClass);
543+
Object retVal = this.target.unwrap(targetClass);
544+
if (retVal instanceof Query query && targetClass.isInterface()) {
545+
retVal = adaptQueryProxy(proxy, query, targetClass);
546+
}
547+
return retVal;
535548
}
536549
}
537550
case "getOutputParameterValue" -> {
@@ -552,14 +565,21 @@ else if (targetClass.isInstance(proxy)) {
552565
// Invoke method on actual Query object.
553566
try {
554567
Object retVal = method.invoke(this.target, args);
555-
if (method.getName().equals("registerStoredProcedureParameter") && args.length == 3 &&
568+
Class<?> returnType = method.getReturnType();
569+
if (retVal == this.target && returnType.isInstance(proxy)) {
570+
retVal = proxy;
571+
}
572+
else if (retVal instanceof Query query) {
573+
retVal = adaptQueryProxy(proxy, query, returnType);
574+
}
575+
else if (method.getName().equals("registerStoredProcedureParameter") && args.length == 3 &&
556576
(args[2] == ParameterMode.OUT || args[2] == ParameterMode.INOUT)) {
557577
if (this.outputParameters == null) {
558578
this.outputParameters = new LinkedHashMap<>();
559579
}
560580
this.outputParameters.put(args[0], null);
561581
}
562-
return (retVal == this.target ? proxy : retVal);
582+
return retVal;
563583
}
564584
catch (InvocationTargetException ex) {
565585
throw ex.getTargetException();
@@ -589,6 +609,14 @@ else if (targetClass.isInstance(proxy)) {
589609
}
590610
}
591611
}
612+
613+
private Object adaptQueryProxy(Object proxy, Query query, Class<?> expectedType) {
614+
InvocationHandler ih = this;
615+
if (query != this.target) {
616+
ih = new DeferredQueryInvocationHandler(query, this.entityHandler);
617+
}
618+
return Proxy.newProxyInstance(proxy.getClass().getClassLoader(), new Class<?>[] {expectedType}, ih);
619+
}
592620
}
593621

594622
}

0 commit comments

Comments
 (0)