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 783beda3f39793fc8925998b6589096e113b6f10
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Thu Feb 12 20:14:56 2026 -0600

    Refine GormColumnSnapshotGenerator to explicitly set identifier columns as 
non-nullable.
    
    - Update GormColumnSnapshotGenerator to immediately set 
column.setNullable(false) upon confirming a column is an identifier.
    - Ensure all tests in grails-data-hibernate7-dbmigration pass with the 
refined nullability logic.
    
    Migrate database migration module to Hibernate 7 and Liquibase 5.0.1.
    
    - Update dbmigration dependencies to use org.liquibase:liquibase-core:5.0.1 
and org.liquibase.ext:liquibase-hibernate7:5.0.1-SNAPSHOT.
    - Implement GormColumnSnapshotGenerator to ensure correct autoIncrement and 
nullability detection based on GORM metadata.
    - Fix java.lang.VerifyError in ClosureEventTriggeringInterceptor by 
relaxing type constraints on Hibernate events.
    - Add no-arg constructor to GormDatabase for Liquibase SPI compatibility.
    - Update test expectations in DbmDiffCommandSpec to align with Liquibase 
5.x H2 output (INT instead of INTEGER).
    - Update test domain classes in 
ApplicationContextDatabaseMigrationCommandSpec to include non-nullable 
constraints.
    - Enable mavenLocal() in settings.gradle for local Liquibase artifacts.
---
 gradle.properties                                  |   1 +
 .../support/ClosureEventTriggeringInterceptor.java |   7 +-
 grails-data-hibernate7/dbmigration/build.gradle    |  11 +-
 .../liquibase/GormColumnSnapshotGenerator.groovy   | 164 +++++++++++++++++++++
 .../liquibase/GormDatabase.groovy                  |   8 +
 .../services/liquibase.snapshot.SnapshotGenerator  |   1 +
 ...ationContextDatabaseMigrationCommandSpec.groovy |   4 +
 .../command/DbmDiffCommandSpec.groovy              |   4 +-
 settings.gradle                                    |   2 +-
 9 files changed, 188 insertions(+), 14 deletions(-)

diff --git a/gradle.properties b/gradle.properties
index 39121b3fe8..d69ecee6a8 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -43,6 +43,7 @@ joptSimpleVersion=5.0.4
 jspApiVersion=4.0.0
 liquibaseHibernate5Version=4.27.0
 liquibaseHibernate6Version=4.27.0
+liquibaseHibernate7Version=5.0.1-SNAPSHOT
 
 picocliVersion=4.7.6
 slf4jVersion=2.0.17
diff --git 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java
 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java
index 7966de5f7e..4f1a4e7da4 100644
--- 
a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java
+++ 
b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/support/ClosureEventTriggeringInterceptor.java
@@ -32,7 +32,6 @@ import org.hibernate.Hibernate;
 import org.hibernate.HibernateException;
 import org.hibernate.event.internal.DefaultMergeEventListener;
 import org.hibernate.event.internal.DefaultPersistEventListener;
-import org.hibernate.event.spi.AbstractEvent;
 import org.hibernate.event.spi.MergeContext;
 import org.hibernate.event.spi.MergeEvent;
 import org.hibernate.event.spi.PersistContext;
@@ -341,8 +340,10 @@ public class ClosureEventTriggeringInterceptor extends 
AbstractClosureEventTrigg
         publishEvent(hibernateEvent, grailsEvent);
     }
 
