This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch 8.0.x-hibernate7 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit f2bbb1f3f501447e88a6f3886f60737ee8bcd093 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Thu Feb 19 15:58:47 2026 -0600 Fluent refactoring of CriteriaMethodInvoker dispatch logic --- .../groovy/grails/orm/CriteriaMethodInvoker.java | 383 ++++++++++++--------- 1 file changed, 212 insertions(+), 171 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/grails/orm/CriteriaMethodInvoker.java b/grails-data-hibernate7/core/src/main/groovy/grails/orm/CriteriaMethodInvoker.java index 0092d8ab72..5e645509f7 100644 --- a/grails-data-hibernate7/core/src/main/groovy/grails/orm/CriteriaMethodInvoker.java +++ b/grails-data-hibernate7/core/src/main/groovy/grails/orm/CriteriaMethodInvoker.java @@ -19,11 +19,10 @@ import java.beans.PropertyDescriptor; import java.util.Collection; import java.util.Map; -/** - * Helper class to handle method invocation for HibernateCriteriaBuilder. - */ public class CriteriaMethodInvoker { + private static final Object UNHANDLED = new Object(); + private final HibernateCriteriaBuilder builder; public CriteriaMethodInvoker(HibernateCriteriaBuilder builder) { @@ -31,203 +30,245 @@ public class CriteriaMethodInvoker { } public Object invokeMethod(String name, Object[] args) { - HibernateQuery hibernateQuery = builder.getHibernateQuery(); CriteriaMethods method = CriteriaMethods.fromName(name); - if (method != null && isCriteriaConstructionMethod(method, args)) { - switch (method) { - case GET_CALL -> builder.setUniqueResult(true); - case SCROLL_CALL -> builder.setScroll(true); - case COUNT_CALL -> builder.setCount(true); - case LIST_DISTINCT_CALL -> builder.setDistinct(true); - } + Object result = tryCriteriaConstruction(method, args); + if (result != UNHANDLED) return result; - // Check for pagination params - if (method == CriteriaMethods.LIST_CALL && args.length == 2) { - builder.setPaginationEnabledList(true); - if (args[0] instanceof Map map) { - if (map.get("max") instanceof Number max) { - hibernateQuery.maxResults(max.intValue()); - } - if (map.get("offset") instanceof Number offset) { - hibernateQuery.firstResult(offset.intValue()); - } + result = tryMetaMethod(name, args); + if (result != UNHANDLED) return result; + + result = tryAssociationOrJunction(name, method, args); + if (result != UNHANDLED) return result; + + result = trySimpleCriteria(name, method, args); + if (result != UNHANDLED) return result; + + result = tryPropertyCriteria(method, args); + if (result != UNHANDLED) return result; + + return CriteriaMethods.fromName(name, HibernateCriteriaBuilder.class, args); + } + + private Object tryCriteriaConstruction(CriteriaMethods method, Object[] args) { + if (method == null || !isCriteriaConstructionMethod(method, args)) { + return UNHANDLED; + } + + HibernateQuery hibernateQuery = builder.getHibernateQuery(); + switch (method) { + case GET_CALL -> builder.setUniqueResult(true); + case SCROLL_CALL -> builder.setScroll(true); + case COUNT_CALL -> builder.setCount(true); + case LIST_DISTINCT_CALL -> builder.setDistinct(true); + } + + // Check for pagination params + if (method == CriteriaMethods.LIST_CALL && args.length == 2) { + builder.setPaginationEnabledList(true); + if (args[0] instanceof Map map) { + if (map.get("max") instanceof Number max) { + hibernateQuery.maxResults(max.intValue()); + } + if (map.get("offset") instanceof Number offset) { + hibernateQuery.firstResult(offset.intValue()); } - invokeClosureNode(args[1]); - } else { - invokeClosureNode(args[0]); } + invokeClosureNode(args[1]); + } else { + invokeClosureNode(args[0]); + } - Object result; - if (!builder.isUniqueResult()) { - if (builder.isDistinct()) { - hibernateQuery.distinct(); - result = hibernateQuery.list(); - } else if (builder.isCount()) { - hibernateQuery.projections().count(); - result = hibernateQuery.singleResult(); - } else if (builder.isPaginationEnabledList()) { - Map argMap = (Map) args[0]; - final String sortField = (String) argMap.get(HibernateQueryConstants.ARGUMENT_SORT); - if (sortField != null) { - boolean ignoreCase = true; - Object caseArg = argMap.get(HibernateQueryConstants.ARGUMENT_IGNORE_CASE); - if (caseArg instanceof Boolean) { - ignoreCase = (Boolean) caseArg; - } - final String orderParam = (String) argMap.get(HibernateQueryConstants.ARGUMENT_ORDER); - final Query.Order.Direction direction = Query.Order.Direction.DESC.name().equalsIgnoreCase(orderParam) ? Query.Order.Direction.DESC : Query.Order.Direction.ASC; - Query.Order order; - if (ignoreCase) { - order = new Query.Order(sortField, direction); - order.ignoreCase(); - } else { - order = new Query.Order(sortField, direction); - } - hibernateQuery.order(order); + Object result; + if (!builder.isUniqueResult()) { + if (builder.isDistinct()) { + hibernateQuery.distinct(); + result = hibernateQuery.list(); + } else if (builder.isCount()) { + hibernateQuery.projections().count(); + result = hibernateQuery.singleResult(); + } else if (builder.isPaginationEnabledList()) { + Map argMap = (Map) args[0]; + final String sortField = (String) argMap.get(HibernateQueryConstants.ARGUMENT_SORT); + if (sortField != null) { + boolean ignoreCase = true; + Object caseArg = argMap.get(HibernateQueryConstants.ARGUMENT_IGNORE_CASE); + if (caseArg instanceof Boolean) { + ignoreCase = (Boolean) caseArg; } - result = new PagedResultList<>(hibernateQuery); - } else { - result = hibernateQuery.list(); + final String orderParam = (String) argMap.get(HibernateQueryConstants.ARGUMENT_ORDER); + final Query.Order.Direction direction = Query.Order.Direction.DESC.name().equalsIgnoreCase(orderParam) ? Query.Order.Direction.DESC : Query.Order.Direction.ASC; + Query.Order order; + if (ignoreCase) { + order = new Query.Order(sortField, direction); + order.ignoreCase(); + } else { + order = new Query.Order(sortField, direction); + } + hibernateQuery.order(order); } + result = new PagedResultList<>(hibernateQuery); } else { - result = hibernateQuery.singleResult(); + result = hibernateQuery.list(); } - if (!builder.isParticipate()) { - builder.closeSession(); - } - return result; + } else { + result = hibernateQuery.singleResult(); + } + if (!builder.isParticipate()) { + builder.closeSession(); } + return result; + } + private Object tryMetaMethod(String name, Object[] args) { MetaMethod metaMethod = builder.getMetaClass().getMetaMethod(name, args); if (metaMethod != null) { return metaMethod.invoke(builder, args); } + return UNHANDLED; + } - if (isAssociationQueryMethod(args) || isAssociationQueryWithJoinSpecificationMethod(args)) { - final boolean hasMoreThanOneArg = args.length > 1; - Closure callable = hasMoreThanOneArg ? (Closure) args[1] : (Closure) args[0]; - JoinType joinType = hasMoreThanOneArg ? builder.convertFromInt((Integer) args[0]) : builder.convertFromInt(0); + private Object tryAssociationOrJunction(String name, CriteriaMethods method, Object[] args) { + if (!isAssociationQueryMethod(args) && !isAssociationQueryWithJoinSpecificationMethod(args)) { + return UNHANDLED; + } - if (method != null) { - switch (method) { - case AND: - hibernateQuery.and(callable); - return name; - case OR: - hibernateQuery.or(callable); - return name; - case NOT: - hibernateQuery.not(callable); + HibernateQuery hibernateQuery = builder.getHibernateQuery(); + final boolean hasMoreThanOneArg = args.length > 1; + Closure callable = hasMoreThanOneArg ? (Closure) args[1] : (Closure) args[0]; + JoinType joinType = hasMoreThanOneArg ? builder.convertFromInt((Integer) args[0]) : builder.convertFromInt(0); + + if (method != null) { + switch (method) { + case AND: + hibernateQuery.and(callable); + return name; + case OR: + hibernateQuery.or(callable); + return name; + case NOT: + hibernateQuery.not(callable); + return name; + case PROJECTIONS: + if (args.length == 1 && (args[0] instanceof Closure)) { + invokeClosureNode(callable); return name; - case PROJECTIONS: - if (args.length == 1 && (args[0] instanceof Closure)) { - invokeClosureNode(callable); - return name; - } - break; + } + break; + } + } + + final PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(builder.getTargetClass(), name); + if (pd != null && pd.getReadMethod() != null) { + final Metamodel metamodel = builder.getSessionFactory().getMetamodel(); + final EntityType<?> entityType = metamodel.entity(builder.getTargetClass()); + final Attribute<?, ?> attribute = entityType.getAttribute(name); + + if (attribute.isAssociation()) { + Class oldTargetClass = builder.getTargetClass(); + builder.setTargetClass(builder.getClassForAssociationType(attribute)); + if (builder.getTargetClass().equals(oldTargetClass) && !hasMoreThanOneArg) { + joinType = JoinType.LEFT; // default to left join if joining on the same table } + + hibernateQuery.join(name, joinType); + hibernateQuery.in(name, new DetachedCriteria(builder.getTargetClass()).build(callable)); + builder.setTargetClass(oldTargetClass); + + return name; } + } + return UNHANDLED; + } - final PropertyDescriptor pd = BeanUtils.getPropertyDescriptor(builder.getTargetClass(), name); - if (pd != null && pd.getReadMethod() != null) { - final Metamodel metamodel = builder.getSessionFactory().getMetamodel(); - final EntityType<?> entityType = metamodel.entity(builder.getTargetClass()); - final Attribute<?, ?> attribute = entityType.getAttribute(name); - - if (attribute.isAssociation()) { - Class oldTargetClass = builder.getTargetClass(); - builder.setTargetClass(builder.getClassForAssociationType(attribute)); - if (builder.getTargetClass().equals(oldTargetClass) && !hasMoreThanOneArg) { - joinType = JoinType.LEFT; // default to left join if joining on the same table + private Object trySimpleCriteria(String name, CriteriaMethods method, Object[] args) { + if (args.length != 1 || args[0] == null) { + return UNHANDLED; + } + + Object value = args[0]; + if (method != null) { + HibernateQuery hibernateQuery = builder.getHibernateQuery(); + switch (method) { + case ID_EQUALS: + return builder.eq("id", value); + case IS_NULL, IS_NOT_NULL, IS_EMPTY, IS_NOT_EMPTY: + if (!(value instanceof String)) { + builder.throwRuntimeException(new IllegalArgumentException("call to [" + name + "] with value [" + + value + "] requires a String value.")); } + String propertyName = builder.calculatePropertyName((String) value); + switch (method) { + case IS_NULL -> hibernateQuery.isNull(propertyName); + case IS_NOT_NULL -> hibernateQuery.isNotNull(propertyName); + case IS_EMPTY -> hibernateQuery.isEmpty(propertyName); + case IS_NOT_EMPTY -> hibernateQuery.isNotEmpty(propertyName); + } + return name; + } + } + return UNHANDLED; + } - hibernateQuery.join(name, joinType); - hibernateQuery.in(name, new DetachedCriteria(builder.getTargetClass()).build(callable)); - builder.setTargetClass(oldTargetClass); + private Object tryPropertyCriteria(CriteriaMethods method, Object[] args) { + if (method == null || args.length < 2 || !(args[0] instanceof String)) { + return UNHANDLED; + } - return name; + String propertyName = builder.calculatePropertyName((String) args[0]); + switch (method) { + case RLIKE: + return builder.rlike(propertyName, args[1]); + case BETWEEN: + if (args.length >= 3) { + return builder.between(propertyName, args[1], args[2]); } - } - } else if (args.length == 1 && args[0] != null) { - Object value = args[0]; - if (method != null) { - switch (method) { - case ID_EQUALS: - return builder.eq("id", value); - case IS_NULL, IS_NOT_NULL, IS_EMPTY, IS_NOT_EMPTY: - if (!(value instanceof String)) { - builder.throwRuntimeException(new IllegalArgumentException("call to [" + name + "] with value [" + - value + "] requires a String value.")); - } - String propertyName = builder.calculatePropertyName((String) value); - switch (method) { - case IS_NULL -> hibernateQuery.isNull(propertyName); - case IS_NOT_NULL -> hibernateQuery.isNotNull(propertyName); - case IS_EMPTY -> hibernateQuery.isEmpty(propertyName); - case IS_NOT_EMPTY -> hibernateQuery.isNotEmpty(propertyName); - } - return name; + break; + case EQUALS: + if (args.length == 3 && args[2] instanceof Map) { + return builder.eq(propertyName, args[1], (Map) args[2]); } - } - } else if (args.length >= 2 && args[0] instanceof String propertyName) { - propertyName = builder.calculatePropertyName(propertyName); - if (method != null) { - switch (method) { - case RLIKE: - return builder.rlike(propertyName, args[1]); - case BETWEEN: - if (args.length >= 3) { - return builder.between(propertyName, args[1], args[2]); - } - break; - case EQUALS: - if (args.length == 3 && args[2] instanceof Map) { - return builder.eq(propertyName, args[1], (Map) args[2]); - } - return builder.eq(propertyName, args[1]); - case EQUALS_PROPERTY: - return builder.eqProperty(propertyName, args[1].toString()); - case GREATER_THAN: - return builder.gt(propertyName, args[1]); - case GREATER_THAN_PROPERTY: - return builder.gtProperty(propertyName, args[1].toString()); - case GREATER_THAN_OR_EQUAL: - return builder.ge(propertyName, args[1]); - case GREATER_THAN_OR_EQUAL_PROPERTY: - return builder.geProperty(propertyName, args[1].toString()); - case ILIKE: - return builder.ilike(propertyName, args[1]); - case IN: - if (args[1] instanceof Collection) { - return builder.in(propertyName, (Collection) args[1]); - } else if (args[1] instanceof Object[]) { - return builder.in(propertyName, (Object[]) args[1]); - } - break; - case LESS_THAN: - return builder.lt(propertyName, args[1]); - case LESS_THAN_PROPERTY: - return builder.ltProperty(propertyName, args[1].toString()); - case LESS_THAN_OR_EQUAL: - return builder.le(propertyName, args[1]); - case LESS_THAN_OR_EQUAL_PROPERTY: - return builder.leProperty(propertyName, args[1].toString()); - case LIKE: - return builder.like(propertyName, args[1]); - case NOT_EQUAL: - return builder.ne(propertyName, args[1]); - case NOT_EQUAL_PROPERTY: - return builder.neProperty(propertyName, args[1].toString()); - case SIZE_EQUALS: - if (args[1] instanceof Number) { - return builder.sizeEq(propertyName, ((Number) args[1]).intValue()); - } - break; + return builder.eq(propertyName, args[1]); + case EQUALS_PROPERTY: + return builder.eqProperty(propertyName, args[1].toString()); + case GREATER_THAN: + return builder.gt(propertyName, args[1]); + case GREATER_THAN_PROPERTY: + return builder.gtProperty(propertyName, args[1].toString()); + case GREATER_THAN_OR_EQUAL: + return builder.ge(propertyName, args[1]); + case GREATER_THAN_OR_EQUAL_PROPERTY: + return builder.geProperty(propertyName, args[1].toString()); + case ILIKE: + return builder.ilike(propertyName, args[1]); + case IN: + if (args[1] instanceof Collection) { + return builder.in(propertyName, (Collection) args[1]); + } else if (args[1] instanceof Object[]) { + return builder.in(propertyName, (Object[]) args[1]); } - } + break; + case LESS_THAN: + return builder.lt(propertyName, args[1]); + case LESS_THAN_PROPERTY: + return builder.ltProperty(propertyName, args[1].toString()); + case LESS_THAN_OR_EQUAL: + return builder.le(propertyName, args[1]); + case LESS_THAN_OR_EQUAL_PROPERTY: + return builder.leProperty(propertyName, args[1].toString()); + case LIKE: + return builder.like(propertyName, args[1]); + case NOT_EQUAL: + return builder.ne(propertyName, args[1]); + case NOT_EQUAL_PROPERTY: + return builder.neProperty(propertyName, args[1].toString()); + case SIZE_EQUALS: + if (args[1] instanceof Number) { + return builder.sizeEq(propertyName, ((Number) args[1]).intValue()); + } + break; } - return CriteriaMethods.fromName(name, HibernateCriteriaBuilder.class, args); + return UNHANDLED; } private boolean isAssociationQueryMethod(Object[] args) {
