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 c209329a8ab2111ad64ce452fb6e342955ca5a17 Author: Walter B Duque de Estrada <[email protected]> AuthorDate: Fri Jan 30 08:37:28 2026 -0600 progress --- .../cfg/domainbinding/BasicValueIdCreator.java | 52 ++++-------- .../cfg/domainbinding/SimpleValueBinder.java | 10 ++- .../domainbinding/BasicValueIdCreatorSpec.groovy | 92 ++++++++++++---------- 3 files changed, 73 insertions(+), 81 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreator.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreator.java index 6514b20181..cbb91964c4 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreator.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreator.java @@ -1,67 +1,45 @@ package org.grails.orm.hibernate.cfg.domainbinding; -import java.util.HashMap; -import java.util.Map; import java.util.Optional; -import java.util.function.BiFunction; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; -import org.hibernate.generator.Assigned; import org.hibernate.generator.Generator; import org.hibernate.generator.GeneratorCreationContext; -import org.hibernate.id.enhanced.SequenceStyleGenerator; -import org.hibernate.id.uuid.UuidGenerator; import org.hibernate.mapping.BasicValue; import org.hibernate.mapping.RootClass; import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity; import org.grails.orm.hibernate.cfg.Identity; +import org.grails.orm.hibernate.cfg.domainbinding.generator.GrailsSequenceGeneratorEnum; +import org.grails.orm.hibernate.cfg.domainbinding.generator.GrailsSequenceWrapper; public class BasicValueIdCreator { private final JdbcEnvironment jdbcEnvironment; private GrailsHibernatePersistentEntity domainClass; - private final Map<String, BiFunction<GeneratorCreationContext, Identity, Generator>> generatorFactories; @SuppressWarnings("unused") // kept for tests that want to provide a prototype BasicValue private final BasicValue id; + private final GrailsSequenceWrapper grailsSequenceWrapper; public BasicValueIdCreator(MetadataBuildingContext metadataBuildingContext, JdbcEnvironment jdbcEnvironment, GrailsHibernatePersistentEntity domainClass, RootClass entity) { // create a prototype BasicValue (table will be set per-entity when creating the actual BasicValue) - this(jdbcEnvironment, new BasicValue(metadataBuildingContext, entity.getTable()), new HashMap<>()); + this.id = new BasicValue(metadataBuildingContext, entity.getTable()); + this.jdbcEnvironment = jdbcEnvironment; this.domainClass = domainClass; - initializeGeneratorFactories(); + this.grailsSequenceWrapper = new GrailsSequenceWrapper(); } - public BasicValueIdCreator(MetadataBuildingContext metadataBuildingContext, JdbcEnvironment jdbcEnvironment, Map<String, BiFunction<GeneratorCreationContext, Identity, Generator>> generatorFactories) { - this(jdbcEnvironment, new BasicValue(metadataBuildingContext), generatorFactories); - } + protected BasicValueIdCreator(JdbcEnvironment jdbcEnvironment - , BasicValue prototypeBasicValue - , Map<String, BiFunction<GeneratorCreationContext - , Identity - , Generator>> generatorFactories) { - this.generatorFactories = generatorFactories; + , BasicValue prototypeBasicValue + , GrailsSequenceWrapper grailsSequenceWrapper) { this.jdbcEnvironment = jdbcEnvironment; this.id = prototypeBasicValue; + this.grailsSequenceWrapper = grailsSequenceWrapper; } - private void initializeGeneratorFactories() { - generatorFactories.put("identity", (context, mappedId) -> new GrailsIdentityGenerator(context, mappedId)); - - BiFunction<GeneratorCreationContext, Identity, Generator> sequenceFactory = (context, mappedId) -> new GrailsSequenceStyleGenerator(context, mappedId, jdbcEnvironment); - generatorFactories.put("sequence", sequenceFactory); - generatorFactories.put("sequence-identity", sequenceFactory); - - generatorFactories.put("increment", (context, mappedId) -> new GrailsIncrementGenerator(context, mappedId, jdbcEnvironment, domainClass)); - generatorFactories.put("uuid", (context, mappedId) -> new UuidGenerator(context.getType().getReturnedClass())); - generatorFactories.put("uuid2", (context, mappedId) -> new UuidGenerator(context.getType().getReturnedClass())); - generatorFactories.put("assigned", (context, mappedId) -> new Assigned()); - generatorFactories.put("table", (context, mappedId) -> new GrailsTableGenerator(context, mappedId, jdbcEnvironment)); - generatorFactories.put("enhanced-table", (context, mappedId) -> new GrailsTableGenerator(context,mappedId, jdbcEnvironment)); - generatorFactories.put("hilo", (context, mappedId) -> new SequenceStyleGenerator()); - } public BasicValue getBasicValueId(RootClass entity, Identity mappedId, boolean useSequence) { // create a BasicValue for the specific entity table (do not reuse the prototype directly because table differs) @@ -74,14 +52,14 @@ public class BasicValueIdCreator { } private Generator createGenerator(Identity mappedId, GeneratorCreationContext context, String generatorName) { - return generatorFactories.getOrDefault(generatorName, (ctx, mid) -> new GrailsNativeGenerator(ctx)) - .apply(context, mappedId); + return grailsSequenceWrapper.getGenerator(generatorName, context, mappedId, domainClass, jdbcEnvironment); } private String determineGeneratorName(Identity mappedId, boolean useSequence) { return Optional.ofNullable(mappedId) .map(Identity::getGenerator) - .filter(gen -> !("native".equals(gen) && useSequence)) - .orElse(useSequence ? "sequence-identity" : "native"); + .filter(gen -> !(GrailsSequenceGeneratorEnum.NATIVE.toString().equals(gen) && useSequence)) + .orElse(useSequence ? GrailsSequenceGeneratorEnum.SEQUENCE_IDENTITY.toString() : GrailsSequenceGeneratorEnum.NATIVE.toString()); } -} \ No newline at end of file +} + diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java index 1915843039..5ce103c5d8 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java @@ -18,6 +18,7 @@ import org.grails.orm.hibernate.cfg.GrailsHibernatePersistentEntity; 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.generator.GrailsSequenceGeneratorEnum; public class SimpleValueBinder { @@ -28,7 +29,7 @@ public class SimpleValueBinder { private final TypeNameProvider typeNameProvider; - private static final String SEQUENCE_KEY = "sequence"; + private static final String SEQUENCE_KEY = GrailsSequenceGeneratorEnum.SEQUENCE.toString(); public SimpleValueBinder(PersistentEntityNamingStrategy namingStrategy) { this(namingStrategy, new PersistentPropertyToPropertyConfig()); @@ -101,13 +102,14 @@ public class SimpleValueBinder { generatorProps.put(SequenceStyleGenerator.SEQUENCE_PARAM, generatorProps.getProperty(SEQUENCE_KEY)); } - switch (generator) { - case "identity" -> basicValue.setCustomIdGeneratorCreator(context -> { + if (GrailsSequenceGeneratorEnum.IDENTITY.toString().equals(generator)) { + basicValue.setCustomIdGeneratorCreator(context -> { var gen = new org.hibernate.id.IdentityGenerator(); context.getProperty().getValue().getColumns().get(0).setIdentity(true); return gen; }); - case "sequence", "sequence-identity" -> basicValue.setCustomIdGeneratorCreator(context -> { + } else if (GrailsSequenceGeneratorEnum.SEQUENCE.toString().equals(generator) || GrailsSequenceGeneratorEnum.SEQUENCE_IDENTITY.toString().equals(generator)) { + basicValue.setCustomIdGeneratorCreator(context -> { var gen = new org.hibernate.id.enhanced.SequenceStyleGenerator(); gen.configure(context.getType(), generatorProps, context.getServiceRegistry()); return gen; diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreatorSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreatorSpec.groovy index a474792a9b..7ed64f7fb4 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreatorSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/BasicValueIdCreatorSpec.groovy @@ -1,5 +1,7 @@ package org.grails.orm.hibernate.cfg.domainbinding +import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment + import grails.gorm.specs.HibernateGormDatastoreSpec import org.grails.orm.hibernate.cfg.Identity import org.hibernate.boot.spi.MetadataBuildingContext @@ -10,23 +12,29 @@ import org.hibernate.mapping.RootClass import org.hibernate.mapping.Table import spock.lang.Unroll -import java.util.function.BiFunction +import org.grails.orm.hibernate.cfg.domainbinding.generator.GrailsSequenceWrapper +import org.grails.orm.hibernate.cfg.domainbinding.generator.GrailsSequenceGeneratorEnum class BasicValueIdCreatorSpec extends HibernateGormDatastoreSpec { MetadataBuildingContext metadataBuildingContext BasicValueIdCreator creator + BasicValue basicValue RootClass entity Table table - Map<String, BiFunction<GeneratorCreationContext, Identity, Generator>> generatorFactories + GrailsSequenceWrapper grailsSequenceWrapper + JdbcEnvironment jdbcEnvironment def setup() { metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() - generatorFactories = [:] - creator = new BasicValueIdCreator(metadataBuildingContext, getGrailsDomainBinder().getJdbcEnvironment(), generatorFactories) + jdbcEnvironment = Mock(JdbcEnvironment) + grailsSequenceWrapper = Mock(GrailsSequenceWrapper) entity = new RootClass(metadataBuildingContext) table = new Table("test_table") entity.setTable(table) + // Use a real BasicValue to test that the generator creator lambda is correctly set and executable + basicValue = new BasicValue(metadataBuildingContext, table) + creator = new BasicValueIdCreator(jdbcEnvironment, basicValue, grailsSequenceWrapper) } @Unroll @@ -35,80 +43,75 @@ class BasicValueIdCreatorSpec extends HibernateGormDatastoreSpec { Identity mappedId = new Identity() mappedId.setGenerator(generatorName) def mockGenerator = Mock(Generator) - generatorFactories.put(generatorName, { ctx, mid -> mockGenerator } as BiFunction) + def context = Mock(GeneratorCreationContext) when: BasicValue id = creator.getBasicValueId(entity, mappedId, useSequence) def generatorCreator = id.getCustomIdGeneratorCreator() - Generator generator = generatorCreator.createGenerator(Mock(GeneratorCreationContext)) + Generator generator = generatorCreator.createGenerator(context) then: + 1 * grailsSequenceWrapper.getGenerator(generatorName, context, mappedId, _, jdbcEnvironment) >> mockGenerator generator == mockGenerator where: - generatorName | useSequence - "identity" | false - "sequence" | true - "sequence-identity" | true - "increment" | false - "uuid" | false - "uuid2" | false - "assigned" | false - "table" | false - "enhanced-table" | false - "hilo" | false + generatorName | useSequence + GrailsSequenceGeneratorEnum.IDENTITY.toString() | false + GrailsSequenceGeneratorEnum.SEQUENCE.toString() | true + GrailsSequenceGeneratorEnum.SEQUENCE_IDENTITY.toString() | true + GrailsSequenceGeneratorEnum.INCREMENT.toString() | false + GrailsSequenceGeneratorEnum.UUID.toString() | false + GrailsSequenceGeneratorEnum.UUID2.toString() | false + GrailsSequenceGeneratorEnum.ASSIGNED.toString() | false + GrailsSequenceGeneratorEnum.TABLE.toString() | false + GrailsSequenceGeneratorEnum.ENHANCED_TABLE.toString() | false + GrailsSequenceGeneratorEnum.HILO.toString() | false } def "should default to native generator when mappedId is null"() { given: - // Native generator is the default, not in the map passed to constructor usually, - // but here we are testing the logic inside getBasicValueId that selects the key. - // If mappedId is null and useSequence is false, it selects "native". - // "native" is NOT in the map by default in my test setup (empty map). - // So it falls back to default lambda in getBasicValueId: (ctx, mid) -> new GrailsNativeGenerator(ctx) - // We can't easily mock the default lambda unless we change the code to look up "native" in the map too. - // The code uses getOrDefault(generator, default). - // If generator is "native", and "native" is NOT in map, it uses default. - // Let's put "native" in the map to verify it selects "native". def mockGenerator = Mock(Generator) - generatorFactories.put("native", { ctx, mid -> mockGenerator } as BiFunction) + def context = Mock(GeneratorCreationContext) when: BasicValue id = creator.getBasicValueId(entity, null, false) def generatorCreator = id.getCustomIdGeneratorCreator() - Generator generator = generatorCreator.createGenerator(Mock(GeneratorCreationContext)) + Generator generator = generatorCreator.createGenerator(context) then: + 1 * grailsSequenceWrapper.getGenerator(GrailsSequenceGeneratorEnum.NATIVE.toString(), context, null, _, jdbcEnvironment) >> mockGenerator generator == mockGenerator } def "should default to sequence-identity when mappedId is null and useSequence is true"() { given: def mockGenerator = Mock(Generator) - generatorFactories.put("sequence-identity", { ctx, mid -> mockGenerator } as BiFunction) + def context = Mock(GeneratorCreationContext) when: BasicValue id = creator.getBasicValueId(entity, null, true) def generatorCreator = id.getCustomIdGeneratorCreator() - Generator generator = generatorCreator.createGenerator(Mock(GeneratorCreationContext)) + Generator generator = generatorCreator.createGenerator(context) then: + 1 * grailsSequenceWrapper.getGenerator(GrailsSequenceGeneratorEnum.SEQUENCE_IDENTITY.toString(), context, null, _, jdbcEnvironment) >> mockGenerator generator == mockGenerator } def "should use sequence-identity when generator is native and useSequence is true"() { given: Identity mappedId = new Identity() - mappedId.setGenerator("native") + mappedId.setGenerator(GrailsSequenceGeneratorEnum.NATIVE.toString()) def mockGenerator = Mock(Generator) - generatorFactories.put("sequence-identity", { ctx, mid -> mockGenerator } as BiFunction) + def context = Mock(GeneratorCreationContext) when: BasicValue id = creator.getBasicValueId(entity, mappedId, true) def generatorCreator = id.getCustomIdGeneratorCreator() - Generator generator = generatorCreator.createGenerator(Mock(GeneratorCreationContext)) + Generator generator = generatorCreator.createGenerator(context) then: + 1 * grailsSequenceWrapper.getGenerator(GrailsSequenceGeneratorEnum.SEQUENCE_IDENTITY.toString(), context, mappedId, _, jdbcEnvironment) >> mockGenerator generator == mockGenerator } @@ -116,18 +119,27 @@ class BasicValueIdCreatorSpec extends HibernateGormDatastoreSpec { given: Identity mappedId = new Identity() mappedId.setGenerator("custom") - Identity capturedId = null - generatorFactories.put("custom", { ctx, mid -> - capturedId = mid - return Mock(Generator) - } as BiFunction) + def context = Mock(GeneratorCreationContext) when: BasicValue id = creator.getBasicValueId(entity, mappedId, false) def generatorCreator = id.getCustomIdGeneratorCreator() - generatorCreator.createGenerator(Mock(GeneratorCreationContext)) + generatorCreator.createGenerator(context) + + then: + 1 * grailsSequenceWrapper.getGenerator("custom", context, mappedId, _, jdbcEnvironment) >> Mock(Generator) + } + + def "should set entity name on mappedId if it is null"() { + given: + Identity mappedId = new Identity() + mappedId.setGenerator(GrailsSequenceGeneratorEnum.IDENTITY.toString()) + entity.setEntityName("MyEntity") + + when: + creator.getBasicValueId(entity, mappedId, false) then: - capturedId == mappedId + mappedId.getName() == "MyEntity" } }
