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 b6eec1bf942ead0fdc40827b1ce9f51fdf543fb6 Author: Walter B Duque de Estrada <[email protected]> AuthorDate: Thu Dec 18 22:59:03 2025 -0600 Now creating ids natively if not assigned as default --- grails-data-hibernate7/core/build.gradle | 3 + .../orm/hibernate/HibernateGormInstanceApi.groovy | 19 +++--- .../orm/hibernate/cfg/GrailsDomainBinder.java | 70 ++++++++++++++-------- .../cfg/domainbinding/GrailsNativeGenerator.java | 34 +++++++++++ 4 files changed, 89 insertions(+), 37 deletions(-) diff --git a/grails-data-hibernate7/core/build.gradle b/grails-data-hibernate7/core/build.gradle index 0f7a117d56..1b63057321 100644 --- a/grails-data-hibernate7/core/build.gradle +++ b/grails-data-hibernate7/core/build.gradle @@ -65,6 +65,9 @@ dependencies { api( 'org.jboss:jandex:3.2.3') + compileOnly "org.hibernate.orm:hibernate-core:$hibernate7Version" + + compileOnly 'org.hibernate.orm:hibernate-jcache', { exclude group:'commons-collections', module:'commons-collections' exclude group:'commons-logging', module:'commons-logging' diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/HibernateGormInstanceApi.groovy b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/HibernateGormInstanceApi.groovy index 94809186d1..debea37937 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/HibernateGormInstanceApi.groovy +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/HibernateGormInstanceApi.groovy @@ -17,6 +17,8 @@ package org.grails.orm.hibernate import org.codehaus.groovy.runtime.InvokerHelper +import jakarta.persistence.GenerationType + import org.hibernate.engine.spi.SessionFactoryImplementor import org.hibernate.generator.Assigned import org.hibernate.generator.Generator @@ -151,19 +153,11 @@ class HibernateGormInstanceApi<D> extends GormInstanceApi<D> { validateable.skipValidation(true) try { - - Serializable idVal = (Serializable) InvokerHelper.getProperty(target, "id") - if (!idVal) { - if (isAssignedId(persistentEntity)) { - def id = nextId() - InvokerHelper.setProperty(target, "id", id) - return performPersist(target, shouldFlush) - } else { - return performPersist(target, shouldFlush) - } - } else if (shouldInsert(arguments)) { + String idPropertyName = domainClass.identity?.name ?: "id" + Object idVal = InvokerHelper.getProperty(target, idPropertyName) + if (idVal == null) { return performPersist(target, shouldFlush) - } else { + } else { return performMerge(target, shouldFlush) } } finally { @@ -171,6 +165,7 @@ class HibernateGormInstanceApi<D> extends GormInstanceApi<D> { } } + private boolean isAssignedId(PersistentEntity entity) { return ((SessionFactoryImplementor) sessionFactory) .getMappingMetamodel() diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java index e41962e4a4..5ff60e052e 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java @@ -2048,42 +2048,62 @@ public class GrailsDomainBinder private void bindSimpleId(PersistentProperty identifier, RootClass entity, InFlightMetadataCollector mappings, Identity mappedId, String sessionFactoryBeanName) { - Mapping mapping = new HibernateEntityWrapper().getMappedForm(identifier.getOwner()); - boolean useSequence = mapping.isTablePerConcreteClass(); - + boolean useSequence = new HibernateEntityWrapper().getMappedForm(identifier.getOwner()).isTablePerConcreteClass(); // create the id value BasicValue id = new BasicValue(metadataBuildingContext, entity.getTable()); - Property idProperty = new Property(); - idProperty.setName(identifier.getName()); - idProperty.setValue(id); - entity.setDeclaredIdentifierProperty(idProperty); - // set identifier on entity - - Properties params = new Properties(); - entity.setIdentifier(id); + String generator; if (mappedId == null) { -// id.se -// id.setIdentifierGeneratorStrategy(useSequence ? "sequence-identity" : "native"); + generator = useSequence ? "sequence-identity" : "native"; } else { - params.putAll(mappedId.getParams()); - if(params.containsKey(SEQUENCE_KEY)) { - params.put(SequenceStyleGenerator.SEQUENCE_PARAM, params.getProperty(SEQUENCE_KEY)); - } - - if (id.getCustomIdGeneratorCreator().isAssigned()) { - id.setNullValue("undefined"); + generator = mappedId.getGenerator(); + if ("native".equals(generator) && useSequence) { + generator = "sequence-identity"; } } - String schemaName = new NamespaceNameExtractor().getSchemaName(mappings); - String catalogName = new NamespaceNameExtractor().getCatalogName(mappings); + switch (generator) { + case "identity" -> id.setCustomIdGeneratorCreator(context -> { + // Force IdentityGenerator for databases like MySQL/H2 + var gen = new org.hibernate.id.IdentityGenerator(); + context.getProperty().getValue().getColumns().get(0).setIdentity(true); + return gen; + }); - IdentifierHelper identifierHelper = getJdbcEnvironment().getIdentifierHelper(); - params.put(IDENTIFIER_NORMALIZER, identifierHelper); + case "sequence", "sequence-identity" -> id.setCustomIdGeneratorCreator(context -> { + // Use the modern SequenceStyleGenerator + return new org.hibernate.id.enhanced.SequenceStyleGenerator(); + }); + case "increment" -> id.setCustomIdGeneratorCreator(context -> { + return new org.hibernate.id.IncrementGenerator(); + }); - // bind value + case "uuid", "uuid2" -> id.setCustomIdGeneratorCreator(context -> { + return new org.hibernate.id.uuid.UuidGenerator(context.getType().getReturnedClass()); + }); + + case "assigned" -> id.setCustomIdGeneratorCreator(context -> { + return new org.hibernate.id.Assigned(); + }); + + case "table", "enhanced-table" -> id.setCustomIdGeneratorCreator(context -> { + return new org.hibernate.id.enhanced.TableGenerator(); + }); + + case "hilo" -> id.setCustomIdGeneratorCreator(context -> { + // Note: Legacy Hilo is often replaced by SequenceStyleGenerator with optimizer + return new org.hibernate.id.enhanced.SequenceStyleGenerator(); + }); + + default -> id.setCustomIdGeneratorCreator(GrailsNativeGenerator::new); + } + + Property idProperty = new Property(); + idProperty.setName(identifier.getName()); + idProperty.setValue(id); + entity.setDeclaredIdentifierProperty(idProperty); + entity.setIdentifier(id); // set type new SimpleValueBinder(namingStrategy).bindSimpleValue(identifier, null, id, EMPTY_PATH); diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsNativeGenerator.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsNativeGenerator.java new file mode 100644 index 0000000000..c03cfee2d9 --- /dev/null +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsNativeGenerator.java @@ -0,0 +1,34 @@ +package org.grails.orm.hibernate.cfg.domainbinding; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.generator.EventType; +import org.hibernate.generator.GeneratorCreationContext; +import org.hibernate.id.NativeGenerator; +import jakarta.persistence.GenerationType; + +public class GrailsNativeGenerator extends NativeGenerator { + + public GrailsNativeGenerator(GeneratorCreationContext context) { + // This triggers the internal switch logic you provided earlier, + // which calls setIdentity(true) on the column for H2. + this.initialize(null, null, context); + } + + @Override + public Object generate(SharedSessionContractImplementor session, Object entity, Object currentValue, EventType eventType) { + // 1. Support Grails assigned identifiers + if (currentValue != null) { + return currentValue; + } + + // 2. Fix the Hibernate 7 ClassCastException + // NativeGenerator.generate() tries to cast the delegate to BeforeExecutionGenerator. + // If the dialect chose IDENTITY, that cast fails. We bypass it by returning null. + if (this.getGenerationType() == GenerationType.IDENTITY) { + return null; + } + + // 3. For Sequences/UUIDs, delegate to the standard logic + return super.generate(session, entity, currentValue, eventType); + } +} \ No newline at end of file
