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 + } + + +}
