Browse Source

Unwrap raw target Query instance in case of proxy mismatch

Closes gh-32766

(cherry picked from commit 59a125d06f)
pull/33211/head
Juergen Hoeller 2 years ago
parent
commit
9f35debdc6
  1. 6
      spring-orm/src/main/java/org/springframework/orm/jpa/SharedEntityManagerCreator.java
  2. 120
      spring-orm/src/test/java/org/springframework/orm/jpa/SharedEntityManagerCreatorTests.java

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

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2023 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -390,7 +390,9 @@ public abstract class SharedEntityManagerCreator {
else if (targetClass.isInstance(proxy)) { else if (targetClass.isInstance(proxy)) {
return proxy; return proxy;
} }
break; else {
return this.target.unwrap(targetClass);
}
case "getOutputParameterValue": case "getOutputParameterValue":
if (this.entityManager == null) { if (this.entityManager == null) {
Object key = args[0]; Object key = args[0];

120
spring-orm/src/test/java/org/springframework/orm/jpa/SharedEntityManagerCreatorTests.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2019 the original author or authors. * Copyright 2002-2024 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -38,16 +38,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.withSettings; import static org.mockito.Mockito.withSettings;
/** /**
* Unit tests for {@link SharedEntityManagerCreator}. * Tests for {@link SharedEntityManagerCreator}.
* *
* @author Oliver Gierke * @author Oliver Gierke
* @author Juergen Hoeller * @author Juergen Hoeller
*/ */
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
public class SharedEntityManagerCreatorTests { class SharedEntityManagerCreatorTests {
@Test @Test
public void proxyingWorksIfInfoReturnsNullEntityManagerInterface() { void proxyingWorksIfInfoReturnsNullEntityManagerInterface() {
EntityManagerFactory emf = mock(EntityManagerFactory.class, EntityManagerFactory emf = mock(EntityManagerFactory.class,
withSettings().extraInterfaces(EntityManagerFactoryInfo.class)); withSettings().extraInterfaces(EntityManagerFactoryInfo.class));
// EntityManagerFactoryInfo.getEntityManagerInterface returns null // EntityManagerFactoryInfo.getEntityManagerInterface returns null
@ -55,7 +55,7 @@ public class SharedEntityManagerCreatorTests {
} }
@Test @Test
public void transactionRequiredExceptionOnJoinTransaction() { void transactionRequiredExceptionOnJoinTransaction() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy( assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
@ -63,7 +63,7 @@ public class SharedEntityManagerCreatorTests {
} }
@Test @Test
public void transactionRequiredExceptionOnFlush() { void transactionRequiredExceptionOnFlush() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy( assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(
@ -71,7 +71,7 @@ public class SharedEntityManagerCreatorTests {
} }
@Test @Test
public void transactionRequiredExceptionOnPersist() { void transactionRequiredExceptionOnPersist() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() -> assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@ -79,7 +79,7 @@ public class SharedEntityManagerCreatorTests {
} }
@Test @Test
public void transactionRequiredExceptionOnMerge() { void transactionRequiredExceptionOnMerge() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() -> assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@ -87,7 +87,7 @@ public class SharedEntityManagerCreatorTests {
} }
@Test @Test
public void transactionRequiredExceptionOnRemove() { void transactionRequiredExceptionOnRemove() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() -> assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@ -95,7 +95,7 @@ public class SharedEntityManagerCreatorTests {
} }
@Test @Test
public void transactionRequiredExceptionOnRefresh() { void transactionRequiredExceptionOnRefresh() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() -> assertThatExceptionOfType(TransactionRequiredException.class).isThrownBy(() ->
@ -103,78 +103,98 @@ public class SharedEntityManagerCreatorTests {
} }
@Test @Test
public void deferredQueryWithUpdate() { void deferredQueryWithUpdate() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class); EntityManager targetEm = mock(EntityManager.class);
Query query = mock(Query.class); Query targetQuery = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm); given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(query); given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.isOpen()).willReturn(true); given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
em.createQuery("x").executeUpdate(); Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.executeUpdate();
verify(query).executeUpdate(); verify(targetQuery).executeUpdate();
verify(targetEm).close(); verify(targetEm).close();
} }
@Test @Test
public void deferredQueryWithSingleResult() { void deferredQueryWithSingleResult() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class); EntityManager targetEm = mock(EntityManager.class);
Query query = mock(Query.class); Query targetQuery = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm); given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(query); given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.isOpen()).willReturn(true); given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
em.createQuery("x").getSingleResult(); Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.getSingleResult();
verify(query).getSingleResult(); verify(targetQuery).getSingleResult();
verify(targetEm).close(); verify(targetEm).close();
} }
@Test @Test
public void deferredQueryWithResultList() { void deferredQueryWithResultList() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class); EntityManager targetEm = mock(EntityManager.class);
Query query = mock(Query.class); Query targetQuery = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm); given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(query); given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.isOpen()).willReturn(true); given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
em.createQuery("x").getResultList(); Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.getResultList();
verify(query).getResultList(); verify(targetQuery).getResultList();
verify(targetEm).close(); verify(targetEm).close();
} }
@Test @Test
public void deferredQueryWithResultStream() { void deferredQueryWithResultStream() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class); EntityManager targetEm = mock(EntityManager.class);
Query query = mock(Query.class); Query targetQuery = mock(Query.class);
given(emf.createEntityManager()).willReturn(targetEm); given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createQuery("x")).willReturn(query); given(targetEm.createQuery("x")).willReturn(targetQuery);
given(targetEm.isOpen()).willReturn(true); given(targetEm.isOpen()).willReturn(true);
given((Query) targetQuery.unwrap(targetQuery.getClass())).willReturn(targetQuery);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
em.createQuery("x").getResultStream(); Query query = em.createQuery("x");
assertThat((Query) query.unwrap(null)).isSameAs(targetQuery);
assertThat((Query) query.unwrap(targetQuery.getClass())).isSameAs(targetQuery);
assertThat(query.unwrap(Query.class)).isSameAs(query);
query.getResultStream();
verify(query).getResultStream(); verify(targetQuery).getResultStream();
verify(targetEm).close(); verify(targetEm).close();
} }
@Test @Test
public void deferredStoredProcedureQueryWithIndexedParameters() { void deferredStoredProcedureQueryWithIndexedParameters() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class); EntityManager targetEm = mock(EntityManager.class);
StoredProcedureQuery query = mock(StoredProcedureQuery.class); StoredProcedureQuery targetQuery = mock(StoredProcedureQuery.class);
given(emf.createEntityManager()).willReturn(targetEm); given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createStoredProcedureQuery("x")).willReturn(query); given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
willReturn("y").given(query).getOutputParameterValue(0); willReturn("y").given(targetQuery).getOutputParameterValue(0);
willReturn("z").given(query).getOutputParameterValue(2); willReturn("z").given(targetQuery).getOutputParameterValue(2);
given(targetEm.isOpen()).willReturn(true); given(targetEm.isOpen()).willReturn(true);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
@ -188,24 +208,24 @@ public class SharedEntityManagerCreatorTests {
spq.getOutputParameterValue(1)); spq.getOutputParameterValue(1));
assertThat(spq.getOutputParameterValue(2)).isEqualTo("z"); assertThat(spq.getOutputParameterValue(2)).isEqualTo("z");
verify(query).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT); verify(targetQuery).registerStoredProcedureParameter(0, String.class, ParameterMode.OUT);
verify(query).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN); verify(targetQuery).registerStoredProcedureParameter(1, Number.class, ParameterMode.IN);
verify(query).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT); verify(targetQuery).registerStoredProcedureParameter(2, Object.class, ParameterMode.INOUT);
verify(query).execute(); verify(targetQuery).execute();
verify(targetEm).close(); verify(targetEm).close();
verifyNoMoreInteractions(query); verifyNoMoreInteractions(targetQuery);
verifyNoMoreInteractions(targetEm); verifyNoMoreInteractions(targetEm);
} }
@Test @Test
public void deferredStoredProcedureQueryWithNamedParameters() { void deferredStoredProcedureQueryWithNamedParameters() {
EntityManagerFactory emf = mock(EntityManagerFactory.class); EntityManagerFactory emf = mock(EntityManagerFactory.class);
EntityManager targetEm = mock(EntityManager.class); EntityManager targetEm = mock(EntityManager.class);
StoredProcedureQuery query = mock(StoredProcedureQuery.class); StoredProcedureQuery targetQuery = mock(StoredProcedureQuery.class);
given(emf.createEntityManager()).willReturn(targetEm); given(emf.createEntityManager()).willReturn(targetEm);
given(targetEm.createStoredProcedureQuery("x")).willReturn(query); given(targetEm.createStoredProcedureQuery("x")).willReturn(targetQuery);
willReturn("y").given(query).getOutputParameterValue("a"); willReturn("y").given(targetQuery).getOutputParameterValue("a");
willReturn("z").given(query).getOutputParameterValue("c"); willReturn("z").given(targetQuery).getOutputParameterValue("c");
given(targetEm.isOpen()).willReturn(true); given(targetEm.isOpen()).willReturn(true);
EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf); EntityManager em = SharedEntityManagerCreator.createSharedEntityManager(emf);
@ -219,12 +239,12 @@ public class SharedEntityManagerCreatorTests {
spq.getOutputParameterValue("b")); spq.getOutputParameterValue("b"));
assertThat(spq.getOutputParameterValue("c")).isEqualTo("z"); assertThat(spq.getOutputParameterValue("c")).isEqualTo("z");
verify(query).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT); verify(targetQuery).registerStoredProcedureParameter("a", String.class, ParameterMode.OUT);
verify(query).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN); verify(targetQuery).registerStoredProcedureParameter("b", Number.class, ParameterMode.IN);
verify(query).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT); verify(targetQuery).registerStoredProcedureParameter("c", Object.class, ParameterMode.INOUT);
verify(query).execute(); verify(targetQuery).execute();
verify(targetEm).close(); verify(targetEm).close();
verifyNoMoreInteractions(query); verifyNoMoreInteractions(targetQuery);
verifyNoMoreInteractions(targetEm); verifyNoMoreInteractions(targetEm);
} }

Loading…
Cancel
Save