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 ace439b0d069007898bda826e074a89fa3b07ba2
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Sun Feb 8 17:00:14 2026 -0600

    refactored CollectionSecondPassBinder
---
 .../cfg/domainbinding/OrderByClauseBuilder.java    | 119 +++++++++++++++++++++
 .../secondpass/CollectionSecondPassBinder.java     | 108 +------------------
 .../domainbinding/OrderByClauseBuilderSpec.groovy  | 103 ++++++++++++++++++
 3 files changed, 226 insertions(+), 104 deletions(-)

diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/OrderByClauseBuilder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/OrderByClauseBuilder.java
new file mode 100644
index 0000000000..7a81e42fea
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/OrderByClauseBuilder.java
@@ -0,0 +1,119 @@
+package org.grails.orm.hibernate.cfg.domainbinding;
+
+import org.grails.datastore.mapping.model.DatastoreConfigurationException;
+import org.hibernate.boot.model.internal.BinderHelper;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Property;
+import org.hibernate.mapping.Selectable;
+import org.hibernate.mapping.SingleTableSubclass;
+
+import java.util.Iterator;
+import java.util.StringTokenizer;
+
+/**
+ * Utility class to build SQL order by clauses from HQL-style order by strings.
+ */
+public class OrderByClauseBuilder {
+
+    @SuppressWarnings("unchecked")
+    public String buildOrderByClause(String hqlOrderBy, PersistentClass 
associatedClass, String role, String defaultOrder) {
+        String orderByString = null;
+        if (hqlOrderBy != null) {
+            java.util.List<String> properties = new java.util.ArrayList<>();
+            java.util.List<String> ordering = new java.util.ArrayList<>();
+            StringBuilder orderByBuffer = new StringBuilder();
+            if (hqlOrderBy.length() == 0) {
+                //order by id
+                Iterator<?> it = 
associatedClass.getIdentifier().getSelectables().iterator();
+                while (it.hasNext()) {
+                    Selectable col = (Selectable) it.next();
+                    orderByBuffer.append(col.getText()).append(" 
asc").append(", ");
+                }
+            }
+            else {
+                StringTokenizer st = new StringTokenizer(hqlOrderBy, " ,", 
false);
+                String currentOrdering = defaultOrder;
+                //FIXME make this code decent
+                while (st.hasMoreTokens()) {
+                    String token = st.nextToken();
+                    if (isNonPropertyToken(token)) {
+                        if (currentOrdering != null) {
+                            throw new DatastoreConfigurationException(
+                                    "Error while parsing sort clause: " + 
hqlOrderBy
+                                            + " (" + role + ")"
+                            );
+                        }
+                        currentOrdering = token;
+                    }
+                    else {
+                        //Add ordering of the previous
+                        if (currentOrdering == null) {
+                            //default ordering
+                            ordering.add("asc");
+                        }
+                        else {
+                            ordering.add(currentOrdering);
+                            currentOrdering = null;
+                        }
+                        properties.add(token);
+                    }
+                }
+                ordering.remove(0); //first one is the algorithm starter
+                // add last one ordering
+                if (currentOrdering == null) {
+                    //default ordering
+                    ordering.add(defaultOrder);
+                }
+                else {
+                    ordering.add(currentOrdering);
+                    currentOrdering = null;
+                }
+                int index = 0;
+
+                for (String property : properties) {
+                    Property p = 
BinderHelper.findPropertyByName(associatedClass, property);
+                    if (p == null) {
+                        throw new DatastoreConfigurationException(
+                                "property from sort clause not found: "
+                                        + associatedClass.getEntityName() + 
"." + property
+                        );
+                    }
+                    PersistentClass pc = p.getPersistentClass();
+                    String table;
+                    if (pc == null) {
+                        table = "";
+                    }
+
+                    else if (pc == associatedClass
+                            || (associatedClass instanceof SingleTableSubclass 
&&
+                            
pc.getMappedClass().isAssignableFrom(associatedClass.getMappedClass()))) {
+                        table = "";
+                    } else {
+                        table = pc.getTable().getQuotedName() + ".";
+                    }
+
+                    Iterator<?> propertyColumns = 
p.getSelectables().iterator();
+                    while (propertyColumns.hasNext()) {
+                        Selectable column = (Selectable) 
propertyColumns.next();
+                        orderByBuffer.append(table)
+                                .append(column.getText())
+                                .append(" ")
+                                .append(ordering.get(index))
+                                .append(", ");
+                    }
+                    index++;
+                }
+            }
+            orderByString = orderByBuffer.substring(0, orderByBuffer.length() 
- 2);
+        }
+        return orderByString;
+    }
+
+    private boolean isNonPropertyToken(String token) {
+        if (" ".equals(token)) return true;
+        if (",".equals(token)) return true;
+        if (token.equalsIgnoreCase("desc")) return true;
+        if (token.equalsIgnoreCase("asc")) return true;
+        return false;
+    }
+}
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
index e35baa073b..fb414a8f56 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/secondpass/CollectionSecondPassBinder.java
@@ -16,6 +16,7 @@ import 
org.grails.orm.hibernate.cfg.domainbinding.CompositeIdentifierToManyToOne
 import org.grails.orm.hibernate.cfg.domainbinding.DefaultColumnNameFetcher;
 import org.grails.orm.hibernate.cfg.domainbinding.EnumTypeBinder;
 import org.grails.orm.hibernate.cfg.domainbinding.ManyToOneBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.OrderByClauseBuilder;
 import org.grails.orm.hibernate.cfg.domainbinding.RootMappingFetcher;
 import org.grails.orm.hibernate.cfg.domainbinding.SimpleValueBinder;
 import org.grails.orm.hibernate.cfg.domainbinding.SimpleValueColumnBinder;
@@ -47,11 +48,13 @@ public class CollectionSecondPassBinder {
     private final MetadataBuildingContext metadataBuildingContext;
     private final PersistentEntityNamingStrategy namingStrategy;
     private final DefaultColumnNameFetcher defaultColumnNameFetcher;
+    private final OrderByClauseBuilder orderByClauseBuilder;
 
     public CollectionSecondPassBinder(MetadataBuildingContext 
metadataBuildingContext, PersistentEntityNamingStrategy namingStrategy) {
         this.metadataBuildingContext = metadataBuildingContext;
         this.namingStrategy = namingStrategy;
         this.defaultColumnNameFetcher = new 
DefaultColumnNameFetcher(namingStrategy);
+        this.orderByClauseBuilder = new OrderByClauseBuilder();
     }
 
 
@@ -80,7 +83,7 @@ public class CollectionSecondPassBinder {
 
                 associatedClass = (PersistentClass) 
persistentClasses.get(associatedClassName);
                 if (associatedClass != null) {
-                    
collection.setOrderBy(buildOrderByClause(propertyToSortBy.getName(), 
associatedClass, collection.getRole(),
+                    
collection.setOrderBy(orderByClauseBuilder.buildOrderByClause(propertyToSortBy.getName(),
 associatedClass, collection.getRole(),
                             propConfig.getOrder() != null ? 
propConfig.getOrder() : "asc"));
                 }
             }
@@ -472,107 +475,4 @@ public class CollectionSecondPassBinder {
         }
         return theSet;
     }
-
-    @SuppressWarnings("unchecked")
-    private String buildOrderByClause(String hqlOrderBy, PersistentClass 
associatedClass, String role, String defaultOrder) {
-        String orderByString = null;
-        if (hqlOrderBy != null) {
-            java.util.List<String> properties = new java.util.ArrayList<>();
-            java.util.List<String> ordering = new java.util.ArrayList<>();
-            StringBuilder orderByBuffer = new StringBuilder();
-            if (hqlOrderBy.length() == 0) {
-                //order by id
-                Iterator<?> it = 
associatedClass.getIdentifier().getSelectables().iterator();
-                while (it.hasNext()) {
-                    Selectable col = (Selectable) it.next();
-                    orderByBuffer.append(col.getText()).append(" 
asc").append(", ");
-                }
-            }
-            else {
-                StringTokenizer st = new StringTokenizer(hqlOrderBy, " ,", 
false);
-                String currentOrdering = defaultOrder;
-                //FIXME make this code decent
-                while (st.hasMoreTokens()) {
-                    String token = st.nextToken();
-                    if (isNonPropertyToken(token)) {
-                        if (currentOrdering != null) {
-                            throw new DatastoreConfigurationException(
-                                    "Error while parsing sort clause: " + 
hqlOrderBy
-                                            + " (" + role + ")"
-                            );
-                        }
-                        currentOrdering = token;
-                    }
-                    else {
-                        //Add ordering of the previous
-                        if (currentOrdering == null) {
-                            //default ordering
-                            ordering.add("asc");
-                        }
-                        else {
-                            ordering.add(currentOrdering);
-                            currentOrdering = null;
-                        }
-                        properties.add(token);
-                    }
-                }
-                ordering.remove(0); //first one is the algorithm starter
-                // add last one ordering
-                if (currentOrdering == null) {
-                    //default ordering
-                    ordering.add(defaultOrder);
-                }
-                else {
-                    ordering.add(currentOrdering);
-                    currentOrdering = null;
-                }
-                int index = 0;
-
-                for (String property : properties) {
-                    Property p = 
BinderHelper.findPropertyByName(associatedClass, property);
-                    if (p == null) {
-                        throw new DatastoreConfigurationException(
-                                "property from sort clause not found: "
-                                        + associatedClass.getEntityName() + 
"." + property
-                        );
-                    }
-                    PersistentClass pc = p.getPersistentClass();
-                    String table;
-                    if (pc == null) {
-                        table = "";
-                    }
-
-                    else if (pc == associatedClass
-                            || (associatedClass instanceof SingleTableSubclass 
&&
-                            
pc.getMappedClass().isAssignableFrom(associatedClass.getMappedClass()))) {
-                        table = "";
-                    } else {
-                        table = pc.getTable().getQuotedName() + ".";
-                    }
-
-                    Iterator<?> propertyColumns = 
p.getSelectables().iterator();
-                    while (propertyColumns.hasNext()) {
-                        Selectable column = (Selectable) 
propertyColumns.next();
-                        orderByBuffer.append(table)
-                                .append(column.getText())
-                                .append(" ")
-                                .append(ordering.get(index))
-                                .append(", ");
-                    }
-                    index++;
-                }
-            }
-            orderByString = orderByBuffer.substring(0, orderByBuffer.length() 
- 2);
-        }
-        return orderByString;
-    }
-
-    private boolean isNonPropertyToken(String token) {
-        if (" ".equals(token)) return true;
-        if (",".equals(token)) return true;
-        if (token.equalsIgnoreCase("desc")) return true;
-        if (token.equalsIgnoreCase("asc")) return true;
-        return false;
-    }
-
 }
diff --git 
a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/OrderByClauseBuilderSpec.groovy
 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/OrderByClauseBuilderSpec.groovy
new file mode 100644
index 0000000000..9a0200fd76
--- /dev/null
+++ 
b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/OrderByClauseBuilderSpec.groovy
@@ -0,0 +1,103 @@
+package org.grails.orm.hibernate.cfg.domainbinding
+
+import grails.gorm.annotation.Entity
+import grails.gorm.specs.HibernateGormDatastoreSpec
+import org.hibernate.mapping.PersistentClass
+import spock.lang.Subject
+
+class OrderByClauseBuilderSpec extends HibernateGormDatastoreSpec {
+
+    @Subject
+    OrderByClauseBuilder builder = new OrderByClauseBuilder()
+
+    void setupSpec() {
+        manager.addAllDomainClasses([OrderTest, SubOrderTest])
+    }
+
+    void "test buildOrderByClause with empty string (default to id)"() {
+        given:
+        PersistentClass pc = 
datastore.metadata.getEntityBinding(OrderTest.name)
+
+        when:
+        String result = builder.buildOrderByClause("", pc, "role", "asc")
+
+        then:
+        result == "id asc"
+    }
+
+    void "test buildOrderByClause with single property"() {
+        given:
+        PersistentClass pc = 
datastore.metadata.getEntityBinding(OrderTest.name)
+
+        when:
+        String result = builder.buildOrderByClause("name", pc, "role", "asc")
+
+        then:
+        result == "name asc"
+    }
+
+    void "test buildOrderByClause with property and explicit order"() {
+        given:
+        PersistentClass pc = 
datastore.metadata.getEntityBinding(OrderTest.name)
+
+        when:
+        String result = builder.buildOrderByClause("name desc", pc, "role", 
"asc")
+
+        then:
+        result == "name desc"
+    }
+
+    void "test buildOrderByClause with multiple properties"() {
+        given:
+        PersistentClass pc = 
datastore.metadata.getEntityBinding(OrderTest.name)
+
+        when:
+        String result = builder.buildOrderByClause("name, age desc", pc, 
"role", "asc")
+
+        then:
+        result == "name asc, age desc"
+    }
+
+    void "test buildOrderByClause with mapped column name"() {
+        given:
+        PersistentClass pc = 
datastore.metadata.getEntityBinding(OrderTest.name)
+
+        when:
+        String result = builder.buildOrderByClause("other", pc, "role", "asc")
+
+        then:
+        result == "other_column asc"
+    }
+
+    void "test buildOrderByClause with table prefix for inherited property"() {
+        given:
+        PersistentClass pc = 
datastore.metadata.getEntityBinding(SubOrderTest.name)
+
+        when:
+        // 'name' is in the base table 'order_test', 'subProperty' is in 
'sub_order_test'
+        String result = builder.buildOrderByClause("name, subProperty", pc, 
"role", "asc")
+
+        then:
+        // Hibernate 7 mapping might use different table qualification 
depending on strategy.
+        // Assuming joined-subclass or TPH.
+        // If TPH (default GORM), table prefix might be empty if it's the same 
table.
+        result.contains("name asc")
+        result.contains("sub_property asc")
+    }
+}
+
+@Entity
+class OrderTest {
+    String name
+    Integer age
+    String other
+
+    static mapping = {
+        other column: 'other_column'
+    }
+}
+
+@Entity
+class SubOrderTest extends OrderTest {
+    String subProperty
+}

Reply via email to