matrei commented on code in PR #15568:
URL: https://github.com/apache/grails-core/pull/15568#discussion_r3466326516


##########
grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java:
##########
@@ -68,226 +75,1274 @@
  *             }
  *             maxResults(10)
  *             order("holderLastName", "desc")
+ *             cache(true)
+ *             readOnly(true)
  *         }
  * </pre>
- * <p>The builder can also be instantiated standalone with a SessionFactory 
and persistent Class instance:
+ *
+ * <h2>Advanced Features</h2>
+ *
+ * <p>The builder supports several advanced Hibernate features:
+ *
+ * <ul>
+ *   <li><b>Pessimistic Locking:</b> Use {@code lock(true)} to obtain a 
pessimistic write lock.
+ *   <li><b>Query Caching:</b> Use {@code cache(true)} to enable query caching 
for the results.
+ *   <li><b>Read-Only Mode:</b> Use {@code readOnly(true)} to disable dirty 
checking for loaded
+ *       entities.
+ *   <li><b>Fetch Mode:</b> Use {@code fetchMode("association", 
FetchMode.JOIN)} to specify Eager/Lazy
+ *       fetching strategies.
+ * </ul>
+ *
+ * <h2>Programmatic instantiation</h2>
+ *
+ * <p>The builder requires a {@link SessionFactory}, the target persistent 
class, and the {@link
+ * org.grails.orm.hibernate.HibernateDatastore} that owns the session:
+ *
  * <pre>
