From b5ee7623991b0c0cab65b06f406d4cc25f2ab057 Mon Sep 17 00:00:00 2001 From: Richard Zowalla Date: Thu, 20 Aug 2026 12:04:31 +0200 Subject: [PATCH] [OPENJPA-2987] Restore externalized-parameter assertions The three "CanDetectExternalized..." tests had been reduced to assertNotNull(getResultList()), which can never fail, after JPA 3.2 made getResultList() return a mutable ArrayList copy instead of the internal ResultList the assertions relied on. Externalized-parameter detection is still in place (PreparedQueryImpl excludes such queries from the prepared query cache), so the original assertions are restored by executing the kernel query directly, which still yields the ResultList carrying the QueryExpressions user object. --- .../sqlcache/TestExternalizedParameter.java | 76 +++++++++++++------ 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestExternalizedParameter.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestExternalizedParameter.java index b559a2fa1c..5d4dbe68fc 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestExternalizedParameter.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/sqlcache/TestExternalizedParameter.java @@ -18,7 +18,9 @@ */ package org.apache.openjpa.persistence.jdbc.sqlcache; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import jakarta.persistence.EntityManager; @@ -29,11 +31,18 @@ import org.apache.openjpa.lib.rop.ResultList; import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.persistence.OpenJPAPersistence; +import org.apache.openjpa.persistence.QueryImpl; import junit.framework.TestCase; /** * Tests that we can detect if a query is using query parameters for fields whose values are externalized. + *

+ * Such queries can not be reparameterized and are therefore excluded from the prepared query cache. The + * detection operates on the {@link QueryExpressions} that are attached as a user object to the + * {@link ResultList} produced by the kernel query. Since JPA 3.2 the facade + * {@link jakarta.persistence.Query#getResultList()} returns a mutable copy of that result rather than the + * {@code ResultList} itself, so the tests execute the kernel query directly to get hold of the expressions. * * @author Pinaki Poddar * @@ -56,44 +65,61 @@ public void setUp() throws Exception { } } - /** - * Verifies that a query with a non-externalized parameter executes - * correctly. - */ public void testNoFalseAlarmOnExternalizedParameterDetection() { String jpql = "select b from Book b where b.title=:title"; EntityManager em = emf.createEntityManager(); - List result = em.createQuery(jpql) - .setParameter("title","XYZ") - .getResultList(); - assertNotNull(result); + Map params = new HashMap<>(); + params.put("title", "XYZ"); + + QueryExpressions[] exps = getExpressions(execute(em, jpql, params)); + assertNotNull(exps); + + assertFalse(isUsingExternalizedParameter(exps[0])); } - /** - * Verifies that a query with an externalized parameter (token maps - * to an enum via ExternalValues) executes correctly. - */ public void testCanDetectExternalizedSingleParameterValue() { String jpql = "select b from Book b where b.token=:token"; EntityManager em = emf.createEntityManager(); - List result = em.createQuery(jpql) - .setParameter("token","MEDIUM") - .getResultList(); - assertNotNull(result); + Map params = new HashMap<>(); + params.put("token", "MEDIUM"); + + QueryExpressions[] exps = getExpressions(execute(em, jpql, params)); + assertNotNull(exps); + + assertTrue(isUsingExternalizedParameter(exps[0])); } - /** - * Verifies that a query mixing externalized and non-externalized - * parameters executes correctly. - */ public void testCanDetectExternalizedMixedParameterValue() { String jpql = "select b from Book b where b.token=:token and b.title = :title"; EntityManager em = emf.createEntityManager(); - List result = em.createQuery(jpql) - .setParameter("token","MEDIUM") - .setParameter("title", "LARGE") - .getResultList(); - assertNotNull(result); + Map params = new HashMap<>(); + params.put("token", "MEDIUM"); + params.put("title", "LARGE"); + + QueryExpressions[] exps = getExpressions(execute(em, jpql, params)); + assertNotNull(exps); + + assertTrue(isUsingExternalizedParameter(exps[0])); + } + + /** + * Executes the given JPQL on the kernel query to get hold of the raw {@link ResultList}. + */ + Object execute(EntityManager em, String jpql, Map params) { + QueryImpl query = (QueryImpl) em.createQuery(jpql); + return query.getDelegate().execute(params); + } + + public QueryExpressions[] getExpressions(Object result) { + if (!(result instanceof ResultList)) + return null; + Object userObject = ((ResultList)result).getUserObject(); + if (userObject == null || !userObject.getClass().isArray() || ((Object[])userObject).length != 2) + return null; + Object executor = ((Object[])userObject)[1]; + if (!(executor instanceof StoreQuery.Executor)) + return null; + return ((StoreQuery.Executor)executor).getQueryExpressions(); } boolean isUsingExternalizedParameter(QueryExpressions exp) {