This is an automated email from the ASF dual-hosted git repository.

borinquenkid pushed a commit to branch 8.0.x-hibernate7-dev
in repository https://gitbox.apache.org/repos/asf/grails-core.git

commit 8b444d90bccd15c92da54daf84f7899e59c76be5
Author: Walter Duque de Estrada <[email protected]>
AuthorDate: Wed Mar 11 14:12:37 2026 -0500

    dbmigration: added specs for Generators
---
 grails-data-hibernate7/dbmigration/ISSUES.md       | 41 +++++++++++
 grails-data-hibernate7/dbmigration/build.gradle    |  5 ++
 .../GroovyDiffToChangeLogCommandStep.groovy        |  2 +-
 .../snapshot/ForeignKeySnapshotGenerator.java      | 64 +++++++----------
 .../snapshot/HibernateSnapshotGenerator.java       | 31 +++++++--
 .../ext/hibernate/snapshot/AuctionEntities.groovy  | 63 +++++++++++++++++
 .../snapshot/CatalogSnapshotGeneratorSpec.groovy   | 41 +++++++++++
 .../ForeignKeySnapshotGeneratorSpec.groovy         | 44 ++++++++++++
 .../snapshot/HibernateSnapshotGeneratorSpec.groovy | 39 +++++++++++
 .../HibernateSnapshotIntegrationSpec.groovy        | 81 ++++++++++++++++++++++
 .../snapshot/IndexSnapshotGeneratorSpec.groovy     | 55 +++++++++++++++
 .../PrimaryKeySnapshotGeneratorSpec.groovy         | 45 ++++++++++++
 .../snapshot/SchemaSnapshotGeneratorSpec.groovy    | 41 +++++++++++
 .../snapshot/SequenceSnapshotGeneratorSpec.groovy  | 54 +++++++++++++++
 .../snapshot/TableSnapshotGeneratorSpec.groovy     | 56 +++++++++++++++
 .../UniqueConstraintSnapshotGeneratorSpec.groovy   | 54 +++++++++++++++
 .../snapshot/ViewSnapshotGeneratorSpec.groovy      | 41 +++++++++++
 .../TableGeneratorSnapshotGeneratorSpec.groovy     | 58 ++++++++++++++++
 .../grails/rest/web/RespondMethodSpec.groovy       |  1 +
 19 files changed, 773 insertions(+), 43 deletions(-)

diff --git a/grails-data-hibernate7/dbmigration/ISSUES.md 
b/grails-data-hibernate7/dbmigration/ISSUES.md
new file mode 100644
index 0000000000..873d73651e
--- /dev/null
+++ b/grails-data-hibernate7/dbmigration/ISSUES.md
@@ -0,0 +1,41 @@
+# Database Migration Issues (Hibernate 7)
+
+This document tracks technical challenges, API incompatibilities, and known 
test failures discovered during the development of the 
`grails-data-hibernate7-dbmigration` module.
+
+---
+
+## ✅ RESOLVED
+
+### Snapshot Generator Specs — GORM Entity Recognition
+- **Affected Tests:** `ForeignKeySnapshotGeneratorSpec`, 
`PrimaryKeySnapshotGeneratorSpec`, `TableSnapshotGeneratorSpec`, 
`IndexSnapshotGeneratorSpec`, `UniqueConstraintSnapshotGeneratorSpec`, 
`SequenceSnapshotGeneratorSpec`
+- **Root Cause:** `HibernateMappingContext.createPersistentEntity()` only 
creates persistent entities for classes where 
`GormEntity.class.isAssignableFrom(javaClass)`. The original test entities were 
plain Java/Groovy classes annotated with `@jakarta.persistence.Entity`, which 
are silently skipped by GORM — resulting in empty Hibernate metadata (no table 
mappings).
+- **Fix:** Replaced all inner-static-class entities (Jakarta `@Entity`) and 
`com.example.ejb3.auction` Java imports with top-level GORM 
`@grails.gorm.annotation.Entity` classes defined in the same `.groovy` spec 
file. All 6 tests now pass.
+
+### Envers Initialization
+- **Issue:** `org.hibernate.HibernateException: Expecting EnversService to 
have been initialized prior to call to EnversIntegrator#integrate`.
+- **Fix:** Explicitly disabled Envers in test config: 
`hibernate.integration.envers.enabled: false`.
+
+---
+
+### `TableGeneratorSnapshotGeneratorSpec` — Real GORM Entity via 
`HibernateSnapshotIntegrationSpec`
+- **Issue:** `TableGenerator.getTableName()` in Hibernate 7 is not 
interceptable by Spock's `Stub()`/`GroovyStub()` — the method accesses 
`this.qualifiedTableName` which is only set after `configure()` + 
`registerExportables()`. `BasicValue.getGenerator()` was also removed in 
Hibernate 7, breaking the original approach.
+- **Fix:** Extended `HibernateSnapshotIntegrationSpec`, added a top-level GORM 
`@Entity TableGeneratorEntity` with `id generator: 'table'`, and retrieved the 
real `GrailsTableGenerator` via 
`datastore.sessionFactory.getMappingMetamodel().getEntityDescriptor(TableGeneratorEntity.name).getGenerator()`.
 Assertions use `tableGenerator.getTableName()` / 
