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

borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit dab82d7d344d154534615feb6a57ff2d8326b645
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Tue Mar 3 21:27:11 2026 -0600

    Restore NamedCriteria to Hibernate 5
---
 .../org/grails/datastore/gorm/GormEnhancer.groovy  |  71 +++++
 .../org/grails/datastore/gorm/GormEntity.groovy    |  26 ++
 .../org/grails/datastore/gorm/GormStaticApi.groovy |   7 +
 .../datastore/gorm/query/NamedCriteriaProxy.groovy | 297 +++++++++++++++++++++
 4 files changed, 401 insertions(+)

diff --git 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEnhancer.groovy
 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEnhancer.groovy
index 3a14c3af62..3660d1bb7b 100644
--- 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEnhancer.groovy
+++ 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEnhancer.groovy
@@ -54,6 +54,11 @@ import org.grails.datastore.mapping.reflect.ClassUtils
 import org.grails.datastore.mapping.reflect.MetaClassUtils
 import org.grails.datastore.mapping.reflect.NameUtils
 import org.grails.datastore.mapping.transactions.TransactionCapableDatastore
+import org.grails.datastore.gorm.query.GormQueryOperations
+import org.grails.datastore.gorm.query.NamedCriteriaProxy
+import org.grails.datastore.gorm.query.NamedQueriesBuilder
+import org.grails.datastore.mapping.model.config.GormProperties
+import org.grails.datastore.mapping.reflect.ClassPropertyFetcher
 
 /**
  * Enhances a class with GORM behavior
@@ -233,6 +238,72 @@ class GormEnhancer implements Closeable {
         }
     }
 
+    /**
+     * @deprecated Use #createNamedQuery(entity, queryName) instead
+     */
+    @Deprecated
+    static GormQueryOperations findNamedQuery(Class entity, String queryName) {
+        return createNamedQuery(entity, queryName)
+    }
+
+    /**
+     * Finds a named query for the given entity
+     *
+     * @param entity The entity name
+     * @param queryName The query name
+     *
+     * @return The named query or null if it doesn't exist
+     */
+    static GormQueryOperations createNamedQuery(Class entity, String 
queryName) {
+        createNamedQuery(entity, queryName, null)
+    }
+
+    /**
+     * Finds a named query for the given entity
+     *
+     * @param entity The entity name
+     * @param queryName The query name
+     *
+     * @return The named query or null if it doesn't exist
+     */
+    static GormQueryOperations createNamedQuery(Class entity, String 
queryName, Object... args) {
+        def className = entity.getName()
+        def namedQueries = NAMED_QUERIES.get(className)
+        if (namedQueries == null) {
+            synchronized (NAMED_QUERIES) {
+                namedQueries = NAMED_QUERIES.get(className)
+                if (namedQueries == null) {
+                    ClassPropertyFetcher cpf = 
ClassPropertyFetcher.forClass(entity)
+                    Closure closure = 
cpf.getStaticPropertyValue(GormProperties.NAMED_QUERIES, Closure.class)
+                    if (closure != null) {
+                        closure = (Closure) closure.clone()
+                        def evaluator = new NamedQueriesBuilder()
+                        namedQueries = evaluator.evaluate(closure)
+                        NAMED_QUERIES.put(className, namedQueries)
+                    }
+                    else {
+                        NAMED_QUERIES.put(className, Collections.emptyMap())
+                        return null
+                    }
+                }
+            }
+        }
+        return buildNamedCriteriaProxy(entity, namedQueries, queryName, args)
+    }
+
+    private static NamedCriteriaProxy buildNamedCriteriaProxy(Class entity, 
Map<String, Closure> namedQueries, String queryName, Object... args) {
+        NamedCriteriaProxy namedCriteriaProxy = null
+        GormStaticApi staticApi = findStaticApi(entity)
+        Closure namedQueryClosure = namedQueries.get(queryName)
+        if (namedQueryClosure != null) {
+            namedCriteriaProxy = new NamedCriteriaProxy((Closure) 
namedQueryClosure.clone(), staticApi.gormPersistentEntity, 
staticApi.gormDynamicFinders)
+            if (args != null) {
+                namedCriteriaProxy.call(args)
+            }
+        }
+        return namedCriteriaProxy
+    }
+
     /**
      * Find a static API for the give entity type and qualifier (the 
connection name)
      *
diff --git 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy
 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy
index bcb10a7314..dac2b19c9b 100644
--- 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy
+++ 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormEntity.groovy
@@ -491,6 +491,32 @@ trait GormEntity<D> implements GormValidateable, 
DirtyCheckable, GormEntityApi<D
         currentGormStaticApi().whereAny(callable)
     }
 
+    /**
+     * Looks up a named query
+     *
+     * @param queryName The name of the query
+     * @return The query or null
+     *
+     * @deprecated Named queries are deprecated, use where queries instead
+     */
+    @Deprecated
+    static GormQueryOperations<D> getNamedQuery(String queryName) {
+        GormEnhancer.createNamedQuery(this, queryName)
+    }
+
+    /**
+     * Looks up a named query
+     *
+     * @param queryName The name of the query
+     * @return The query or null
+     *
+     * @deprecated Named queries are deprecated, use where queries instead
+     */
+    @Deprecated
+    static GormQueryOperations<D> getNamedQuery(String queryName, Object... 
args) {
+        GormEnhancer.createNamedQuery(this, queryName, args)
+    }
+
     /**
      * Uses detached criteria to build a query and then execute it returning a 
list
      *
diff --git 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormStaticApi.groovy
 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormStaticApi.groovy
index bf00c7cf68..f1a2d2d5ee 100644
--- 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormStaticApi.groovy
+++ 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/GormStaticApi.groovy
@@ -55,6 +55,7 @@ import 
org.springframework.transaction.PlatformTransactionManager
 import org.springframework.transaction.TransactionDefinition
 import org.springframework.transaction.support.DefaultTransactionDefinition
 import org.springframework.util.Assert
+import org.grails.datastore.gorm.query.NamedCriteriaProxy
 
 /**
  * Static methods of the GORM API.
@@ -140,6 +141,12 @@ class GormStaticApi<D> extends AbstractGormApi<D> 
implements GormAllOperations<D
     def methodMissing(String methodName, Object args) {
         FinderMethod method = gormDynamicFinders.find { FinderMethod f -> 
f.isMethodMatch(methodName) }
         if (!method) {
+            if (args && args[-1] instanceof Closure) {
+                NamedCriteriaProxy proxy = 
GormEnhancer.createNamedQuery(persistentClass, methodName)
+                if (proxy != null) {
+                    return proxy.call(args)
+                }
+            }
             throw new MissingMethodException(methodName, persistentClass, args)
         }
 
diff --git 
a/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/NamedCriteriaProxy.groovy
 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/NamedCriteriaProxy.groovy
new file mode 100644
index 0000000000..0d8aea23fd
--- /dev/null
+++ 
b/grails-datamapping-core/src/main/groovy/org/grails/datastore/gorm/query/NamedCriteriaProxy.groovy
@@ -0,0 +1,297 @@
+/*
+ *  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
+ *
+ *    https://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.grails.datastore.gorm.query
+
+import grails.gorm.CriteriaBuilder
+import org.grails.datastore.gorm.GormEnhancer
+import org.grails.datastore.gorm.finders.DynamicFinder
+import org.grails.datastore.gorm.finders.FinderMethod
+import org.grails.datastore.mapping.model.PersistentEntity
+import org.grails.datastore.mapping.query.api.BuildableCriteria
+import org.grails.datastore.mapping.reflect.NameUtils
+import org.springframework.util.ReflectionUtils
+
+import java.lang.reflect.Modifier
+
+
+
+/**
+ * Handles named queries
+ *
+ * @author Graeme Rocher
+ * @author Jeff Brown
+ *
+ *
+ * @since 5.0
+ * @deprecated Use where queries instead
+ */
+@Deprecated
+class NamedCriteriaProxy<D> implements GormQueryOperations<D> {
+
+    final Closure criteriaClosure
+    final PersistentEntity entity
+    final List finders
+    private namedCriteriaParams
+    private previousInChain
+    private queryBuilder
+
+
+    NamedCriteriaProxy(Closure criteriaClosure, PersistentEntity entity, List 
finders) {
+        this.criteriaClosure = (Closure)criteriaClosure.clone()
+        this.criteriaClosure.delegate = this
+        this.entity = entity
+        this.finders = finders
+    }
+
+    private invokeCriteriaClosure(additionalCriteriaClosure = null) {
+        def crit = getPreparedCriteriaClosure(additionalCriteriaClosure)
+        crit()
+    }
+
+    def call(Object[] params) {
+        if (params && params[-1] instanceof Closure) {
+            Closure additionalCriteriaClosure = (Closure)params[-1]
+            params = params.length > 1 ? params[0..-2] : [:]
+            if (params) {
+                if (params[-1] instanceof Map) {
+                    if (params.length > 1) {
+                        namedCriteriaParams = params[0..-2] as Object[]
+                    }
+                    return list((Map)params[-1], additionalCriteriaClosure)
+                } else {
+                    namedCriteriaParams = params
+                    return list(Collections.emptyMap(), 
additionalCriteriaClosure)
+                }
+            }
+            else {
+                return list(Collections.emptyMap(), additionalCriteriaClosure)
+            }
+        }
+        else {
+            namedCriteriaParams = params
+            this
+        }
+    }
+
+    D get(Serializable id) {
+        id = (Serializable)entity.mappingContext.conversionService.convert(id, 
entity.identity.type)
+        def getClosure = {
+            queryBuilder = delegate
+            invokeCriteriaClosure()
+            eq 'id', id
+            uniqueResult = true
+        }
+        return  entity.javaClass.createCriteria().get(getClosure)
+    }
+
+    @Override
+    D find(Map args = Collections.emptyMap(), Closure additionalCriteria = 
null) {
+        return get(args, additionalCriteria)
+    }
+
+    D find(Closure additionalCriteria) {
+        return get(Collections.emptyMap(), additionalCriteria)
+    }
+
+    D get(Closure additionalCriteria) {
+        return get(Collections.emptyMap(), additionalCriteria)
+    }
+
+    @Override
+    D get(Map paramsMap = Collections.emptyMap(), Closure additionalCriteria = 
null) {
+        def conversionService = entity.mappingContext.conversionService
+        return (D) entity.javaClass.createCriteria().get( {
+            queryBuilder = delegate
+            maxResults 1
+            uniqueResult = true
+            invokeCriteriaClosure(additionalCriteria)
+            if (paramsMap && queryBuilder instanceof CriteriaBuilder) {
+                DynamicFinder.populateArgumentsForCriteria(entity.javaClass, 
queryBuilder.query, paramsMap)
+            }
+        } )
+    }
+
+
+    List<D> list(Closure additionalCriteria) {
+        list(Collections.emptyMap(), additionalCriteria)
+    }
+
+    @Override
+    List<D> list(Map paramsMap = Collections.emptyMap(), Closure 
additionalCriteria = null) {
+        def conversionService = entity.mappingContext.conversionService
+        def callable = {
+            queryBuilder = delegate
+            invokeCriteriaClosure(additionalCriteria)
+        }
+        if(paramsMap.isEmpty()) {
+            return entity.javaClass.createCriteria().list(callable)
+        }
+        else {
+            return entity.javaClass.createCriteria().list(paramsMap, callable)
+        }
+    }
+
+    List<D> listDistinct(Closure additionalCriteria) {
+        listDistinct(Collections.emptyMap(), additionalCriteria)
+    }
+
+    List<D> listDistinct(Map paramsMap = Collections.emptyMap(), Closure 
additionalCriteria = null) {
+        def conversionService = entity.mappingContext.conversionService
+        return entity.javaClass.withCriteria {
+            queryBuilder = delegate
+            projections {
+                distinct()
+            }
+            invokeCriteriaClosure(additionalCriteria)
+            if (paramsMap?.max) {
+                maxResults conversionService.convert(paramsMap.max, Integer)
+            }
+            if (paramsMap?.offset) {
+                firstResult conversionService.convert(paramsMap.offset, 
Integer)
+            }
+            if (paramsMap && queryBuilder instanceof CriteriaBuilder) {
+                DynamicFinder.populateArgumentsForCriteria(entity.javaClass, 
queryBuilder.query, paramsMap)
+            }
+        }
+    }
+
+    @Override
+    Number count(Map args = Collections.emptyMap(), Closure additionalCriteria 
= null) {
+        def countClosure = {
+            queryBuilder = delegate
+            invokeCriteriaClosure(additionalCriteria)
+        }
+        entity.javaClass.createCriteria().count(countClosure)
+    }
+
+    Number count(Closure additionalCriteria ) {
+        count(Collections.emptyMap(), additionalCriteria)
+    }
+
+
+    D findWhere(Map params) {
+        def queryClosure = {
+            queryBuilder = delegate
+            invokeCriteriaClosure()
+            params.each {key, val ->
+                eq key, val
+            }
+            maxResults 1
+            uniqueResult = true
+        }
+        entity.javaClass.withCriteria(queryClosure)
+    }
+
+    List<D> findAllWhere(Map params) {
+        def queryClosure = {
+            queryBuilder = delegate
+            invokeCriteriaClosure()
+            params.each {key, val ->
+                eq key, val
+            }
+        }
+        entity.javaClass.withCriteria(queryClosure)
+    }
+
+    def propertyMissing(String propertyName) {
+        def javaClass = entity.javaClass
+        if (javaClass.metaClass.getMetaProperty(propertyName)) {
+            def nextInChain = 
javaClass.metaClass.getMetaProperty(propertyName).getProperty(javaClass)
+            nextInChain.previousInChain = this
+            return nextInChain
+        }
+        throw new MissingPropertyException(propertyName, NamedCriteriaProxy)
+    }
+
+    void propertyMissing(String propName, val) {
+        queryBuilder?."${propName}" = val
+    }
+
+    def methodMissing(String methodName, args) {
+
+        def javaClass = entity.javaClass
+        FinderMethod method = finders.find { FinderMethod f ->  
f.isMethodMatch(methodName) }
+
+        if (method) {
+            def preparedClosure = getPreparedCriteriaClosure()
+            return method.invoke(javaClass, methodName, preparedClosure, args)
+        }
+
+        if (queryBuilder == null) {
+            NamedCriteriaProxy nextInChain = 
GormEnhancer.createNamedQuery(javaClass, methodName)
+            if(nextInChain != null) {
+                nextInChain.previousInChain = this
+                return nextInChain.call(args)
+            }
+            else {
+                throw new MissingMethodException(methodName, javaClass, args)
+            }
+        }
+        else {
+
+            try {
+                return queryBuilder."${methodName}"(*args)
+            } catch (MissingMethodException e) {
+                try {
+                    return queryBuilder."${methodName}"(*args)
+                } catch (MissingMethodException mme) {
+                    def targetType = queryBuilder?.targetClass
+                    NamedCriteriaProxy proxy = 
GormEnhancer.createNamedQuery(targetType, methodName)
+                    if (proxy != null) {
+                        Closure nestedCriteria = proxy.criteriaClosure.clone()
+                        
nestedCriteria.setResolveStrategy(Closure.DELEGATE_ONLY)
+                        nestedCriteria.delegate = this
+                        nestedCriteria(*args)
+                        return this
+                    }
+                    else {
+                        throw mme
+                    }
+                }
+            }
+        }
+
+    }
+
+    private Closure getPreparedCriteriaClosure(additionalCriteriaClosure = 
null) {
+        Closure closureClone = criteriaClosure.clone()
+        closureClone.resolveStrategy = Closure.DELEGATE_FIRST
+        if (namedCriteriaParams) {
+            closureClone = closureClone.curry(*namedCriteriaParams)
+        }
+        def c = {
+            closureClone.delegate = delegate
+            if (previousInChain) {
+                def previousClosure = 
previousInChain.getPreparedCriteriaClosure()
+                previousClosure.delegate = delegate
+                previousClosure()
+            }
+            closureClone()
+            if (additionalCriteriaClosure) {
+                additionalCriteriaClosure = additionalCriteriaClosure.clone()
+                additionalCriteriaClosure.delegate = delegate
+                additionalCriteriaClosure()
+            }
+        }
+        c.delegate = this
+        return c
+    }
+
+
+}

Reply via email to