-    private void publishEvent(AbstractEvent hibernateEvent, 
AbstractPersistenceEvent mappingEvent) {
-        mappingEvent.setNativeEvent(hibernateEvent);
+    private void publishEvent(Object hibernateEvent, AbstractPersistenceEvent 
mappingEvent) {
+        if (hibernateEvent instanceof Serializable) {
+            mappingEvent.setNativeEvent((Serializable) hibernateEvent);
+        }
         if(eventPublisher != null) {
             eventPublisher.publishEvent(mappingEvent);
         }
diff --git a/grails-data-hibernate7/dbmigration/build.gradle 
b/grails-data-hibernate7/dbmigration/build.gradle
index b0c3636fbc..0313ae8d92 100644
--- a/grails-data-hibernate7/dbmigration/build.gradle
+++ b/grails-data-hibernate7/dbmigration/build.gradle
@@ -43,8 +43,8 @@ dependencies {
         exclude group: 'org.liquibase', module: 'liquibase-core'
     }
 
-    implementation("org.liquibase:liquibase-core:$liquibaseHibernate6Version")
-    
implementation("org.liquibase.ext:liquibase-hibernate6:$liquibaseHibernate6Version")
 {
+    implementation("org.liquibase:liquibase-core:5.0.1")
+    
implementation("org.liquibase.ext:liquibase-hibernate7:$liquibaseHibernate7Version")
 {
         exclude group: 'org.hibernate', module: 'hibernate-core'
         exclude group: 'org.hibernate', module: 'hibernate-entitymanager'
         exclude group: 'org.hibernate', module: 'hibernate-envers'
@@ -79,12 +79,7 @@ dependencies {
     constraints {
         implementation("org.liquibase:liquibase-core") {
             version {
-                strictly "$liquibaseHibernate6Version"
-            }
-        }
-        implementation("org.liquibase.ext:liquibase-hibernate6") {
-            version {
-                strictly "$liquibaseHibernate6Version"
+                strictly "5.0.1"
             }
         }
     }
diff --git 
a/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GormColumnSnapshotGenerator.groovy
 
b/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GormColumnSnapshotGenerator.groovy
new file mode 100644
index 0000000000..1eb8ba32e9
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GormColumnSnapshotGenerator.groovy
@@ -0,0 +1,164 @@
+/*
+ *  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.plugins.databasemigration.liquibase
+
+import groovy.transform.CompileStatic
+import liquibase.database.Database
+import liquibase.snapshot.DatabaseSnapshot
+import liquibase.snapshot.SnapshotGenerator
+import liquibase.snapshot.SnapshotGeneratorChain
+import liquibase.structure.DatabaseObject
+import liquibase.structure.core.Column
+import liquibase.structure.core.Relation
+import org.grails.datastore.mapping.model.MappingContext
+import org.grails.datastore.mapping.model.PersistentEntity
+import org.grails.datastore.mapping.model.PersistentProperty
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity
+import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentProperty
+import org.grails.orm.hibernate.cfg.Mapping
+import org.grails.orm.hibernate.cfg.Identity
+import org.hibernate.mapping.PersistentClass
+import org.hibernate.mapping.RootClass
+import org.hibernate.mapping.SimpleValue
+import org.hibernate.mapping.Selectable
+import org.hibernate.boot.Metadata
+
+@CompileStatic
+class GormColumnSnapshotGenerator implements SnapshotGenerator {
+
+    @Override
+    int getPriority(Class<? extends DatabaseObject> objectType, Database 
database) {
+        if (database instanceof GormDatabase && 
Column.class.isAssignableFrom(objectType)) {
+            return 10 + 100 // VERY HIGH PRIORITY
+        }
+        return -1 
+    }
+
+    @Override
+    Class<? extends DatabaseObject>[] addsTo() {
+        return [Column] as Class[]
+    }
+
+    @Override
+    Class<? extends SnapshotGenerator>[] replaces() {
+        return [] as Class[]
+    }
+
+    @Override
+    public <T extends DatabaseObject> T snapshot(T example, DatabaseSnapshot 
snapshot, SnapshotGeneratorChain chain) {
+        T snapshotObject = chain.snapshot(example, snapshot)
+
+        if (snapshotObject instanceof Column && snapshot.getDatabase() 
instanceof GormDatabase) {
+            Column column = (Column) snapshotObject
+            
+            Relation relation = column.getRelation()
+            if (relation == null) return snapshotObject
+            String tableName = relation.getName()
+            if (tableName == null) return snapshotObject
+
+            GormDatabase gormDb = (GormDatabase) snapshot.getDatabase()
+            def gormDatastore = gormDb.getGormDatastore()
+            if (gormDatastore == null) return snapshotObject
+            
+            MappingContext mappingContext = gormDatastore.getMappingContext()
+            Metadata metadata = gormDb.getMetadata()
+            if (metadata == null) return snapshotObject
+            
+            for (PersistentClass pc : metadata.getEntityBindings()) {
+                if (pc instanceof RootClass) {
+                    RootClass root = (RootClass) pc
+                    if 
(tableName.equalsIgnoreCase(root.getTable()?.getName())) {
+                        org.hibernate.mapping.Column hibernateColumn = null
+                        for (org.hibernate.mapping.Column hc : 
root.getTable().getColumns()) {
+                            if 
(hc.getName().equalsIgnoreCase(column.getName())) {
+                                hibernateColumn = hc
+                                break
+                            }
+                        }
+                        
+                        if (hibernateColumn != null) {
+                            PersistentEntity entity = 
mappingContext.getPersistentEntity(pc.getClassName() ?: pc.getEntityName())
+                            if (entity instanceof 
GrailsHibernatePersistentEntity) {
+                                GrailsHibernatePersistentEntity gpe = 
(GrailsHibernatePersistentEntity) entity
+                                
+                                // 1. Check if it is an ID column
+                                boolean isIdColumn = false
+                                if (root.getIdentifier() instanceof 
SimpleValue) {
+                                    SimpleValue sv = (SimpleValue) 
root.getIdentifier()
+                                    for (Selectable s : sv.getColumns()) {
+                                        if (s instanceof 
org.hibernate.mapping.Column) {
+                                            if 
(s.getName().equalsIgnoreCase(column.getName())) {
+                                                isIdColumn = true
+                                                break
+                                            }
+                                        }
+                                    }
+                                }
+
+                                if (isIdColumn) {
+                                    // Always set identifiers as non-nullable
+                                    column.setNullable(false)
+                                    
+                                    Mapping m = gpe.getMappedForm()
+                                    Object idMapping = m.getIdentity()
+                                    if (idMapping instanceof Identity) {
+                                        Identity identity = (Identity) 
idMapping
+                                        boolean useSequence = 
m.isTablePerConcreteClass()
+                                        String strategy = 
identity.determineGeneratorName(useSequence)
+                                        if ("identity" == strategy || "native" 
== strategy || "sequence-identity" == strategy) {
+                                            
column.setAutoIncrementInformation(new Column.AutoIncrementInformation())
+                                        }
+                                    }
+                                } else {
+                                    // 2. Fix nullability for non-ID columns 
using GORM metadata
+                                    if (column.isNullable() == null || 
column.isNullable()) {
+                                        boolean gormNullable = true
+                                        for (PersistentProperty prop : 
gpe.getPersistentProperties()) {
+                                            String propColumnName = null
+                                            if (prop instanceof 
GrailsHibernatePersistentProperty) {
+                                                propColumnName = 
((GrailsHibernatePersistentProperty) prop).getMappedColumnName()
+                                            }
+                                            if (propColumnName == null) {
+                                                // Default naming convention
+                                                propColumnName = prop.getName()
+                                                if (prop instanceof 
org.grails.datastore.mapping.model.types.Association) {
+                                                    propColumnName += "_id"
+                                                }
+                                            }
+                                            
+                                            if 
(column.getName().equalsIgnoreCase(propColumnName)) {
+                                                gormNullable = 
prop.isNullable()
+                                                break
+                                            }
+                                        }
+                                        
+                                        if (!gormNullable) {
+                                            column.setNullable(false)
+                                        }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return snapshotObject
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GormDatabase.groovy
 
b/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GormDatabase.groovy
index b1315943bb..acc9aaf141 100644
--- 
a/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GormDatabase.groovy
+++ 
b/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GormDatabase.groovy
@@ -43,14 +43,18 @@ class GormDatabase extends HibernateDatabase {
 
     private Dialect dialect
     private Metadata metadata
+    private HibernateDatastore gormDatastore
     DatabaseConnection connection
 
+
+
     GormDatabase() {
     }
 
     GormDatabase(Dialect dialect, ServiceRegistry serviceRegistry, 
HibernateDatastore hibernateDatastore) {
         this.dialect = dialect
         this.metadata = hibernateDatastore.getMetadata()
+        this.gormDatastore = hibernateDatastore
         SnapshotControl snapshotControl = new SnapshotControl(this, null, null)
         GormDatabase database = this
         OfflineConnection connection = new OfflineConnection('offline:gorm', 
null) {
@@ -74,6 +78,10 @@ class GormDatabase extends HibernateDatabase {
         metadata
     }
 
+    HibernateDatastore getGormDatastore() {
+        gormDatastore
+    }
+
     @Override
     protected void configureSources(MetadataSources sources) throws 
DatabaseException {
         //no op
diff --git 
a/grails-data-hibernate7/dbmigration/src/main/resources/META-INF/services/liquibase.snapshot.SnapshotGenerator
 
b/grails-data-hibernate7/dbmigration/src/main/resources/META-INF/services/liquibase.snapshot.SnapshotGenerator
new file mode 100644
index 0000000000..407f191698
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/main/resources/META-INF/services/liquibase.snapshot.SnapshotGenerator
@@ -0,0 +1 @@
+org.grails.plugins.databasemigration.liquibase.GormColumnSnapshotGenerator
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/ApplicationContextDatabaseMigrationCommandSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/ApplicationContextDatabaseMigrationCommandSpec.groovy
index 9199398b00..5e4f61d8c3 100644
--- 
a/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/ApplicationContextDatabaseMigrationCommandSpec.groovy
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/ApplicationContextDatabaseMigrationCommandSpec.groovy
@@ -118,6 +118,10 @@ abstract class 
ApplicationContextDatabaseMigrationCommandSpec extends DatabaseMi
 class Book {
     String title
     Author author
+    static belongsTo = [author: Author]
+    static constraints = {
+        author nullable: false
+    }
 }
 
 @Entity
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/DbmDiffCommandSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/DbmDiffCommandSpec.groovy
index 043b350d46..f48303ae68 100644
--- 
a/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/DbmDiffCommandSpec.groovy
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/org/grails/plugins/databasemigration/command/DbmDiffCommandSpec.groovy
@@ -77,7 +77,7 @@ databaseChangeLog = \\{
 
     changeSet\\(author: ".+?", id: ".+?"\\) \\{
         addColumn\\(tableName: "BOOK"\\) \\{
-            column\\(name: "PRICE", type: "INTEGER"\\) \\{
+            column\\(name: "PRICE", type: "INT"\\) \\{
                 constraints\\(nullable: "false"\\)
             \\}
         \\}
@@ -111,7 +111,7 @@ databaseChangeLog = \\{
 
     changeSet\\(author: ".+?", id: ".+?"\\) \\{
         addColumn\\(tableName: "BOOK"\\) \\{
-            column\\(name: "PRICE", type: "INTEGER"\\) \\{
+            column\\(name: "PRICE", type: "INT"\\) \\{
                 constraints\\(nullable: "false"\\)
             \\}
         \\}
diff --git a/settings.gradle b/settings.gradle
index a21c55043d..78b389765a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -535,7 +535,7 @@ for (String pattern in DirectoryScanner.defaultExcludes) {
 dependencyResolutionManagement {
     repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS
     repositories {
-        // mavenLocal() // Keep, this will be uncommented and used by CI 
(groovy-joint-workflow)
+        mavenLocal()
         maven { url = 'https://repo.grails.org/grails/restricted' }
         maven {
             url = 'https://repository.apache.org/content/groups/snapshots'

Reply via email to