- *      new HibernateCriteriaBuilder(clazz, sessionFactory).list {
+ *      new HibernateCriteriaBuilder(Account, sessionFactory, datastore).list {
  *         eq("firstName", "Fred")
  *      }
  * </pre>
  *
+ * <h2>Architecture</h2>
+ *
+ * <p>Closure method calls in the DSL are dispatched through {@code 
invokeMethod} → {@code
+ * CriteriaMethodInvoker} → {@link HibernateQuery}, which translates each GORM 
constraint into the
+ * equivalent JPA Criteria predicate. {@link grails.gorm.DetachedCriteria} can 
also be passed in
+ * place of a closure to support multi-tenant and reusable query fragments.
+ *
+ * To adjust the methods to be handled you have to extend this class, extend 
CriteriaMethodInvoker

Review Comment:
   This sentence does not make sense to me.



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassBinder.java:
##########
@@ -0,0 +1,79 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;

Review Comment:
   Align on JSpecify?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassBinder.java:
##########
@@ -0,0 +1,79 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.boot.spi.InFlightMetadataCollector;
+import org.hibernate.mapping.PersistentClass;
+
+import org.grails.orm.hibernate.cfg.Mapping;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity;
+
+import static org.grails.orm.hibernate.cfg.GrailsHibernateUtil.unqualify;
+
+/** The class binder class. */
+public class ClassBinder {
+
+    private final InFlightMetadataCollector collector;
+
+    public ClassBinder(@Nonnull InFlightMetadataCollector collector) {
+        this.collector = collector;
+    }
+
+    /**
+     * Binds the specified persistant class to the runtime model based on the 
properties defined in
+     * the domain class
+     *
+     * @param persistentEntity The Grails domain class
+     * @param persistentClass The persistant class
+     */

Review Comment:
   I'm not a native english speaker, but shouldn't `persistant` be `persistent`?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionForPropertyConfigBinder.java:
##########
@@ -0,0 +1,38 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import java.util.Optional;
+
+import jakarta.annotation.Nonnull;

Review Comment:
   Align on JSpecify?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ColumnBinder.java:
##########
@@ -0,0 +1,145 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Table;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.grails.orm.hibernate.cfg.ColumnConfig;
+import org.grails.orm.hibernate.cfg.Mapping;
+import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateAssociation;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.ColumnNameForPropertyAndPathFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.CreateKeyForProps;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
+
+@SuppressWarnings({"PMD.NullAssignment", "PMD.DataflowAnomalyAnalysis"})
+public class ColumnBinder {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ColumnBinder.class);
+
+    private final ColumnNameForPropertyAndPathFetcher 
columnNameForPropertyAndPathFetcher;
+    private final StringColumnConstraintsBinder stringColumnConstraintsBinder;
+    private final NumericColumnConstraintsBinder 
numericColumnConstraintsBinder;
+    private final CreateKeyForProps createKeyForProps;
+    private final IndexBinder indexBinder;
+
+    /** Public constructor that accepts all collaborators. */
+    public ColumnBinder(
+            ColumnNameForPropertyAndPathFetcher 
columnNameForPropertyAndPathFetcher,
+            StringColumnConstraintsBinder stringColumnConstraintsBinder,
+            NumericColumnConstraintsBinder numericColumnConstraintsBinder,
+            CreateKeyForProps createKeyForProps,
+            IndexBinder indexBinder) {
+        this.columnNameForPropertyAndPathFetcher = 
columnNameForPropertyAndPathFetcher;
+        this.stringColumnConstraintsBinder = stringColumnConstraintsBinder;
+        this.numericColumnConstraintsBinder = numericColumnConstraintsBinder;
+        this.createKeyForProps = createKeyForProps;
+        this.indexBinder = indexBinder;
+    }
+
+    /** Convenience constructor for backward compatibility. */
+    public ColumnBinder(PersistentEntityNamingStrategy namingStrategy) {
+        this(
+                new ColumnNameForPropertyAndPathFetcher(
+                        namingStrategy, new 
DefaultColumnNameFetcher(namingStrategy), new BackticksRemover()),
+                new StringColumnConstraintsBinder(),
+                new NumericColumnConstraintsBinder(),
+                new CreateKeyForProps(new ColumnNameForPropertyAndPathFetcher(
+                        namingStrategy, new 
DefaultColumnNameFetcher(namingStrategy), new BackticksRemover())),
+                new IndexBinder());
+    }
+
+    /**
+     * Binds a Column instance to the Hibernate meta model
+     *
+     * @param property The Grails domain class property
+     * @param parentProperty parent property
+     * @param column The column to bind
+     * @param path the path
+     * @param table The table name
+     */
+    public void bindColumn(
+            HibernatePersistentProperty property,
+            HibernatePersistentProperty parentProperty,
+            Column column,
+            ColumnConfig cc,
+            String path,
+            Table table) {
+
+        if (cc != null) {
+            column.setComment(cc.getComment());
+            column.setDefaultValue(cc.getDefaultValue());
+            column.setCustomRead(cc.getRead());
+            column.setCustomWrite(cc.getWrite());
+        }
+
+        Class<?> userType = property.getUserType();
+        String columnName = 
columnNameForPropertyAndPathFetcher.getColumnNameForPropertyAndPath(property, 
path, cc);
+        if ((property instanceof HibernateAssociation assoc) && userType == 
null) {
+            // Only use conventional naming when the column has not been 
explicitly mapped.
+            if (column.getName() == null) {
+                column.setName(columnName);
+            }
+            column.setNullable(assoc.isAssociationColumnNullable());
+        } else {
+            column.setName(columnName);
+            column.setNullable(property.isNullable() || (parentProperty != 
null && parentProperty.isNullable()));
+            // Use the constraints for this property to more accurately define
+            // the column's length, precision, and scale
+            Class<?> type = property.getType();
+            if (type != null && (String.class.isAssignableFrom(type) || 
byte[].class.isAssignableFrom(type))) {
+                PropertyConfig mappedForm = property.getHibernateMappedForm();
+                
stringColumnConstraintsBinder.bindStringColumnConstraints(column, mappedForm);
+            } else if (type != null && Number.class.isAssignableFrom(type)) {
+                PropertyConfig mappedForm = property.getHibernateMappedForm();
+                
numericColumnConstraintsBinder.bindNumericColumnConstraints(column, cc, 
mappedForm);
+            }
+        }
+
+        createKeyForProps.createKeyForProps(property, path, table, columnName);
+        indexBinder.bindIndex(columnName, column, cc, table);
+
+        var owner = property.getHibernateOwner();
+        if (!owner.isRoot()) {
+            Mapping mapping = owner.getHibernateMappedForm();
+            if (mapping != null && mapping.getTablePerHierarchy()) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("[GrailsDomainBinder] Sub class property [{}] 
for column name [{}] set to nullable", property.getName(), column.getName());

Review Comment:
   Why are we logging the class name `GrailsDomainBinder` here?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java:
##########
@@ -0,0 +1,179 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import org.hibernate.boot.spi.InFlightMetadataCollector;
+import org.hibernate.boot.spi.MetadataBuildingContext;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.OneToMany;
+
+import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
+import 
org.grails.orm.hibernate.cfg.domainbinding.collectionType.CollectionHolder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyEntityProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BasicCollectionElementBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalMapElementBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalOneToManyLinker;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyColumnUpdater;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionWithJoinTableBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.DependentKeyValueBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.HibernateToManyEntityOrderByBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.ListSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ListSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ManyToOneElementBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.MapSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.MapSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.PrimaryKeyValueCreator;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.SetSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ToManyEntityMultiTenantFilterBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.UnidirectionalOneToManyBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.UnidirectionalOneToManyInverseValuesBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.GrailsPropertyResolver;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.SimpleValueColumnFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.TableForManyCalculator;
+
+
+/** Handles the binding of collections to the Hibernate runtime meta model. */
+@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
+public class CollectionBinder {
+
+    private final MetadataBuildingContext metadataBuildingContext;
+    private final CollectionHolder collectionHolder;
+    private final ListSecondPassBinder listSecondPassBinder;
+    private final CollectionSecondPassBinder collectionSecondPassBinder;
+    final MapSecondPassBinder mapSecondPassBinder;
+    private final InFlightMetadataCollector mappings;
+    private final TableForManyCalculator tableForManyCalculator;
+
+    public void setComponentBinder(ComponentBinder componentBinder) {
+        this.collectionSecondPassBinder.setComponentBinder(componentBinder);
+    }
+
+    /** Creates a new {@link CollectionBinder} instance. */

Review Comment:
   Do we need this description on a constructor?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ComponentBinder.java:
##########
@@ -0,0 +1,121 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.boot.spi.MetadataBuildingContext;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.Component;
+import org.hibernate.mapping.PersistentClass;
+
+import org.grails.orm.hibernate.cfg.GrailsHibernateUtil;
+import org.grails.orm.hibernate.cfg.MappingCacheHolder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedCollectionProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+
+// TODO (Hibernate 8 refactor): ComponentBinder holds a GrailsPropertyBinder 
reference set post-construction
+// via setGrailsPropertyBinder() to break a circular dependency 
(ComponentBinder ↔ GrailsPropertyBinder ↔
+// CollectionBinder ↔ ComponentBinder). This mutual dependency should be 
resolved by introducing a shared
+// binding context or factory object that all binders receive at construction 
time.
+@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
+public class ComponentBinder {

Review Comment:
   `@since`?



##########
grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java:
##########
@@ -68,226 +75,1274 @@
  *             }
  *             maxResults(10)
  *             order("holderLastName", "desc")
+ *             cache(true)
+ *             readOnly(true)
  *         }
  * </pre>
- * <p>The builder can also be instantiated standalone with a SessionFactory 
and persistent Class instance:
+ *
+ * <h2>Advanced Features</h2>
+ *
+ * <p>The builder supports several advanced Hibernate features:
+ *
+ * <ul>
+ *   <li><b>Pessimistic Locking:</b> Use {@code lock(true)} to obtain a 
pessimistic write lock.
+ *   <li><b>Query Caching:</b> Use {@code cache(true)} to enable query caching 
for the results.
+ *   <li><b>Read-Only Mode:</b> Use {@code readOnly(true)} to disable dirty 
checking for loaded
+ *       entities.
+ *   <li><b>Fetch Mode:</b> Use {@code fetchMode("association", 
FetchMode.JOIN)} to specify Eager/Lazy
+ *       fetching strategies.
+ * </ul>
+ *
+ * <h2>Programmatic instantiation</h2>
+ *
+ * <p>The builder requires a {@link SessionFactory}, the target persistent 
class, and the {@link
+ * org.grails.orm.hibernate.HibernateDatastore} that owns the session:
+ *
  * <pre>
- *      new HibernateCriteriaBuilder(clazz, sessionFactory).list {
+ *      new HibernateCriteriaBuilder(Account, sessionFactory, datastore).list {
  *         eq("firstName", "Fred")
  *      }
  * </pre>
  *
+ * <h2>Architecture</h2>
+ *
+ * <p>Closure method calls in the DSL are dispatched through {@code 
invokeMethod} → {@code
+ * CriteriaMethodInvoker} → {@link HibernateQuery}, which translates each GORM 
constraint into the
+ * equivalent JPA Criteria predicate. {@link grails.gorm.DetachedCriteria} can 
also be passed in
+ * place of a closure to support multi-tenant and reusable query fragments.
+ *
+ * To adjust the methods to be handled you have to extend this class, extend 
CriteriaMethodInvoker
+ *
  * @author Graeme Rocher
+ * @author walterduquedeestrada
+ * @see HibernateQuery
+ * @see grails.gorm.DetachedCriteria
  */
-public class HibernateCriteriaBuilder extends AbstractHibernateCriteriaBuilder 
{
-    /*
-     * Define constants which may be used inside of criteria queries
-     * to refer to standard Hibernate Type instances.
-     */
-    public static final Type BOOLEAN = StandardBasicTypes.BOOLEAN;
-    public static final Type YES_NO = StandardBasicTypes.YES_NO;
-    public static final Type BYTE = StandardBasicTypes.BYTE;
-    public static final Type CHARACTER = StandardBasicTypes.CHARACTER;
-    public static final Type SHORT = StandardBasicTypes.SHORT;
-    public static final Type INTEGER = StandardBasicTypes.INTEGER;
-    public static final Type LONG = StandardBasicTypes.LONG;
-    public static final Type FLOAT = StandardBasicTypes.FLOAT;
-    public static final Type DOUBLE = StandardBasicTypes.DOUBLE;
-    public static final Type BIG_DECIMAL = StandardBasicTypes.BIG_DECIMAL;
-    public static final Type BIG_INTEGER = StandardBasicTypes.BIG_INTEGER;
-    public static final Type STRING = StandardBasicTypes.STRING;
-    public static final Type NUMERIC_BOOLEAN = 
StandardBasicTypes.NUMERIC_BOOLEAN;
-    public static final Type TRUE_FALSE = StandardBasicTypes.TRUE_FALSE;
-    public static final Type URL = StandardBasicTypes.URL;
-    public static final Type TIME = StandardBasicTypes.TIME;
-    public static final Type DATE = StandardBasicTypes.DATE;
-    public static final Type TIMESTAMP = StandardBasicTypes.TIMESTAMP;
-    public static final Type CALENDAR = StandardBasicTypes.CALENDAR;
-    public static final Type CALENDAR_DATE = StandardBasicTypes.CALENDAR_DATE;
-    public static final Type CLASS = StandardBasicTypes.CLASS;
-    public static final Type LOCALE = StandardBasicTypes.LOCALE;
-    public static final Type CURRENCY = StandardBasicTypes.CURRENCY;
-    public static final Type TIMEZONE = StandardBasicTypes.TIMEZONE;
-    public static final Type UUID_BINARY = StandardBasicTypes.UUID_BINARY;
-    public static final Type UUID_CHAR = StandardBasicTypes.UUID_CHAR;
-    public static final Type BINARY = StandardBasicTypes.BINARY;
-    public static final Type WRAPPER_BINARY = 
StandardBasicTypes.WRAPPER_BINARY;
-    public static final Type IMAGE = StandardBasicTypes.IMAGE;
-    public static final Type BLOB = StandardBasicTypes.BLOB;
-    public static final Type MATERIALIZED_BLOB = 
StandardBasicTypes.MATERIALIZED_BLOB;
-    public static final Type CHAR_ARRAY = StandardBasicTypes.CHAR_ARRAY;
-    public static final Type CHARACTER_ARRAY = 
StandardBasicTypes.CHARACTER_ARRAY;
-    public static final Type TEXT = StandardBasicTypes.TEXT;
-    public static final Type CLOB = StandardBasicTypes.CLOB;
-    public static final Type MATERIALIZED_CLOB = 
StandardBasicTypes.MATERIALIZED_CLOB;
-    public static final Type SERIALIZABLE = StandardBasicTypes.SERIALIZABLE;
+@Slf4j
+@SuppressWarnings("PMD.AvoidDuplicateLiterals")
+public class HibernateCriteriaBuilder extends GroovyObjectSupport implements 
BuildableCriteria, ProjectionList {
+    private final SessionFactory sessionFactory;
+    private final boolean participate;
+    private final org.hibernate.query.criteria.HibernateCriteriaBuilder cb;
+    private final HibernateQuery hibernateQuery;
+    private Class<?> targetClass;
+    private CriteriaQuery<?> criteriaQuery;
+    private boolean uniqueResult = false;
 
-    @SuppressWarnings("rawtypes")
-    public HibernateCriteriaBuilder(Class targetClass, SessionFactory 
sessionFactory) {
-        super(targetClass, sessionFactory);
+    @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
+    private boolean scroll;
+
+    @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
+    private boolean count;
+
+    private boolean paginationEnabledList = false;
+    private int defaultFlushMode;
+
+    @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
+    private boolean distinct = false;
+    private CriteriaMethodInvoker criteriaMethodInvoker;
+
+    @SuppressWarnings({"rawtypes", "PMD.CloseResource"})
+    public HibernateCriteriaBuilder(Class targetClass, SessionFactory 
sessionFactory, HibernateDatastore datastore) {
+        this.targetClass = targetClass;
+        setDatastore(datastore);
+        this.sessionFactory = sessionFactory;
+        this.cb = sessionFactory.getCriteriaBuilder();
+        if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
+            this.participate = true;
+        } else {
+            this.participate = false;
+            org.hibernate.Session session = sessionFactory.openSession();

Review Comment:
   Why fully qualified, or event better: `var`?
   



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java:
##########
@@ -0,0 +1,81 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;

Review Comment:
   Align on JSpecify?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java:
##########
@@ -0,0 +1,81 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.MappingException;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.Value;
+
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentEntity;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.PropertyFromValueCreator;
+
+/**
+ * Binds the properties of a Grails domain class to the Hibernate meta-model.
+ *
+ * @author Graeme Rocher
+ * @since 7.0
+ */
+@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
+public class ClassPropertiesBinder {
+
+    private final GrailsPropertyBinder grailsPropertyBinder;
+    private final PropertyFromValueCreator propertyFromValueCreator;
+    private final NaturalIdentifierBinder naturalIdentifierBinder;
+
+    /** Creates a new {@link ClassPropertiesBinder} instance. */

Review Comment:
   Do we need this description on a constructor?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java:
##########
@@ -0,0 +1,81 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.MappingException;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.Value;
+
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentEntity;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.PropertyFromValueCreator;
+
+/**
+ * Binds the properties of a Grails domain class to the Hibernate meta-model.
+ *
+ * @author Graeme Rocher
+ * @since 7.0
+ */
+@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
+public class ClassPropertiesBinder {
+
+    private final GrailsPropertyBinder grailsPropertyBinder;
+    private final PropertyFromValueCreator propertyFromValueCreator;
+    private final NaturalIdentifierBinder naturalIdentifierBinder;
+
+    /** Creates a new {@link ClassPropertiesBinder} instance. */
+    public ClassPropertiesBinder(
+            GrailsPropertyBinder grailsPropertyBinder,
+            PropertyFromValueCreator propertyFromValueCreator,
+            NaturalIdentifierBinder naturalIdentifierBinder) {
+        this.grailsPropertyBinder = grailsPropertyBinder;
+        this.propertyFromValueCreator = propertyFromValueCreator;
+        this.naturalIdentifierBinder = naturalIdentifierBinder;
+    }
+
+    /** Creates a new {@link ClassPropertiesBinder} instance. */

Review Comment:
   Do we need this description on a constructor?



##########
grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java:
##########
@@ -68,226 +75,1274 @@
  *             }
  *             maxResults(10)
  *             order("holderLastName", "desc")
+ *             cache(true)
+ *             readOnly(true)
  *         }
  * </pre>
- * <p>The builder can also be instantiated standalone with a SessionFactory 
and persistent Class instance:
+ *
+ * <h2>Advanced Features</h2>
+ *
+ * <p>The builder supports several advanced Hibernate features:
+ *
+ * <ul>
+ *   <li><b>Pessimistic Locking:</b> Use {@code lock(true)} to obtain a 
pessimistic write lock.
+ *   <li><b>Query Caching:</b> Use {@code cache(true)} to enable query caching 
for the results.
+ *   <li><b>Read-Only Mode:</b> Use {@code readOnly(true)} to disable dirty 
checking for loaded
+ *       entities.
+ *   <li><b>Fetch Mode:</b> Use {@code fetchMode("association", 
FetchMode.JOIN)} to specify Eager/Lazy
+ *       fetching strategies.
+ * </ul>
+ *
+ * <h2>Programmatic instantiation</h2>
+ *
+ * <p>The builder requires a {@link SessionFactory}, the target persistent 
class, and the {@link
+ * org.grails.orm.hibernate.HibernateDatastore} that owns the session:
+ *
  * <pre>
- *      new HibernateCriteriaBuilder(clazz, sessionFactory).list {
+ *      new HibernateCriteriaBuilder(Account, sessionFactory, datastore).list {
  *         eq("firstName", "Fred")
  *      }
  * </pre>
  *
+ * <h2>Architecture</h2>
+ *
+ * <p>Closure method calls in the DSL are dispatched through {@code 
invokeMethod} → {@code
+ * CriteriaMethodInvoker} → {@link HibernateQuery}, which translates each GORM 
constraint into the
+ * equivalent JPA Criteria predicate. {@link grails.gorm.DetachedCriteria} can 
also be passed in
+ * place of a closure to support multi-tenant and reusable query fragments.
+ *
+ * To adjust the methods to be handled you have to extend this class, extend 
CriteriaMethodInvoker
+ *
  * @author Graeme Rocher
+ * @author walterduquedeestrada

Review Comment:
   Do not add `@author` tags?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassPropertiesBinder.java:
##########
@@ -0,0 +1,81 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.MappingException;
+import org.hibernate.mapping.PersistentClass;
+import org.hibernate.mapping.Table;
+import org.hibernate.mapping.Value;
+
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentEntity;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.PropertyFromValueCreator;
+
+/**
+ * Binds the properties of a Grails domain class to the Hibernate meta-model.
+ *
+ * @author Graeme Rocher
+ * @since 7.0

Review Comment:
   Is this correct?



##########
grails-data-hibernate7/core/src/main/groovy/grails/orm/HibernateCriteriaBuilder.java:
##########
@@ -68,226 +75,1274 @@
  *             }
  *             maxResults(10)
  *             order("holderLastName", "desc")
+ *             cache(true)
+ *             readOnly(true)
  *         }
  * </pre>
- * <p>The builder can also be instantiated standalone with a SessionFactory 
and persistent Class instance:
+ *
+ * <h2>Advanced Features</h2>
+ *
+ * <p>The builder supports several advanced Hibernate features:
+ *
+ * <ul>
+ *   <li><b>Pessimistic Locking:</b> Use {@code lock(true)} to obtain a 
pessimistic write lock.
+ *   <li><b>Query Caching:</b> Use {@code cache(true)} to enable query caching 
for the results.
+ *   <li><b>Read-Only Mode:</b> Use {@code readOnly(true)} to disable dirty 
checking for loaded
+ *       entities.
+ *   <li><b>Fetch Mode:</b> Use {@code fetchMode("association", 
FetchMode.JOIN)} to specify Eager/Lazy
+ *       fetching strategies.
+ * </ul>
+ *
+ * <h2>Programmatic instantiation</h2>
+ *
+ * <p>The builder requires a {@link SessionFactory}, the target persistent 
class, and the {@link
+ * org.grails.orm.hibernate.HibernateDatastore} that owns the session:
+ *
  * <pre>
- *      new HibernateCriteriaBuilder(clazz, sessionFactory).list {
+ *      new HibernateCriteriaBuilder(Account, sessionFactory, datastore).list {
  *         eq("firstName", "Fred")
  *      }
  * </pre>
  *
+ * <h2>Architecture</h2>
+ *
+ * <p>Closure method calls in the DSL are dispatched through {@code 
invokeMethod} → {@code
+ * CriteriaMethodInvoker} → {@link HibernateQuery}, which translates each GORM 
constraint into the
+ * equivalent JPA Criteria predicate. {@link grails.gorm.DetachedCriteria} can 
also be passed in
+ * place of a closure to support multi-tenant and reusable query fragments.
+ *
+ * To adjust the methods to be handled you have to extend this class, extend 
CriteriaMethodInvoker
+ *
  * @author Graeme Rocher
+ * @author walterduquedeestrada
+ * @see HibernateQuery
+ * @see grails.gorm.DetachedCriteria
  */
-public class HibernateCriteriaBuilder extends AbstractHibernateCriteriaBuilder 
{
-    /*
-     * Define constants which may be used inside of criteria queries
-     * to refer to standard Hibernate Type instances.
-     */
-    public static final Type BOOLEAN = StandardBasicTypes.BOOLEAN;
-    public static final Type YES_NO = StandardBasicTypes.YES_NO;
-    public static final Type BYTE = StandardBasicTypes.BYTE;
-    public static final Type CHARACTER = StandardBasicTypes.CHARACTER;
-    public static final Type SHORT = StandardBasicTypes.SHORT;
-    public static final Type INTEGER = StandardBasicTypes.INTEGER;
-    public static final Type LONG = StandardBasicTypes.LONG;
-    public static final Type FLOAT = StandardBasicTypes.FLOAT;
-    public static final Type DOUBLE = StandardBasicTypes.DOUBLE;
-    public static final Type BIG_DECIMAL = StandardBasicTypes.BIG_DECIMAL;
-    public static final Type BIG_INTEGER = StandardBasicTypes.BIG_INTEGER;
-    public static final Type STRING = StandardBasicTypes.STRING;
-    public static final Type NUMERIC_BOOLEAN = 
StandardBasicTypes.NUMERIC_BOOLEAN;
-    public static final Type TRUE_FALSE = StandardBasicTypes.TRUE_FALSE;
-    public static final Type URL = StandardBasicTypes.URL;
-    public static final Type TIME = StandardBasicTypes.TIME;
-    public static final Type DATE = StandardBasicTypes.DATE;
-    public static final Type TIMESTAMP = StandardBasicTypes.TIMESTAMP;
-    public static final Type CALENDAR = StandardBasicTypes.CALENDAR;
-    public static final Type CALENDAR_DATE = StandardBasicTypes.CALENDAR_DATE;
-    public static final Type CLASS = StandardBasicTypes.CLASS;
-    public static final Type LOCALE = StandardBasicTypes.LOCALE;
-    public static final Type CURRENCY = StandardBasicTypes.CURRENCY;
-    public static final Type TIMEZONE = StandardBasicTypes.TIMEZONE;
-    public static final Type UUID_BINARY = StandardBasicTypes.UUID_BINARY;
-    public static final Type UUID_CHAR = StandardBasicTypes.UUID_CHAR;
-    public static final Type BINARY = StandardBasicTypes.BINARY;
-    public static final Type WRAPPER_BINARY = 
StandardBasicTypes.WRAPPER_BINARY;
-    public static final Type IMAGE = StandardBasicTypes.IMAGE;
-    public static final Type BLOB = StandardBasicTypes.BLOB;
-    public static final Type MATERIALIZED_BLOB = 
StandardBasicTypes.MATERIALIZED_BLOB;
-    public static final Type CHAR_ARRAY = StandardBasicTypes.CHAR_ARRAY;
-    public static final Type CHARACTER_ARRAY = 
StandardBasicTypes.CHARACTER_ARRAY;
-    public static final Type TEXT = StandardBasicTypes.TEXT;
-    public static final Type CLOB = StandardBasicTypes.CLOB;
-    public static final Type MATERIALIZED_CLOB = 
StandardBasicTypes.MATERIALIZED_CLOB;
-    public static final Type SERIALIZABLE = StandardBasicTypes.SERIALIZABLE;
+@Slf4j
+@SuppressWarnings("PMD.AvoidDuplicateLiterals")
+public class HibernateCriteriaBuilder extends GroovyObjectSupport implements 
BuildableCriteria, ProjectionList {
+    private final SessionFactory sessionFactory;
+    private final boolean participate;
+    private final org.hibernate.query.criteria.HibernateCriteriaBuilder cb;
+    private final HibernateQuery hibernateQuery;
+    private Class<?> targetClass;
+    private CriteriaQuery<?> criteriaQuery;
+    private boolean uniqueResult = false;
 
-    @SuppressWarnings("rawtypes")
-    public HibernateCriteriaBuilder(Class targetClass, SessionFactory 
sessionFactory) {
-        super(targetClass, sessionFactory);
+    @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
+    private boolean scroll;
+
+    @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
+    private boolean count;
+
+    private boolean paginationEnabledList = false;
+    private int defaultFlushMode;
+
+    @SuppressWarnings("PMD.AvoidFieldNameMatchingMethodName")
+    private boolean distinct = false;
+    private CriteriaMethodInvoker criteriaMethodInvoker;
+
+    @SuppressWarnings({"rawtypes", "PMD.CloseResource"})
+    public HibernateCriteriaBuilder(Class targetClass, SessionFactory 
sessionFactory, HibernateDatastore datastore) {
+        this.targetClass = targetClass;
+        setDatastore(datastore);
+        this.sessionFactory = sessionFactory;
+        this.cb = sessionFactory.getCriteriaBuilder();
+        if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
+            this.participate = true;
+        } else {
+            this.participate = false;
+            org.hibernate.Session session = sessionFactory.openSession();
+            TransactionSynchronizationManager.bindResource(sessionFactory, new 
SessionHolder(session));
+        }
+        HibernateSession session = (HibernateSession) datastore.connect();
+        hibernateQuery = new HibernateQuery(
+                session, (GrailsHibernatePersistentEntity) 
datastore.getMappingContext().getPersistentEntity(targetClass.getName()));
         setDefaultFlushMode(GrailsHibernateTemplate.FLUSH_AUTO);
+        criteriaMethodInvoker = new CriteriaMethodInvoker(this);
     }
 
-    @SuppressWarnings("rawtypes")
-    public HibernateCriteriaBuilder(Class targetClass, SessionFactory 
sessionFactory, boolean uniqueResult) {
-        super(targetClass, sessionFactory, uniqueResult);
-        setDefaultFlushMode(GrailsHibernateTemplate.FLUSH_AUTO);
+    public static final String ALIAS_SEPARATOR = ":";
+
+    private static String getFullyQualifiedColumn(String propertyName, String 
alias) {
+        return (Objects.nonNull(alias) ? alias + ALIAS_SEPARATOR : "") + 
propertyName;
+    }
+
+    public org.grails.datastore.mapping.query.api.Criteria exists(Closure 
subquery) {
+        return exists(new 
grails.gorm.DetachedCriteria(targetClass).build(subquery));

Review Comment:
   - Why fully qualified class names?
   - The issue with fully qualified class names are repeated many times in this 
class. The same comment applies for each occurrence.



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ComponentBinder.java:
##########
@@ -0,0 +1,121 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;

Review Comment:
   Align on JSpecify?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java:
##########
@@ -0,0 +1,179 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import org.hibernate.boot.spi.InFlightMetadataCollector;
+import org.hibernate.boot.spi.MetadataBuildingContext;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.OneToMany;
+
+import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
+import 
org.grails.orm.hibernate.cfg.domainbinding.collectionType.CollectionHolder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyEntityProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BasicCollectionElementBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalMapElementBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalOneToManyLinker;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyColumnUpdater;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionWithJoinTableBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.DependentKeyValueBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.HibernateToManyEntityOrderByBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.ListSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ListSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ManyToOneElementBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.MapSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.MapSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.PrimaryKeyValueCreator;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.SetSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ToManyEntityMultiTenantFilterBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.UnidirectionalOneToManyBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.UnidirectionalOneToManyInverseValuesBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.GrailsPropertyResolver;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.SimpleValueColumnFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.TableForManyCalculator;
+
+
+/** Handles the binding of collections to the Hibernate runtime meta model. */
+@SuppressWarnings("PMD.DataflowAnomalyAnalysis")
+public class CollectionBinder {
+
+    private final MetadataBuildingContext metadataBuildingContext;
+    private final CollectionHolder collectionHolder;
+    private final ListSecondPassBinder listSecondPassBinder;
+    private final CollectionSecondPassBinder collectionSecondPassBinder;
+    final MapSecondPassBinder mapSecondPassBinder;
+    private final InFlightMetadataCollector mappings;
+    private final TableForManyCalculator tableForManyCalculator;
+
+    public void setComponentBinder(ComponentBinder componentBinder) {
+        this.collectionSecondPassBinder.setComponentBinder(componentBinder);
+    }
+
+    /** Creates a new {@link CollectionBinder} instance. */
+    public CollectionBinder(
+            MetadataBuildingContext metadataBuildingContext,
+            PersistentEntityNamingStrategy namingStrategy,
+            SimpleValueBinder simpleValueBinder,
+            EnumTypeBinder enumTypeBinder,
+            ManyToOneBinder manyToOneBinder,
+            CompositeIdentifierToManyToOneBinder 
compositeIdentifierToManyToOneBinder,
+            SimpleValueColumnFetcher simpleValueColumnFetcher,
+            CollectionHolder collectionHolder,
+            InFlightMetadataCollector mappings,
+            TableForManyCalculator tableForManyCalculator) {
+        this.metadataBuildingContext = metadataBuildingContext;
+        this.collectionHolder = collectionHolder;
+        this.mappings = mappings;
+        this.tableForManyCalculator = tableForManyCalculator;
+        GrailsPropertyResolver grailsPropertyResolver = new 
GrailsPropertyResolver();
+        CollectionForPropertyConfigBinder collectionForPropertyConfigBinder = 
new CollectionForPropertyConfigBinder();
+        UnidirectionalOneToManyInverseValuesBinder 
unidirectionalOneToManyInverseValuesBinder =
+                new 
UnidirectionalOneToManyInverseValuesBinder(metadataBuildingContext);
+        SimpleValueColumnBinder simpleValueColumnBinder = new 
SimpleValueColumnBinder();
+        CollectionWithJoinTableBinder collectionWithJoinTableBinder = new 
CollectionWithJoinTableBinder(

Review Comment:
   `var`?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ColumnConfigToColumnBinder.java:
##########
@@ -0,0 +1,79 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import java.util.Optional;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.mapping.Column;
+
+import org.grails.orm.hibernate.cfg.ColumnConfig;
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+
+public class ColumnConfigToColumnBinder {
+
+    private final Dialect dialect;
+
+    public ColumnConfigToColumnBinder() {
+        this(new H2Dialect());
+    }
+
+    public ColumnConfigToColumnBinder(Dialect dialect) {
+        this.dialect = dialect;
+    }
+
+    public void bindColumnConfigToColumn(@Nonnull Column column, ColumnConfig 
columnConfig, PropertyConfig mappedForm) {
+        Optional.ofNullable(columnConfig).ifPresent(config -> {
+            Optional.of(config.getLength()).filter(l -> l != 
-1).ifPresent(column::setLength);
+
+            int precision = getPrecision(config);
+
+            column.setPrecision(precision);
+
+            Optional.of(config.getScale()).filter(s -> s != 
-1).ifPresent(column::setScale);
+
+            Optional.ofNullable(config.getSqlType()).filter(s -> 
!s.isEmpty()).ifPresent(column::setSqlType);
+
+            Optional.ofNullable(mappedForm)
+                    .filter(mf -> !mf.isUniqueWithinGroup())
+                    .ifPresent(mf -> column.setUnique(config.isUnique()));
+        });
+    }
+
+    private int getPrecision(ColumnConfig config) {
+        int precision = config.getPrecision();
+        if (precision == -1) {

Review Comment:
   Won't this set precision for all column types?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ColumnConfigToColumnBinder.java:
##########
@@ -0,0 +1,79 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import java.util.Optional;
+
+import jakarta.annotation.Nonnull;

Review Comment:
   Align on JSpecify?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionBinder.java:
##########
@@ -0,0 +1,179 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import org.hibernate.boot.spi.InFlightMetadataCollector;
+import org.hibernate.boot.spi.MetadataBuildingContext;
+import org.hibernate.mapping.Collection;
+import org.hibernate.mapping.OneToMany;
+
+import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
+import 
org.grails.orm.hibernate.cfg.domainbinding.collectionType.CollectionHolder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyEntityProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BasicCollectionElementBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalMapElementBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.BidirectionalOneToManyLinker;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionKeyColumnUpdater;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.CollectionWithJoinTableBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.DependentKeyValueBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.HibernateToManyEntityOrderByBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.ListSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ListSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ManyToOneElementBinder;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.MapSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.MapSecondPassBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.PrimaryKeyValueCreator;
+import org.grails.orm.hibernate.cfg.domainbinding.secondpass.SetSecondPass;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.ToManyEntityMultiTenantFilterBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.UnidirectionalOneToManyBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.secondpass.UnidirectionalOneToManyInverseValuesBinder;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.GrailsPropertyResolver;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.SimpleValueColumnFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.TableForManyCalculator;
+
+
+/** Handles the binding of collections to the Hibernate runtime meta model. */

Review Comment:
   `@since`?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ColumnBinder.java:
##########
@@ -0,0 +1,145 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Table;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.grails.orm.hibernate.cfg.ColumnConfig;
+import org.grails.orm.hibernate.cfg.Mapping;
+import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateAssociation;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.ColumnNameForPropertyAndPathFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.CreateKeyForProps;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
+
+@SuppressWarnings({"PMD.NullAssignment", "PMD.DataflowAnomalyAnalysis"})
+public class ColumnBinder {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(ColumnBinder.class);
+
+    private final ColumnNameForPropertyAndPathFetcher 
columnNameForPropertyAndPathFetcher;
+    private final StringColumnConstraintsBinder stringColumnConstraintsBinder;
+    private final NumericColumnConstraintsBinder 
numericColumnConstraintsBinder;
+    private final CreateKeyForProps createKeyForProps;
+    private final IndexBinder indexBinder;
+
+    /** Public constructor that accepts all collaborators. */
+    public ColumnBinder(
+            ColumnNameForPropertyAndPathFetcher 
columnNameForPropertyAndPathFetcher,
+            StringColumnConstraintsBinder stringColumnConstraintsBinder,
+            NumericColumnConstraintsBinder numericColumnConstraintsBinder,
+            CreateKeyForProps createKeyForProps,
+            IndexBinder indexBinder) {
+        this.columnNameForPropertyAndPathFetcher = 
columnNameForPropertyAndPathFetcher;
+        this.stringColumnConstraintsBinder = stringColumnConstraintsBinder;
+        this.numericColumnConstraintsBinder = numericColumnConstraintsBinder;
+        this.createKeyForProps = createKeyForProps;
+        this.indexBinder = indexBinder;
+    }
+
+    /** Convenience constructor for backward compatibility. */
+    public ColumnBinder(PersistentEntityNamingStrategy namingStrategy) {
+        this(
+                new ColumnNameForPropertyAndPathFetcher(
+                        namingStrategy, new 
DefaultColumnNameFetcher(namingStrategy), new BackticksRemover()),
+                new StringColumnConstraintsBinder(),
+                new NumericColumnConstraintsBinder(),
+                new CreateKeyForProps(new ColumnNameForPropertyAndPathFetcher(
+                        namingStrategy, new 
DefaultColumnNameFetcher(namingStrategy), new BackticksRemover())),
+                new IndexBinder());
+    }
+
+    /**
+     * Binds a Column instance to the Hibernate meta model
+     *
+     * @param property The Grails domain class property
+     * @param parentProperty parent property
+     * @param column The column to bind
+     * @param path the path
+     * @param table The table name
+     */
+    public void bindColumn(
+            HibernatePersistentProperty property,
+            HibernatePersistentProperty parentProperty,
+            Column column,
+            ColumnConfig cc,
+            String path,
+            Table table) {
+
+        if (cc != null) {
+            column.setComment(cc.getComment());
+            column.setDefaultValue(cc.getDefaultValue());
+            column.setCustomRead(cc.getRead());
+            column.setCustomWrite(cc.getWrite());
+        }
+
+        Class<?> userType = property.getUserType();
+        String columnName = 
columnNameForPropertyAndPathFetcher.getColumnNameForPropertyAndPath(property, 
path, cc);
+        if ((property instanceof HibernateAssociation assoc) && userType == 
null) {
+            // Only use conventional naming when the column has not been 
explicitly mapped.
+            if (column.getName() == null) {
+                column.setName(columnName);
+            }
+            column.setNullable(assoc.isAssociationColumnNullable());
+        } else {
+            column.setName(columnName);
+            column.setNullable(property.isNullable() || (parentProperty != 
null && parentProperty.isNullable()));
+            // Use the constraints for this property to more accurately define
+            // the column's length, precision, and scale
+            Class<?> type = property.getType();
+            if (type != null && (String.class.isAssignableFrom(type) || 
byte[].class.isAssignableFrom(type))) {
+                PropertyConfig mappedForm = property.getHibernateMappedForm();
+                
stringColumnConstraintsBinder.bindStringColumnConstraints(column, mappedForm);
+            } else if (type != null && Number.class.isAssignableFrom(type)) {
+                PropertyConfig mappedForm = property.getHibernateMappedForm();
+                
numericColumnConstraintsBinder.bindNumericColumnConstraints(column, cc, 
mappedForm);
+            }
+        }
+
+        createKeyForProps.createKeyForProps(property, path, table, columnName);
+        indexBinder.bindIndex(columnName, column, cc, table);
+
+        var owner = property.getHibernateOwner();
+        if (!owner.isRoot()) {
+            Mapping mapping = owner.getHibernateMappedForm();
+            if (mapping != null && mapping.getTablePerHierarchy()) {
+                if (LOG.isDebugEnabled()) {
+                    LOG.debug("[GrailsDomainBinder] Sub class property [{}] 
for column name [{}] set to nullable", property.getName(), column.getName());
+                }
+                column.setNullable(true);
+            } else {
+                column.setNullable(property.isNullable());
+            }
+        }
+
+        // Apply uniqueness last to ensure it isn't overridden by downstream 
binders
+        PropertyConfig mappedFormFinal = property.getHibernateMappedForm();
+        column.setUnique(mappedFormFinal.isUnique() && 
!mappedFormFinal.isUniqueWithinGroup());
+
+        if (LOG.isDebugEnabled()) {
+            LOG.debug("[GrailsDomainBinder] bound property [{}] to column name 
[{}] in table [{}]", property.getName(), column.getName(), table.getName());

Review Comment:
   Why are we logging the class name `GrailsDomainBinder` here?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ColumnConfigToColumnBinder.java:
##########
@@ -0,0 +1,79 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import java.util.Optional;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.mapping.Column;
+
+import org.grails.orm.hibernate.cfg.ColumnConfig;
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+
+public class ColumnConfigToColumnBinder {
+
+    private final Dialect dialect;
+
+    public ColumnConfigToColumnBinder() {
+        this(new H2Dialect());
+    }
+
+    public ColumnConfigToColumnBinder(Dialect dialect) {
+        this.dialect = dialect;
+    }
+
+    public void bindColumnConfigToColumn(@Nonnull Column column, ColumnConfig 
columnConfig, PropertyConfig mappedForm) {
+        Optional.ofNullable(columnConfig).ifPresent(config -> {
+            Optional.of(config.getLength()).filter(l -> l != 
-1).ifPresent(column::setLength);
+
+            int precision = getPrecision(config);
+
+            column.setPrecision(precision);
+
+            Optional.of(config.getScale()).filter(s -> s != 
-1).ifPresent(column::setScale);
+
+            Optional.ofNullable(config.getSqlType()).filter(s -> 
!s.isEmpty()).ifPresent(column::setSqlType);
+
+            Optional.ofNullable(mappedForm)
+                    .filter(mf -> !mf.isUniqueWithinGroup())
+                    .ifPresent(mf -> column.setUnique(config.isUnique()));
+        });
+    }
+
+    private int getPrecision(ColumnConfig config) {
+        int precision = config.getPrecision();
+        if (precision == -1) {
+            // Apply dialect-specific defaults for Double/Float types if 
precision is not set
+            if (dialect instanceof OracleDialect) {
+                // Oracle defaults to 126 bits or 64 depending on version/type
+                precision = 126;
+            } else {
+                // Most other databases (H2, PostgreSQL, MySQL) use 53 bits 
for Double
+                // Hibernate 7 interprets this precision as decimal digits for 
some dialects
+                // and converts to bits. 15 decimal digits maps to ~50-53 bits.
+                precision = 15;
+            }
+        }

Review Comment:
   Are we sure this is correct?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ColumnBinder.java:
##########
@@ -0,0 +1,145 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import org.hibernate.mapping.Column;
+import org.hibernate.mapping.Table;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.grails.orm.hibernate.cfg.ColumnConfig;
+import org.grails.orm.hibernate.cfg.Mapping;
+import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy;
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateAssociation;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty;
+import org.grails.orm.hibernate.cfg.domainbinding.util.BackticksRemover;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.ColumnNameForPropertyAndPathFetcher;
+import org.grails.orm.hibernate.cfg.domainbinding.util.CreateKeyForProps;
+import 
org.grails.orm.hibernate.cfg.domainbinding.util.DefaultColumnNameFetcher;
+
+@SuppressWarnings({"PMD.NullAssignment", "PMD.DataflowAnomalyAnalysis"})
+public class ColumnBinder {

Review Comment:
   `@since`?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/CollectionForPropertyConfigBinder.java:
##########
@@ -0,0 +1,38 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import java.util.Optional;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.mapping.Collection;
+
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty;
+
+/** The collection for property config binder class. */

Review Comment:
   `@since`?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ColumnConfigToColumnBinder.java:
##########
@@ -0,0 +1,79 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import java.util.Optional;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.dialect.Dialect;
+import org.hibernate.dialect.H2Dialect;
+import org.hibernate.dialect.OracleDialect;
+import org.hibernate.mapping.Column;
+
+import org.grails.orm.hibernate.cfg.ColumnConfig;
+import org.grails.orm.hibernate.cfg.PropertyConfig;
+
+public class ColumnConfigToColumnBinder {

Review Comment:
   `@since`?



##########
grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ClassBinder.java:
##########
@@ -0,0 +1,79 @@
+/*
+ *  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.orm.hibernate.cfg.domainbinding.binder;
+
+import jakarta.annotation.Nonnull;
+
+import org.hibernate.boot.spi.InFlightMetadataCollector;
+import org.hibernate.mapping.PersistentClass;
+
+import org.grails.orm.hibernate.cfg.Mapping;
+import 
org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity;
+
+import static org.grails.orm.hibernate.cfg.GrailsHibernateUtil.unqualify;
+
+/** The class binder class. */

Review Comment:
   - Improve javadoc?
   - `@since`?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to