From 29449baa2941f6c07f5d57a89c63a495f855103b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C3=A9phane=20=C3=89pardaud?= Date: Wed, 7 Jun 2023 17:55:33 +0200 Subject: [PATCH] ORM/HR Panache: support named query projection For #33310 --- .../PanacheJpaCommonResourceProcessor.java | 12 ++++------ ...PanacheNamedQueryEntityClassBuildStep.java | 8 +++---- .../runtime/CommonPanacheQueryImpl.java | 15 +++++++----- .../common/runtime/NamedQueryUtil.java | 13 +++++------ .../runtime/PanacheHibernateRecorder.java | 3 +-- .../hibernate/orm/panache/PanacheQuery.java | 3 +-- .../PanacheJpaCommonResourceProcessor.java | 11 +++++---- ...PanacheNamedQueryEntityClassBuildStep.java | 8 +++---- .../runtime/CommonPanacheQueryImpl.java | 20 +++++++++------- .../common/runtime/NamedQueryUtil.java | 23 +++++++++++++------ .../runtime/PanacheHibernateRecorder.java | 3 +-- .../reactive/panache/PanacheQuery.java | 3 +-- .../main/java/io/quarkus/it/panache/Cat.java | 2 ++ .../io/quarkus/it/panache/TestEndpoint.java | 10 +++++++- .../io/quarkus/it/panache/reactive/Cat.java | 2 ++ .../it/panache/reactive/TestEndpoint.java | 14 +++++++++++ 16 files changed, 93 insertions(+), 57 deletions(-) diff --git a/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheJpaCommonResourceProcessor.java b/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheJpaCommonResourceProcessor.java index 9e554993ab961..d6219a1163016 100644 --- a/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheJpaCommonResourceProcessor.java +++ b/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheJpaCommonResourceProcessor.java @@ -1,10 +1,8 @@ package io.quarkus.hibernate.orm.panache.common.deployment; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import jakarta.persistence.NamedQueries; import jakarta.persistence.NamedQuery; @@ -38,7 +36,7 @@ void lookupNamedQueries(CombinedIndexBuildItem index, JpaModelBuildItem jpaModel) { for (String modelClass : jpaModel.getAllModelClassNames()) { // lookup for `@NamedQuery` on the hierarchy and produce NamedQueryEntityClassBuildStep - Set typeNamedQueries = new HashSet<>(); + Map typeNamedQueries = new HashMap<>(); lookupNamedQueries(index, DotName.createSimple(modelClass), typeNamedQueries); namedQueries.produce(new PanacheNamedQueryEntityClassBuildStep(modelClass, typeNamedQueries)); } @@ -48,7 +46,7 @@ void lookupNamedQueries(CombinedIndexBuildItem index, @Record(ExecutionTime.STATIC_INIT) void buildNamedQueryMap(List namedQueryEntityClasses, PanacheHibernateRecorder panacheHibernateRecorder) { - Map> namedQueryMap = new HashMap<>(); + Map> namedQueryMap = new HashMap<>(); for (PanacheNamedQueryEntityClassBuildStep entityNamedQueries : namedQueryEntityClasses) { namedQueryMap.put(entityNamedQueries.getClassName(), entityNamedQueries.getNamedQueries()); } @@ -56,7 +54,7 @@ void buildNamedQueryMap(List namedQueryEn panacheHibernateRecorder.setNamedQueryMap(namedQueryMap); } - private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set namedQueries) { + private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Map namedQueries) { ClassInfo classInfo = index.getComputingIndex().getClassByName(name); if (classInfo == null) { return; @@ -65,7 +63,7 @@ private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set< List namedQueryInstances = classInfo.annotationsMap().get(DOTNAME_NAMED_QUERY); if (namedQueryInstances != null) { for (AnnotationInstance namedQueryInstance : namedQueryInstances) { - namedQueries.add(namedQueryInstance.value("name").asString()); + namedQueries.put(namedQueryInstance.value("name").asString(), namedQueryInstance.value("query").asString()); } } @@ -75,7 +73,7 @@ private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set< AnnotationValue value = namedQueriesInstance.value(); AnnotationInstance[] nestedInstances = value.asNestedArray(); for (AnnotationInstance nested : nestedInstances) { - namedQueries.add(nested.value("name").asString()); + namedQueries.put(nested.value("name").asString(), nested.value("query").asString()); } } } diff --git a/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java b/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java index 0bfec3ccb4c7b..87f4a80000494 100644 --- a/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java +++ b/extensions/panache/hibernate-orm-panache-common/deployment/src/main/java/io/quarkus/hibernate/orm/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java @@ -1,14 +1,14 @@ package io.quarkus.hibernate.orm.panache.common.deployment; -import java.util.Set; +import java.util.Map; import io.quarkus.builder.item.MultiBuildItem; final class PanacheNamedQueryEntityClassBuildStep extends MultiBuildItem { private String className; - private Set namedQueries; + private Map namedQueries; - public PanacheNamedQueryEntityClassBuildStep(String className, Set namedQueries) { + public PanacheNamedQueryEntityClassBuildStep(String className, Map namedQueries) { this.className = className; this.namedQueries = namedQueries; } @@ -17,7 +17,7 @@ public String getClassName() { return this.className; } - public Set getNamedQueries() { + public Map getNamedQueries() { return namedQueries; } } diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java index 531958d8b8071..6e1771c9b91cd 100644 --- a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/CommonPanacheQueryImpl.java @@ -86,13 +86,16 @@ private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String n // Builder public CommonPanacheQueryImpl project(Class type) { + String selectQuery = query; if (PanacheJpaUtil.isNamedQuery(query)) { - throw new PanacheQueryException("Unable to perform a projection on a named query"); + org.hibernate.query.Query q = (org.hibernate.query.Query) em.createNamedQuery(query.substring(1)); + selectQuery = q.getQueryString(); } - String lowerCasedTrimmedQuery = query.trim().replace('\n', ' ').replace('\r', ' ').toLowerCase(); - if (lowerCasedTrimmedQuery.startsWith("select new ")) { - throw new PanacheQueryException("Unable to perform a projection on a 'select new' query: " + query); + String lowerCasedTrimmedQuery = selectQuery.trim().replace('\n', ' ').replace('\r', ' ').toLowerCase(); + if (lowerCasedTrimmedQuery.startsWith("select new ") + || lowerCasedTrimmedQuery.startsWith("select distinct new ")) { + throw new PanacheQueryException("Unable to perform a projection on a 'select [distinct]? new' query: " + query); } // If the query starts with a select clause, we generate an HQL query @@ -101,7 +104,7 @@ public CommonPanacheQueryImpl project(Class type) { // New query: SELECT new org.acme.ProjectionClass(e.field1, e.field2) from EntityClass e if (lowerCasedTrimmedQuery.startsWith("select ")) { int endSelect = lowerCasedTrimmedQuery.indexOf(" from "); - String trimmedQuery = query.trim().replace('\n', ' ').replace('\r', ' '); + String trimmedQuery = selectQuery.trim().replace('\n', ' ').replace('\r', ' '); // 7 is the length of "select " String selectClause = trimmedQuery.substring(7, endSelect).trim(); String from = trimmedQuery.substring(endSelect); @@ -150,7 +153,7 @@ public CommonPanacheQueryImpl project(Class type) { } select.append(") "); - return new CommonPanacheQueryImpl<>(this, select.toString() + query, "select count(*) " + query); + return new CommonPanacheQueryImpl<>(this, select.toString() + selectQuery, "select count(*) " + selectQuery); } public void filter(String filterName, Map parameters) { diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/NamedQueryUtil.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/NamedQueryUtil.java index 9e8c3a46ddf03..5eb9eaf69e31d 100644 --- a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/NamedQueryUtil.java +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/NamedQueryUtil.java @@ -2,7 +2,6 @@ import java.util.Collections; import java.util.Map; -import java.util.Set; import org.hibernate.query.SemanticException; @@ -11,13 +10,13 @@ public final class NamedQueryUtil { // will be replaced at augmentation phase - private static volatile Map> namedQueryMap = Collections.emptyMap(); + private static volatile Map> namedQueryMap = Collections.emptyMap(); private NamedQueryUtil() { // prevent initialization } - public static void setNamedQueryMap(Map> newNamedQueryMap) { + public static void setNamedQueryMap(Map> newNamedQueryMap) { namedQueryMap = newNamedQueryMap; } @@ -29,13 +28,13 @@ public static void checkNamedQuery(Class entityClass, String namedQuery) { } public static boolean isNamedQuery(Class entityClass, String namedQuery) { - Set namedQueries = namedQueryMap.get(entityClass.getName()); - return namedQueries != null && namedQueries.contains(namedQuery); + Map namedQueries = namedQueryMap.get(entityClass.getName()); + return namedQueries != null && namedQueries.containsKey(namedQuery); } private static boolean isNamedQuery(String namedQuery) { - for (Set namedQueries : namedQueryMap.values()) { - if (namedQueries.contains(namedQuery)) { + for (Map namedQueries : namedQueryMap.values()) { + if (namedQueries.containsKey(namedQuery)) { return true; } } diff --git a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/PanacheHibernateRecorder.java b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/PanacheHibernateRecorder.java index 9299ed6901f69..eca507e2a0a3a 100644 --- a/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/PanacheHibernateRecorder.java +++ b/extensions/panache/hibernate-orm-panache-common/runtime/src/main/java/io/quarkus/hibernate/orm/panache/common/runtime/PanacheHibernateRecorder.java @@ -1,13 +1,12 @@ package io.quarkus.hibernate.orm.panache.common.runtime; import java.util.Map; -import java.util.Set; import io.quarkus.runtime.annotations.Recorder; @Recorder public class PanacheHibernateRecorder { - public void setNamedQueryMap(Map> namedQueryMap) { + public void setNamedQueryMap(Map> namedQueryMap) { NamedQueryUtil.setNamedQueryMap(namedQueryMap); } } diff --git a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheQuery.java b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheQuery.java index c8c6c6d8fadc0..692d3806ffd27 100644 --- a/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheQuery.java +++ b/extensions/panache/hibernate-orm-panache/runtime/src/main/java/io/quarkus/hibernate/orm/panache/PanacheQuery.java @@ -49,7 +49,6 @@ public interface PanacheQuery { * class' * single constructor, using its parameter names (or their {@link ProjectedFieldName} annotations), in the same order as the * constructor. - *
  • If this is a named query, we throw a {@link PanacheQueryException}
  • *
  • If this is already a project query of the form select distinct? new…, we throw a * {@link PanacheQueryException}
  • * @@ -57,7 +56,7 @@ public interface PanacheQuery { * @return a new query with the same state as the previous one (params, page, range, lockMode, hints, ...) but a projected * result of the type * type - * @throws PanacheQueryException if this represents a named query or an already-projected query + * @throws PanacheQueryException if this represents an already-projected query */ public PanacheQuery project(Class type); diff --git a/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheJpaCommonResourceProcessor.java b/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheJpaCommonResourceProcessor.java index 8faf7a2510d68..fca9ba39f6520 100644 --- a/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheJpaCommonResourceProcessor.java +++ b/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheJpaCommonResourceProcessor.java @@ -191,7 +191,7 @@ void lookupNamedQueries(CombinedIndexBuildItem index, JpaModelBuildItem jpaModel) { for (String modelClass : jpaModel.getAllModelClassNames()) { // lookup for `@NamedQuery` on the hierarchy and produce NamedQueryEntityClassBuildStep - Set typeNamedQueries = new HashSet<>(); + Map typeNamedQueries = new HashMap<>(); lookupNamedQueries(index, DotName.createSimple(modelClass), typeNamedQueries); namedQueries.produce(new PanacheNamedQueryEntityClassBuildStep(modelClass, typeNamedQueries)); } @@ -201,7 +201,7 @@ void lookupNamedQueries(CombinedIndexBuildItem index, @Record(ExecutionTime.STATIC_INIT) void buildNamedQueryMap(List namedQueryEntityClasses, PanacheHibernateRecorder panacheHibernateRecorder) { - Map> namedQueryMap = new HashMap<>(); + Map> namedQueryMap = new HashMap<>(); for (PanacheNamedQueryEntityClassBuildStep entityNamedQueries : namedQueryEntityClasses) { namedQueryMap.put(entityNamedQueries.getClassName(), entityNamedQueries.getNamedQueries()); } @@ -215,7 +215,7 @@ public void shutdown(ShutdownContextBuildItem shutdownContextBuildItem, PanacheH panacheHibernateRecorder.clear(shutdownContextBuildItem); } - private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set namedQueries) { + private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Map namedQueries) { ClassInfo classInfo = index.getComputingIndex().getClassByName(name); if (classInfo == null) { return; @@ -224,7 +224,8 @@ private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set< List namedQueryInstances = classInfo.annotationsMap().get(DOTNAME_NAMED_QUERY); if (namedQueryInstances != null) { for (AnnotationInstance namedQueryInstance : namedQueryInstances) { - namedQueries.add(namedQueryInstance.value("name").asString()); + namedQueries.put(namedQueryInstance.value("name").asString(), + namedQueryInstance.value("query").asString()); } } @@ -234,7 +235,7 @@ private void lookupNamedQueries(CombinedIndexBuildItem index, DotName name, Set< AnnotationValue value = namedQueriesInstance.value(); AnnotationInstance[] nestedInstances = value.asNestedArray(); for (AnnotationInstance nested : nestedInstances) { - namedQueries.add(nested.value("name").asString()); + namedQueries.put(nested.value("name").asString(), nested.value("query").asString()); } } } diff --git a/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java b/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java index 4022e8a33a2ea..a5b9088fab015 100644 --- a/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java +++ b/extensions/panache/hibernate-reactive-panache-common/deployment/src/main/java/io/quarkus/hibernate/reactive/panache/common/deployment/PanacheNamedQueryEntityClassBuildStep.java @@ -1,14 +1,14 @@ package io.quarkus.hibernate.reactive.panache.common.deployment; -import java.util.Set; +import java.util.Map; import io.quarkus.builder.item.MultiBuildItem; final class PanacheNamedQueryEntityClassBuildStep extends MultiBuildItem { private String className; - private Set namedQueries; + private Map namedQueries; - public PanacheNamedQueryEntityClassBuildStep(String className, Set namedQueries) { + public PanacheNamedQueryEntityClassBuildStep(String className, Map namedQueries) { this.className = className; this.namedQueries = namedQueries; } @@ -17,7 +17,7 @@ public String getClassName() { return this.className; } - public Set getNamedQueries() { + public Map getNamedQueries() { return namedQueries; } } diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java index 0419cc9b22ea2..c5af498cc840c 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/CommonPanacheQueryImpl.java @@ -73,11 +73,12 @@ private CommonPanacheQueryImpl(CommonPanacheQueryImpl previousQuery, String n // Builder public CommonPanacheQueryImpl project(Class type) { + String selectQuery = query; if (PanacheJpaUtil.isNamedQuery(query)) { - throw new PanacheQueryException("Unable to perform a projection on a named query"); + selectQuery = NamedQueryUtil.getNamedQuery(query.substring(1)); } - String lowerCasedTrimmedQuery = query.trim().toLowerCase(); + String lowerCasedTrimmedQuery = selectQuery.trim().toLowerCase(); if (lowerCasedTrimmedQuery.startsWith("select new ")) { throw new PanacheQueryException("Unable to perform a projection on a 'select new' query: " + query); } @@ -88,7 +89,7 @@ public CommonPanacheQueryImpl project(Class type) { // New query: SELECT new org.acme.ProjectionClass(e.field1, e.field2) from EntityClass e if (lowerCasedTrimmedQuery.startsWith("select ")) { int endSelect = lowerCasedTrimmedQuery.indexOf(" from "); - String trimmedQuery = query.trim(); + String trimmedQuery = selectQuery.trim(); // 7 is the length of "select " String selectClause = trimmedQuery.substring(7, endSelect).trim(); String from = trimmedQuery.substring(endSelect); @@ -136,7 +137,7 @@ public CommonPanacheQueryImpl project(Class type) { } select.append(") "); - return new CommonPanacheQueryImpl<>(this, select.toString() + query, "select count(*) " + query); + return new CommonPanacheQueryImpl<>(this, select.toString() + selectQuery, "select count(*) " + selectQuery); } public void filter(String filterName, Map parameters) { @@ -235,14 +236,17 @@ public void withHint(String hintName, Object value) { @SuppressWarnings("unchecked") public Uni count() { + String selectQuery; if (PanacheJpaUtil.isNamedQuery(query)) { - throw new PanacheQueryException("Unable to perform a count operation on a named query"); + selectQuery = NamedQueryUtil.getNamedQuery(query.substring(1)); + } else { + selectQuery = query; } if (count == null) { // FIXME: question about caching the result here count = em.flatMap(session -> { - Mutiny.Query countQuery = session.createQuery(countQuery()); + Mutiny.Query countQuery = session.createQuery(countQuery(selectQuery)); if (paramsArrayOrMap instanceof Map) AbstractJpaOperations.bindParameters(countQuery, (Map) paramsArrayOrMap); else @@ -253,11 +257,11 @@ public Uni count() { return count; } - private String countQuery() { + private String countQuery(String selectQuery) { if (countQuery != null) { return countQuery; } - return PanacheJpaUtil.getCountQuery(query); + return PanacheJpaUtil.getCountQuery(selectQuery); } @SuppressWarnings({ "unchecked", "rawtypes" }) diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/NamedQueryUtil.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/NamedQueryUtil.java index 75a869a1324e1..0aee47decd816 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/NamedQueryUtil.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/NamedQueryUtil.java @@ -2,7 +2,6 @@ import java.util.Collections; import java.util.Map; -import java.util.Set; import org.hibernate.query.SemanticException; import org.hibernate.query.sqm.ParsingException; @@ -12,13 +11,13 @@ public final class NamedQueryUtil { // will be replaced at augmentation phase - private static volatile Map> namedQueryMap = Collections.emptyMap(); + private static volatile Map> namedQueryMap = Collections.emptyMap(); private NamedQueryUtil() { // prevent initialization } - public static void setNamedQueryMap(Map> newNamedQueryMap) { + public static void setNamedQueryMap(Map> newNamedQueryMap) { namedQueryMap = newNamedQueryMap; } @@ -30,19 +29,29 @@ public static void checkNamedQuery(Class entityClass, String namedQuery) { } public static boolean isNamedQuery(Class entityClass, String namedQuery) { - Set namedQueries = namedQueryMap.get(entityClass.getName()); - return namedQueries != null && namedQueries.contains(namedQuery); + Map namedQueries = namedQueryMap.get(entityClass.getName()); + return namedQueries != null && namedQueries.containsKey(namedQuery); } private static boolean isNamedQuery(String namedQuery) { - for (Set namedQueries : namedQueryMap.values()) { - if (namedQueries.contains(namedQuery)) { + for (Map namedQueries : namedQueryMap.values()) { + if (namedQueries.containsKey(namedQuery)) { return true; } } return false; } + static String getNamedQuery(String namedQuery) { + for (Map namedQueries : namedQueryMap.values()) { + String query = namedQueries.get(namedQuery); + if (query != null) { + return query; + } + } + return null; + } + public static RuntimeException checkForNamedQueryMistake(IllegalArgumentException x, String originalQuery) { if (originalQuery != null && (x.getCause() instanceof SemanticException || x.getCause() instanceof ParsingException) diff --git a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/PanacheHibernateRecorder.java b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/PanacheHibernateRecorder.java index 7774ff963487d..8acc905de194b 100644 --- a/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/PanacheHibernateRecorder.java +++ b/extensions/panache/hibernate-reactive-panache-common/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/common/runtime/PanacheHibernateRecorder.java @@ -1,14 +1,13 @@ package io.quarkus.hibernate.reactive.panache.common.runtime; import java.util.Map; -import java.util.Set; import io.quarkus.runtime.ShutdownContext; import io.quarkus.runtime.annotations.Recorder; @Recorder public class PanacheHibernateRecorder { - public void setNamedQueryMap(Map> namedQueryMap) { + public void setNamedQueryMap(Map> namedQueryMap) { NamedQueryUtil.setNamedQueryMap(namedQueryMap); } diff --git a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/PanacheQuery.java b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/PanacheQuery.java index 7ac8dc2256be7..f1b4e781c1fca 100644 --- a/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/PanacheQuery.java +++ b/extensions/panache/hibernate-reactive-panache/runtime/src/main/java/io/quarkus/hibernate/reactive/panache/PanacheQuery.java @@ -49,7 +49,6 @@ public interface PanacheQuery { * class' * single constructor, using its parameter names (or their {@link ProjectedFieldName} annotations), in the same order as the * constructor. - *
  • If this is a named query, we throw a {@link PanacheQueryException}
  • *
  • If this is already a project query of the form select distinct? new…, we throw a * {@link PanacheQueryException}
  • * @@ -57,7 +56,7 @@ public interface PanacheQuery { * @return a new query with the same state as the previous one (params, page, range, lockMode, hints, ...) but a projected * result of the type * type - * @throws PanacheQueryException if this represents a named query or an already-projected query + * @throws PanacheQueryException if this represents an already-projected query */ public PanacheQuery project(Class type); diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Cat.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Cat.java index 1c1021d3fc30d..23cad520333a7 100644 --- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Cat.java +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/Cat.java @@ -2,9 +2,11 @@ import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQuery; import io.quarkus.hibernate.orm.panache.PanacheEntity; +@NamedQuery(name = "Cat.NameAndOwnerName", query = "select c.name, c.owner.name as ownerName from Cat c") @Entity public class Cat extends PanacheEntity { diff --git a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java index 375605f439b66..8643dda0b99cb 100644 --- a/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java +++ b/integration-tests/hibernate-orm-panache/src/main/java/io/quarkus/it/panache/TestEndpoint.java @@ -1264,6 +1264,9 @@ public String testProjection() { person = Person.find("name = :name", Parameters.with("name", "2")).project(PersonName.class).firstResult(); Assertions.assertEquals("2", person.name); + person = Person.find("#Person.getByName", Parameters.with("name", "2")).project(PersonName.class).firstResult(); + Assertions.assertEquals("2", person.name); + PanacheQuery query = Person.findAll().project(PersonName.class).page(0, 2); Assertions.assertEquals(1, query.list().size()); query.nextPage(); @@ -1289,10 +1292,15 @@ public String testProjection() { .project(CatProjectionBean.class).firstResult(); Assertions.assertEquals("Julie", fieldsProjection.getOwnerName()); + fieldsProjection = Cat.find("#Cat.NameAndOwnerName") + .project(CatProjectionBean.class).firstResult(); + Assertions.assertEquals("Julie", fieldsProjection.getOwnerName()); + PanacheQueryException exception = Assertions.assertThrows(PanacheQueryException.class, () -> Cat.find("select new FakeClass('fake_cat', 'fake_owner', 12.5 from Cat c)") .project(CatProjectionBean.class).firstResult()); - Assertions.assertTrue(exception.getMessage().startsWith("Unable to perform a projection on a 'select new' query")); + Assertions.assertTrue( + exception.getMessage().startsWith("Unable to perform a projection on a 'select [distinct]? new' query")); CatProjectionBean constantProjection = Cat.find("select 'fake_cat', 'fake_owner', 12.5D from Cat c") .project(CatProjectionBean.class).firstResult(); diff --git a/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/Cat.java b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/Cat.java index a562c873bbcb3..7dbb9a434a010 100644 --- a/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/Cat.java +++ b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/Cat.java @@ -2,9 +2,11 @@ import jakarta.persistence.Entity; import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQuery; import io.quarkus.hibernate.reactive.panache.PanacheEntity; +@NamedQuery(name = "Cat.NameAndOwnerName", query = "select c.name, c.owner.name as ownerName from Cat c where c.name = :name") @Entity public class Cat extends PanacheEntity { diff --git a/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java index 90ef071ce4f21..655962a4f1559 100644 --- a/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java +++ b/integration-tests/hibernate-reactive-panache/src/main/java/io/quarkus/it/panache/reactive/TestEndpoint.java @@ -1598,6 +1598,11 @@ public Uni testProjection() { return Person.find("name = :name", Parameters.with("name", "2")).project(PersonName.class) . firstResult(); + }).flatMap(person -> { + Assertions.assertEquals("2", person.name); + return Person.find("#Person.getByName", Parameters.with("name", "2")).project(PersonName.class) + . firstResult(); + }).flatMap(person -> { Assertions.assertEquals("2", person.name); @@ -1646,6 +1651,15 @@ public Uni testProjection2() { Assertions.assertEquals(ownerName, catView.ownerName); Assertions.assertNull(catView.weight); }) + .chain(() -> Cat.find("#Cat.NameAndOwnerName", + Parameters.with("name", catName)) + .project(CatProjectionBean.class) + . singleResult()) + .invoke(catView -> { + Assertions.assertEquals(catName, catView.name); + Assertions.assertEquals(ownerName, catView.ownerName); + Assertions.assertNull(catView.weight); + }) .chain(() -> Cat.find("select 'fake_cat', 'fake_owner', 12.5D from Cat c") .project(CatProjectionBean.class) . firstResult())