`tableGenerator.getSegmentColumnName()` / `tableGenerator.getValueColumnName()` 
so they remain correct regardless of default param values.
+
+---
+
+## ℹ️ BACKGROUND / OTHER NOTES
+
+### `BasicValue.getGenerator()` Removal (Hibernate 7)
+- `org.hibernate.mapping.BasicValue.getGenerator()` was removed in Hibernate 7.
+- Any code that previously extracted a generator from mapping metadata this 
way must be rewritten using `createGenerator(Dialect, RootClass, ...)` or by 
iterating namespace exportables.
+
+### `JdbcDatabaseSnapshot` Constructor
+- Liquibase 4.x requires `JdbcDatabaseSnapshot(DatabaseObject[] examples, 
Database database)`. Tests use the correct constructor.
+
+### Testcontainers / Docker Dependency
+- Integration tests require Docker (PostgreSQL container). Tests use 
`@Requires({ isDockerAvailable() })` to skip when Docker is absent.
+- Checks both `~/.docker/run/docker.sock` and `/var/run/docker.sock`.
+
+### `GormDatabase` Service Loading
+- Liquibase's `DatabaseFactory` tries to instantiate all registered `Database` 
implementations via no-arg constructor. `GormDatabase` requires a 
`HibernateDatastore`, so service-loader registration may cause issues in CLI 
contexts. Worked around in tests by manual instantiation.
+
diff --git a/grails-data-hibernate7/dbmigration/build.gradle 
b/grails-data-hibernate7/dbmigration/build.gradle
index 3d4d094733..61a6e94bc1 100644
--- a/grails-data-hibernate7/dbmigration/build.gradle
+++ b/grails-data-hibernate7/dbmigration/build.gradle
@@ -78,10 +78,15 @@ dependencies {
     compileOnly 'org.springframework:spring-orm'
 
     testImplementation 'org.springframework.boot:spring-boot-starter-tomcat'
+    testImplementation project(':grails-data-hibernate7-core')
     testImplementation project(':grails-data-hibernate7')
     testImplementation project(':grails-core')
     testImplementation project(':grails-testing-support-datamapping')
     testImplementation project(':grails-testing-support-web')
+    testImplementation platform('org.testcontainers:testcontainers-bom:2.0.3')
+    testImplementation 'org.testcontainers:testcontainers'
+    testImplementation 'org.testcontainers:postgresql'
+    testImplementation 'org.testcontainers:spock'
     testImplementation 'com.h2database:h2'
     testImplementation 'org.hsqldb:hsqldb'
     testImplementation 'org.postgresql:postgresql'
diff --git 
a/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GroovyDiffToChangeLogCommandStep.groovy
 
b/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GroovyDiffToChangeLogCommandStep.groovy
index f9c46a7fbe..84c7b7027e 100644
--- 
a/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GroovyDiffToChangeLogCommandStep.groovy
+++ 
b/grails-data-hibernate7/dbmigration/src/main/groovy/org/grails/plugins/databasemigration/liquibase/GroovyDiffToChangeLogCommandStep.groovy
@@ -79,7 +79,7 @@ class GroovyDiffToChangeLogCommandStep extends 
DiffChangelogCommandStep {
         return new String[][] { COMMAND_NAME }
     }
 
-    protected static DiffCommandStep createDiffCommandStep() {
+    protected DiffCommandStep createDiffCommandStep() {
         return new DiffCommandStep()
     }
 
diff --git 
a/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGenerator.java
 
b/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGenerator.java
index 502ea80374..01f2882090 100644
--- 
a/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGenerator.java
+++ 
b/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGenerator.java
@@ -1,8 +1,5 @@
 package liquibase.ext.hibernate.snapshot;
 
-import java.util.Collection;
-
-import liquibase.diff.compare.DatabaseObjectComparatorFactory;
 import liquibase.exception.DatabaseException;
 import liquibase.ext.hibernate.database.HibernateDatabase;
 import liquibase.snapshot.DatabaseSnapshot;
@@ -33,54 +30,45 @@ public class ForeignKeySnapshotGenerator extends 
HibernateSnapshotGenerator {
             return;
         }
         if (foundObject instanceof Table table) {
-            HibernateDatabase database = (HibernateDatabase) 
snapshot.getDatabase();
-            MetadataImplementor metadata = (MetadataImplementor) 
database.getMetadata();
-
-            Collection<org.hibernate.mapping.Table> tmapp = 
metadata.collectTableMappings();
-            for (org.hibernate.mapping.Table hibernateTable : tmapp) {
-                for (org.hibernate.mapping.ForeignKey hibernateForeignKey : 
hibernateTable.getForeignKeyCollection()) {
-                    Table currentTable = new 
Table().setName(hibernateTable.getName());
-                    currentTable.setSchema(hibernateTable.getCatalog(), 
hibernateTable.getSchema());
+            org.hibernate.mapping.Table hibernateTable = 
findHibernateTable(table, snapshot);
+            if (hibernateTable == null) {
+                return;
+            }
 
+            for (org.hibernate.mapping.ForeignKey hibernateForeignKey : 
hibernateTable.getForeignKeyCollection()) {
+                if (hibernateForeignKey.isCreationEnabled()) {
                     org.hibernate.mapping.Table hibernateReferencedTable = 
hibernateForeignKey.getReferencedTable();
+
                     Table referencedTable = new 
Table().setName(hibernateReferencedTable.getName());
                     referencedTable.setSchema(
                             hibernateReferencedTable.getCatalog(), 
hibernateReferencedTable.getSchema());
 
-                    if (hibernateForeignKey.isCreationEnabled() && 
hibernateForeignKey.isPhysicalConstraint()) {
-                        ForeignKey fk = new ForeignKey();
-                        fk.setName(hibernateForeignKey.getName());
-                        fk.setPrimaryKeyTable(referencedTable);
-                        fk.setForeignKeyTable(currentTable);
-                        for (Column column : hibernateForeignKey.getColumns()) 
{
-                            fk.addForeignKeyColumn(new 
liquibase.structure.core.Column(column.getName()));
-                        }
-                        for (Column column : 
hibernateForeignKey.getReferencedColumns()) {
-                            fk.addPrimaryKeyColumn(new 
liquibase.structure.core.Column(column.getName()));
-                        }
-                        if (fk.getPrimaryKeyColumns() == null
-                                || fk.getPrimaryKeyColumns().isEmpty()) {
+                    ForeignKey fk = new ForeignKey();
+                    fk.setName(hibernateForeignKey.getName());
+                    fk.setPrimaryKeyTable(referencedTable);
+                    fk.setForeignKeyTable(table);
+                    for (Column column : hibernateForeignKey.getColumns()) {
+                        fk.addForeignKeyColumn(new 
liquibase.structure.core.Column(column.getName()));
+                    }
+                    for (Column column : 
hibernateForeignKey.getReferencedColumns()) {
+                        fk.addPrimaryKeyColumn(new 
liquibase.structure.core.Column(column.getName()));
+                    }
+                    if (fk.getPrimaryKeyColumns() == null
+                            || fk.getPrimaryKeyColumns().isEmpty()) {
+                        if (hibernateReferencedTable.getPrimaryKey() != null) {
                             for (Column column :
                                     
hibernateReferencedTable.getPrimaryKey().getColumns()) {
                                 fk.addPrimaryKeyColumn(new 
liquibase.structure.core.Column(column.getName()));
                             }
                         }
+                    }
 
-                        fk.setDeferrable(false);
-                        fk.setInitiallyDeferred(false);
-
-                        //                     Index index = new Index();
-                        //                     index.setName("IX_" + 
fk.getName());
-                        //                     
index.setTable(fk.getForeignKeyTable());
-                        //                     
index.setColumns(fk.getForeignKeyColumns());
-                        //                     fk.setBackingIndex(index);
-                        //                     table.getIndexes().add(index);
+                    fk.setDeferrable(false);
+                    fk.setInitiallyDeferred(false);
 
-                        if (DatabaseObjectComparatorFactory.getInstance()
-                                .isSameObject(currentTable, table, null, 
database)) {
-                            table.getOutgoingForeignKeys().add(fk);
-                            table.getSchema().addDatabaseObject(fk);
-                        }
+                    table.getOutgoingForeignKeys().add(fk);
+                    if (table.getSchema() != null) {
+                        table.getSchema().addDatabaseObject(fk);
                     }
                 }
             }
diff --git 
a/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/HibernateSnapshotGenerator.java
 
b/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/HibernateSnapshotGenerator.java
index bfc40d256a..676026e8d7 100644
--- 
a/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/HibernateSnapshotGenerator.java
+++ 
b/grails-data-hibernate7/dbmigration/src/main/java/liquibase/ext/hibernate/snapshot/HibernateSnapshotGenerator.java
@@ -8,6 +8,7 @@ import liquibase.snapshot.InvalidExampleException;
 import liquibase.snapshot.SnapshotGenerator;
 import liquibase.snapshot.SnapshotGeneratorChain;
 import liquibase.structure.DatabaseObject;
+import org.hibernate.boot.Metadata;
 import org.hibernate.boot.spi.MetadataImplementor;
 
 /**
@@ -87,16 +88,38 @@ public abstract class HibernateSnapshotGenerator implements 
SnapshotGenerator {
             throws DatabaseException, InvalidExampleException;
 
     protected org.hibernate.mapping.Table findHibernateTable(DatabaseObject 
example, DatabaseSnapshot snapshot) {
-        var database = (HibernateDatabase) snapshot.getDatabase();
-        var metadata = (MetadataImplementor) database.getMetadata();
+        Metadata metadata = null;
+        Database database = snapshot.getDatabase();
+        if (database instanceof HibernateDatabase hibernateDatabase) {
+            metadata = hibernateDatabase.getMetadata();
+        } else {
+            try {
+                metadata = (Metadata) 
database.getClass().getMethod("getMetadata").invoke(database);
+            } catch (Exception e) {
+                // not a metadata-bearing database
+            }
+        }
+
+        if (metadata == null) {
+            return null;
+        }
 
-        var tmapp = metadata.collectTableMappings();
+        MetadataImplementor metadataImplementor = (MetadataImplementor) 
metadata;
 
-        for (var hibernateTable : tmapp) {
+        for (var hibernateTable : metadataImplementor.collectTableMappings()) {
             if (hibernateTable.getName().equalsIgnoreCase(example.getName())) {
                 return hibernateTable;
             }
         }
+
+        for (var namespace : 
metadataImplementor.getDatabase().getNamespaces()) {
+            for (var hibernateTable : namespace.getTables()) {
+                if 
(hibernateTable.getName().equalsIgnoreCase(example.getName())) {
+                    return hibernateTable;
+                }
+            }
+        }
+
         return null;
     }
 }
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/AuctionEntities.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/AuctionEntities.groovy
new file mode 100644
index 0000000000..fbfbd8b6f5
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/AuctionEntities.groovy
@@ -0,0 +1,63 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import grails.gorm.annotation.Entity
+
+@Entity
+class AuctionItem {
+    String description
+    String shortDescription
+    Date ends
+    Integer condition
+
+    static hasMany = [bids: Bid]
+
+    static constraints = {
+        description nullable: true, maxSize: 1000
+        shortDescription nullable: true, maxSize: 200
+        ends nullable: true
+        condition nullable: true
+    }
+}
+
+@Entity
+class Bid {
+    Float amount
+    Date datetime
+
+    static belongsTo = [item: AuctionItem, bidder: AuctionUser]
+
+    static constraints = {
+        datetime nullable: false
+    }
+}
+
+@Entity
+class AuctionUser {
+    String userName
+    String email
+
+    static hasMany = [bids: Bid]
+
+    static constraints = {
+        userName nullable: true
+        email nullable: true
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/CatalogSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/CatalogSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..f7a082446c
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/CatalogSnapshotGeneratorSpec.groovy
@@ -0,0 +1,41 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import com.example.ejb3.auction.AuctionItem
+import liquibase.structure.core.Catalog
+
+class CatalogSnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec {
+
+    CatalogSnapshotGenerator generator = new CatalogSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [AuctionItem]
+    }
+
+    def "snapshotObject returns default catalog"() {
+        when:
+        def result = generator.snapshotObject(new Catalog(), snapshot)
+
+        then:
+        result instanceof Catalog
+        result.isDefault()
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..399f3b90c6
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/ForeignKeySnapshotGeneratorSpec.groovy
@@ -0,0 +1,44 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import liquibase.structure.core.ForeignKey
+import liquibase.structure.core.Table
+
+class ForeignKeySnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec 
{
+
+    ForeignKeySnapshotGenerator generator = new ForeignKeySnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [AuctionItem, Bid, AuctionUser]
+    }
+
+    def "addTo adds foreign keys to table"() {
+        given:
+        Table table = new Table(name: "Bid")
+        snapshot.getSnapshotControl().shouldInclude(ForeignKey) >> true
+
+        when:
+        generator.addTo(table, snapshot)
+
+        then:
+        table.getOutgoingForeignKeys().any { 
it.foreignKeyTable.name.equalsIgnoreCase("Bid") }
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/HibernateSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/HibernateSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..885901aaf4
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/HibernateSnapshotGeneratorSpec.groovy
@@ -0,0 +1,39 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import liquibase.ext.hibernate.database.HibernateDatabase
+import liquibase.snapshot.DatabaseSnapshot
+import liquibase.snapshot.SnapshotControl
+import org.hibernate.boot.spi.MetadataImplementor
+import spock.lang.Specification
+
+abstract class HibernateSnapshotGeneratorSpec extends Specification {
+
+    DatabaseSnapshot snapshot = Mock()
+    HibernateDatabase database = Mock()
+    MetadataImplementor metadata = Mock()
+    SnapshotControl snapshotControl = Mock()
+
+    def setup() {
+        snapshot.getDatabase() >> database
+        snapshot.getSnapshotControl() >> snapshotControl
+        database.getMetadata() >> metadata
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/HibernateSnapshotIntegrationSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/HibernateSnapshotIntegrationSpec.groovy
new file mode 100644
index 0000000000..0dab1a71d4
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/HibernateSnapshotIntegrationSpec.groovy
@@ -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 liquibase.ext.hibernate.snapshot
+
+import liquibase.CatalogAndSchema
+import liquibase.ext.hibernate.database.HibernateDatabase
+import liquibase.snapshot.DatabaseSnapshot
+import liquibase.snapshot.JdbcDatabaseSnapshot
+import liquibase.structure.DatabaseObject
+import org.grails.orm.hibernate.HibernateDatastore
+import org.grails.plugins.databasemigration.liquibase.GormDatabase
+import org.hibernate.boot.Metadata
+import org.hibernate.dialect.PostgreSQLDialect
+import org.testcontainers.containers.PostgreSQLContainer
+import org.testcontainers.spock.Testcontainers
+import spock.lang.AutoCleanup
+import spock.lang.Requires
+import spock.lang.Shared
+import spock.lang.Specification
+
+@Testcontainers
+@Requires({ isDockerAvailable() })
+abstract class HibernateSnapshotIntegrationSpec extends Specification {
+
+    @Shared PostgreSQLContainer postgres = new 
PostgreSQLContainer("postgres:16")
+
+    @AutoCleanup
+    HibernateDatastore datastore
+    HibernateDatabase database
+    DatabaseSnapshot snapshot
+    Metadata metadata
+
+    def setup() {
+        Map config = [
+                'hibernate.dialect'           : 
PostgreSQLDialect.class.getName(),
+                'dataSource.url'              : postgres.jdbcUrl,
+                'dataSource.driverClassName'  : postgres.driverClassName,
+                'dataSource.username'         : postgres.username,
+                'dataSource.password'         : postgres.password,
+                'hibernate.hbm2ddl.auto'      : 'create-drop',
+                'hibernate.integration.envers.enabled': false
+        ]
+        
+        datastore = new HibernateDatastore(config, getEntityClasses() as 
Class[])
+        metadata = datastore.getMetadata()
+        
+        database = new GormDatabase(new PostgreSQLDialect(), datastore)
+        
+        snapshot = new JdbcDatabaseSnapshot([] as DatabaseObject[], database)
+    }
+
+    abstract List<Class> getEntityClasses()
+
+    /**
+     * Returns true when a Docker daemon is reachable on this machine.
+     */
+    static boolean isDockerAvailable() {
+        def candidates = [
+            System.getProperty('user.home') + '/.docker/run/docker.sock',
+            '/var/run/docker.sock',
+            System.getenv('DOCKER_HOST') ?: ''
+        ]
+        candidates.any { it && new File(it).exists() }
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/IndexSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/IndexSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..f5039090a4
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/IndexSnapshotGeneratorSpec.groovy
@@ -0,0 +1,55 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import grails.gorm.annotation.Entity
+import liquibase.structure.core.Index as LiquibaseIndex
+import liquibase.structure.core.Table as LiquibaseTable
+
+class IndexSnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec {
+
+    IndexSnapshotGenerator generator = new IndexSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [IndexedEntity]
+    }
+
+    def "addTo adds indexes to table"() {
+        given:
+        LiquibaseTable table = new LiquibaseTable(name: "indexed_entity")
+        snapshot.getSnapshotControl().shouldInclude(LiquibaseIndex) >> true
+
+        when:
+        generator.addTo(table, snapshot)
+
+        then:
+        table.getIndexes().any { it.name.equalsIgnoreCase("idx_code") }
+    }
+}
+
+@Entity
+class IndexedEntity {
+    String code
+
+    static mapping = {
+        table 'indexed_entity'
+        code index: 'idx_code'
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/PrimaryKeySnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/PrimaryKeySnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..87ae95410b
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/PrimaryKeySnapshotGeneratorSpec.groovy
@@ -0,0 +1,45 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import liquibase.structure.core.PrimaryKey
+import liquibase.structure.core.Table
+
+class PrimaryKeySnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec 
{
+
+    PrimaryKeySnapshotGenerator generator = new PrimaryKeySnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [AuctionItem, Bid, AuctionUser]
+    }
+
+    def "addTo adds primary key to table"() {
+        given:
+        Table table = new Table(name: "auction_item")
+        snapshot.getSnapshotControl().shouldInclude(PrimaryKey) >> true
+
+        when:
+        generator.addTo(table, snapshot)
+
+        then:
+        table.primaryKey != null
+        table.primaryKey.columns*.name.contains("id")
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/SchemaSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/SchemaSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..bc9158e57b
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/SchemaSnapshotGeneratorSpec.groovy
@@ -0,0 +1,41 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import com.example.ejb3.auction.AuctionItem
+import liquibase.structure.core.Schema
+
+class SchemaSnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec {
+
+    SchemaSnapshotGenerator generator = new SchemaSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [AuctionItem]
+    }
+
+    def "snapshotObject returns default schema"() {
+        when:
+        def result = generator.snapshotObject(new Schema(), snapshot)
+
+        then:
+        result instanceof Schema
+        result.isDefault()
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/SequenceSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/SequenceSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..d1a0755c5d
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/SequenceSnapshotGeneratorSpec.groovy
@@ -0,0 +1,54 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import grails.gorm.annotation.Entity
+import liquibase.structure.core.Schema
+import liquibase.structure.core.Sequence as LiquibaseSequence
+
+class SequenceSnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec {
+
+    SequenceSnapshotGenerator generator = new SequenceSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [SequenceEntity]
+    }
+
+    def "addTo adds sequences from namespaces"() {
+        given:
+        Schema schema = new Schema()
+        snapshot.getSnapshotControl().shouldInclude(LiquibaseSequence) >> true
+
+        when:
+        generator.addTo(schema, snapshot)
+
+        then:
+        schema.getDatabaseObjects(LiquibaseSequence).any { 
it.name.equalsIgnoreCase("test_sequence") }
+    }
+}
+
+@Entity
+class SequenceEntity {
+    Long id
+
+    static mapping = {
+        id generator: 'sequence', params: [sequence_name: 'test_sequence', 
allocationSize: 50]
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/TableSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/TableSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..d7fe2ab294
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/TableSnapshotGeneratorSpec.groovy
@@ -0,0 +1,56 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import liquibase.structure.core.Schema
+import liquibase.structure.core.Table
+
+class TableSnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec {
+
+    TableSnapshotGenerator generator = new TableSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [AuctionItem, Bid, AuctionUser]
+    }
+
+    def "snapshotObject returns table with name"() {
+        given:
+        Table example = new Table(name: "auction_item")
+
+        when:
+        def result = generator.snapshotObject(example, snapshot)
+
+        then:
+        result instanceof Table
+        result.name == "auction_item"
+    }
+
+    def "addTo adds tables to schema"() {
+        given:
+        Schema schema = new Schema()
+        snapshot.getSnapshotControl().shouldInclude(Table) >> true
+
+        when:
+        generator.addTo(schema, snapshot)
+
+        then:
+        schema.getDatabaseObjects(Table).any { it.name == "auction_item" }
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..5e9ae89c57
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/UniqueConstraintSnapshotGeneratorSpec.groovy
@@ -0,0 +1,54 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import grails.gorm.annotation.Entity
+import liquibase.structure.core.Table
+import liquibase.structure.core.UniqueConstraint
+
+class UniqueConstraintSnapshotGeneratorSpec extends 
HibernateSnapshotIntegrationSpec {
+
+    UniqueConstraintSnapshotGenerator generator = new 
UniqueConstraintSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [UniqueEntity]
+    }
+
+    def "addTo adds unique constraints to table"() {
+        given:
+        Table table = new Table(name: "unique_entity")
+        snapshot.getSnapshotControl().shouldInclude(UniqueConstraint) >> true
+
+        when:
+        generator.addTo(table, snapshot)
+
+        then:
+        table.getUniqueConstraints().any { it.columnNames.contains("code") }
+    }
+}
+
+@Entity
+class UniqueEntity {
+    String code
+
+    static constraints = {
+        code unique: true
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/ViewSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/ViewSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..768618698e
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/ViewSnapshotGeneratorSpec.groovy
@@ -0,0 +1,41 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot
+
+import com.example.ejb3.auction.AuctionItem
+import liquibase.exception.DatabaseException
+import liquibase.structure.core.View
+
+class ViewSnapshotGeneratorSpec extends HibernateSnapshotIntegrationSpec {
+
+    ViewSnapshotGenerator generator = new ViewSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [AuctionItem]
+    }
+
+    def "snapshotObject throws exception as views are not supported"() {
+        when:
+        generator.snapshotObject(new View(), snapshot)
+
+        then:
+        thrown(DatabaseException)
+    }
+}
diff --git 
a/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/extension/TableGeneratorSnapshotGeneratorSpec.groovy
 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/extension/TableGeneratorSnapshotGeneratorSpec.groovy
new file mode 100644
index 0000000000..2d9e022482
--- /dev/null
+++ 
b/grails-data-hibernate7/dbmigration/src/test/groovy/liquibase/ext/hibernate/snapshot/extension/TableGeneratorSnapshotGeneratorSpec.groovy
@@ -0,0 +1,58 @@
+/*
+ *  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 liquibase.ext.hibernate.snapshot.extension
+
+import grails.gorm.annotation.Entity
+import liquibase.ext.hibernate.snapshot.HibernateSnapshotIntegrationSpec
+import liquibase.structure.core.Table
+import org.hibernate.id.enhanced.TableGenerator
+
+class TableGeneratorSnapshotGeneratorSpec extends 
HibernateSnapshotIntegrationSpec {
+
+    TableGeneratorSnapshotGenerator generator = new 
TableGeneratorSnapshotGenerator()
+
+    @Override
+    List<Class> getEntityClasses() {
+        return [TableGeneratorEntity]
+    }
+
+    def "snapshot returns table with generator details"() {
+        given:
+        def persister = 
datastore.sessionFactory.getMappingMetamodel().getEntityDescriptor(TableGeneratorEntity.name)
+        def tableGenerator = persister.getGenerator() as TableGenerator
+
+        when:
+        Table table = generator.snapshot(tableGenerator)
+
+        then:
+        table.name == tableGenerator.getTableName()
+        table.getColumn(tableGenerator.getSegmentColumnName()) != null
+        table.getColumn(tableGenerator.getValueColumnName()) != null
+        table.primaryKey != null
+    }
+}
+
+@Entity
+class TableGeneratorEntity {
+    Long id
+
+    static mapping = {
+        id generator: 'table'
+    }
+}
diff --git 
a/grails-test-suite-web/src/test/groovy/grails/rest/web/RespondMethodSpec.groovy
 
b/grails-test-suite-web/src/test/groovy/grails/rest/web/RespondMethodSpec.groovy
index 3a51e20b4a..0d45e8f5e3 100644
--- 
a/grails-test-suite-web/src/test/groovy/grails/rest/web/RespondMethodSpec.groovy
+++ 
b/grails-test-suite-web/src/test/groovy/grails/rest/web/RespondMethodSpec.groovy
@@ -292,6 +292,7 @@ class BookController {
 }
 @Entity
 class Book {
+    Long id
     String title
 
     static constraints = {


Reply via email to