You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by rz...@apache.org on 2026/08/20 10:46:49 UTC

(openjpa) 01/03: [OPENJPA-2985] Implement EntityManager.createQuery(TypedQueryReference)

This is an automated email from the ASF dual-hosted git repository.

rzo1 pushed a commit to branch OPENJPA-2985
in repository https://gitbox.apache.org/repos/asf/openjpa.git

commit 674fe11d36ed0eddb41821815f55e5e50cf08283
Author: Richard Zowalla <rz...@apache.org>
AuthorDate: Thu Aug 20 12:29:47 2026 +0200

    [OPENJPA-2985] Implement EntityManager.createQuery(TypedQueryReference)
    
    EntityManagerImpl.createQuery(TypedQueryReference<T>) no longer throws
    UnsupportedOperationException. It now:
     - calls assertNotCloseInvoked() first, so a closed EntityManager yields
       IllegalStateException;
     - rejects a null reference (and a null reference name) with
       IllegalArgumentException;
     - delegates to createNamedQuery(name, resultType) so the named query
       metadata (query string, hints, flush mode, max results, lock mode) is
       applied and the reference's result type becomes the query result class;
       an unknown query name propagates as ArgumentException, which extends
       IllegalArgumentException, matching the createNamedQuery contract;
     - applies the hints carried by the reference afterwards, so they take
       precedence over hints declared on the @NamedQuery itself; failures there
       go through translateException like the rest of the named query path.
    
    A null result type falls back to createNamedQuery(name) defensively.
    
    Tested by the new TestTypedQueryReference in
    org.apache.openjpa.persistence.query, which covers query creation and
    execution from a reference, the applied result class, reference hints
    surviving in getHints(), positional parameters of the named query, null
    reference, unknown query name and a closed EntityManager. All seven cases
    failed against the previous UnsupportedOperationException.
---
 .../persistence/query/TestTypedQueryReference.java | 156 +++++++++++++++++++++
 .../openjpa/persistence/EntityManagerImpl.java     |  23 ++-
 2 files changed, 178 insertions(+), 1 deletion(-)

diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestTypedQueryReference.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestTypedQueryReference.java
new file mode 100644
index 000000000..bcd60495f
--- /dev/null
+++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/query/TestTypedQueryReference.java
@@ -0,0 +1,156 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.openjpa.persistence.query;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import jakarta.persistence.EntityManager;
+import jakarta.persistence.TypedQuery;
+import jakarta.persistence.TypedQueryReference;
+
+import org.apache.openjpa.persistence.OpenJPAQuery;
+import org.apache.openjpa.persistence.test.SingleEMFTestCase;
+
+/**
+ * Tests {@code EntityManager.createQuery(TypedQueryReference)} added in JPA 3.2.
+ */
+public class TestTypedQueryReference extends SingleEMFTestCase {
+
+    private static final String TIMEOUT_HINT = "jakarta.persistence.query.timeout";
+
+    @Override
+    public void setUp() {
+        setUp(SimpleEntity.class, CLEAR_TABLES);
+        EntityManager em = emf.createEntityManager();
+        em.getTransaction().begin();
+        em.persist(new SimpleEntity("Name One", "Value One"));
+        em.persist(new SimpleEntity("Name Two", "Value Two"));
+        em.getTransaction().commit();
+        em.close();
+    }
+
+    private static <R> TypedQueryReference<R> ref(final String name, final Class<R> type,
+            final Map<String, Object> hints) {
+        return new TypedQueryReference<R>() {
+
+            @Override
+            public String getName() {
+                return name;
+            }
+
+            @Override
+            public Class<? extends R> getResultType() {
+                return type;
+            }
+
+            @Override
+            public Map<String, Object> getHints() {
+                return hints;
+            }
+        };
+    }
+
+    public void testCreateQueryFromReference() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            TypedQuery<SimpleEntity> q =
+                em.createQuery(ref("FindAll", SimpleEntity.class, Collections.<String, Object>emptyMap()));
+            assertNotNull(q);
+            List<SimpleEntity> list = q.getResultList();
+            assertEquals(2, list.size());
+            for (SimpleEntity s : list) {
+                assertNotNull(s.getName());
+            }
+        } finally {
+            em.close();
+        }
+    }
+
+    public void testResultTypeIsApplied() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            TypedQuery<SimpleEntity> q =
+                em.createQuery(ref("FindAll", SimpleEntity.class, Collections.<String, Object>emptyMap()));
+            assertEquals(SimpleEntity.class, ((OpenJPAQuery<?>) q).getResultClass());
+        } finally {
+            em.close();
+        }
+    }
+
+    public void testReferenceHintsAreApplied() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            Map<String, Object> hints =
+                Collections.<String, Object>singletonMap(TIMEOUT_HINT, Integer.valueOf(12345));
+            TypedQuery<SimpleEntity> q = em.createQuery(ref("FindAll", SimpleEntity.class, hints));
+            assertEquals(Integer.valueOf(12345), q.getHints().get(TIMEOUT_HINT));
+        } finally {
+            em.close();
+        }
+    }
+
+    public void testParametersStillWork() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            TypedQuery<SimpleEntity> q =
+                em.createQuery(ref("FindOne", SimpleEntity.class, Collections.<String, Object>emptyMap()));
+            q.setParameter(1, "Name One");
+            assertEquals("Value One", q.getSingleResult().getValue());
+        } finally {
+            em.close();
+        }
+    }
+
+    public void testNullReference() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            em.createQuery((TypedQueryReference<SimpleEntity>) null);
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            // expected
+        } finally {
+            em.close();
+        }
+    }
+
+    public void testUnknownQueryName() {
+        EntityManager em = emf.createEntityManager();
+        try {
+            em.createQuery(ref("NoSuchNamedQuery", SimpleEntity.class, Collections.<String, Object>emptyMap()));
+            fail("expected IllegalArgumentException");
+        } catch (IllegalArgumentException expected) {
+            // expected
+        } finally {
+            em.close();
+        }
+    }
+
+    public void testClosedEntityManager() {
+        EntityManager em = emf.createEntityManager();
+        em.close();
+        try {
+            em.createQuery(ref("FindAll", SimpleEntity.class, Collections.<String, Object>emptyMap()));
+            fail("expected IllegalStateException");
+        } catch (IllegalStateException expected) {
+            // expected
+        }
+    }
+}
diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
index bd693bb52..d202c7ac8 100644
--- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
+++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/EntityManagerImpl.java
@@ -2775,8 +2775,29 @@ public class EntityManagerImpl
 	}
 
 	@Override
+	@SuppressWarnings("unchecked")
 	public <T> TypedQuery<T> createQuery(TypedQueryReference<T> reference) {
-    	throw new UnsupportedOperationException("Not yet implemented (JPA 3.2)");
+		assertNotCloseInvoked();
+		if (reference == null) {
+			throw new IllegalArgumentException("TypedQueryReference must not be null");
+		}
+		String name = reference.getName();
+		if (name == null) {
+			throw new IllegalArgumentException("TypedQueryReference name must not be null");
+		}
+		Class<? extends T> resultType = reference.getResultType();
+		Query query = (resultType == null) ? createNamedQuery(name) : createNamedQuery(name, resultType);
+		Map<String, Object> hints = reference.getHints();
+		if (hints != null) {
+			try {
+				for (Map.Entry<String, Object> hint : hints.entrySet()) {
+					query.setHint(hint.getKey(), hint.getValue());
+				}
+			} catch (RuntimeException re) {
+				throw translateException(re);
+			}
+		}
+		return (TypedQuery<T>) query;
 	}
 
 	@Override