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 9441d2ed2605c82ac3b1317894f7e3f04e1e399e Author: Walter Duque de Estrada <[email protected]> AuthorDate: Mon Mar 2 08:48:21 2026 -0600 Introduce HibernateEnumProperty marker interface; replace isEnumType() checks with instanceof --- .../domainbinding/binder/GrailsPropertyBinder.java | 18 +++----- .../cfg/domainbinding/binder/PropertyBinder.java | 3 +- .../hibernate/HibernateCustomEnumProperty.java | 40 +++++++++++++++++ .../hibernate/HibernateEnumProperty.java | 35 +++++++++++++++ .../hibernate/HibernateMappingFactory.groovy | 8 +++- .../hibernate/HibernateSimpleEnumProperty.java | 35 +++++++++++++++ .../util/PropertyFromValueCreator.java | 3 +- .../gorm/specs/HibernateMappingFactorySpec.groovy | 51 +++++++++++++++++++++- .../domainbinding/GrailsPropertyBinderSpec.groovy | 10 ++++- 9 files changed, 184 insertions(+), 19 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java index 9bb67de3f4..63e5c95542 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/GrailsPropertyBinder.java @@ -20,8 +20,7 @@ package org.grails.orm.hibernate.cfg.domainbinding.binder; import jakarta.annotation.Nonnull; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty; +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEnumProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToManyProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty; @@ -76,25 +75,20 @@ public class GrailsPropertyBinder { Value value = null; // 1. Create Value and apply binders (consolidated block) - if (currentGrailsProp.isEnumType()) { - // HibernateEnumTypeProperty + if (currentGrailsProp instanceof HibernateEnumProperty) { value = enumTypeBinder.bindEnumType(currentGrailsProp, currentGrailsProp.getType(), table, path); - } else if (currentGrailsProp instanceof HibernateOneToOneProperty oneToOne) { - // HibernateOneToOneProperty - if (oneToOne.isHibernateOneToOne()) { + } else if (currentGrailsProp instanceof HibernateToOneProperty toOne) { + if (toOne.isHibernateOneToOne()) { value = oneToOneBinder.bindOneToOne( - (org.grails.datastore.mapping.model.types.OneToOne) currentGrailsProp, + (org.grails.datastore.mapping.model.types.OneToOne) toOne, persistentClass, table, path); } else { - value = - manyToOneBinder.bindManyToOne((HibernateToOneProperty) currentGrailsProp, table, path); + value = manyToOneBinder.bindManyToOne(toOne, table, path); } - } else if (currentGrailsProp instanceof HibernateManyToOneProperty manyToOne) { - value = manyToOneBinder.bindManyToOne(manyToOne, table, path); } else if (currentGrailsProp instanceof HibernateToManyProperty toMany && !currentGrailsProp.isSerializableType()) { // HibernateToManyProperty diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/PropertyBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/PropertyBinder.java index f1416f090d..f1bbf1d290 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/PropertyBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/PropertyBinder.java @@ -25,6 +25,7 @@ import org.grails.datastore.mapping.reflect.EntityReflector; import org.grails.orm.hibernate.access.TraitPropertyAccessStrategy; import org.grails.orm.hibernate.cfg.PropertyConfig; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateAssociation; +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEnumProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty; import org.grails.orm.hibernate.cfg.domainbinding.util.CascadeBehaviorFetcher; import org.hibernate.boot.spi.AccessType; @@ -84,7 +85,7 @@ public class PropertyBinder { prop.setOptional(persistentProperty.isNullable()); if (persistentProperty instanceof Association<?> association - && !persistentProperty.isEnumType()) { + && !(persistentProperty instanceof HibernateEnumProperty)) { prop.setCascade(cascadeBehaviorFetcher.getCascadeBehaviour(association)); } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateCustomEnumProperty.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateCustomEnumProperty.java new file mode 100644 index 0000000000..0ed1ac89b4 --- /dev/null +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateCustomEnumProperty.java @@ -0,0 +1,40 @@ +/* + * 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.hibernate; + +import java.beans.PropertyDescriptor; +import org.grails.datastore.mapping.engine.types.CustomTypeMarshaller; +import org.grails.datastore.mapping.model.MappingContext; +import org.grails.datastore.mapping.model.PersistentEntity; + +/** + * Hibernate custom property whose Java type is an enum backed by a registered {@link + * CustomTypeMarshaller}. Created by {@link HibernateMappingFactory#createCustom} when {@code + * pd.propertyType.isEnum()} is true and a matching marshaller is found. + */ +public class HibernateCustomEnumProperty extends HibernateCustomProperty + implements HibernateEnumProperty { + public HibernateCustomEnumProperty( + PersistentEntity entity, + MappingContext context, + PropertyDescriptor property, + CustomTypeMarshaller<?, ?, ?> customTypeMarshaller) { + super(entity, context, property, customTypeMarshaller); + } +} diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateEnumProperty.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateEnumProperty.java new file mode 100644 index 0000000000..a471622545 --- /dev/null +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateEnumProperty.java @@ -0,0 +1,35 @@ +/* + * 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.hibernate; + +/** + * Marker interface for Hibernate persistent properties whose Java type is an enum. + * + * <p>Two concrete subtypes exist, corresponding to the two creation paths in {@link + * HibernateMappingFactory}: + * + * <ul> + * <li>{@link HibernateSimpleEnumProperty} — plain enum with no custom type marshaller + * <li>{@link HibernateCustomEnumProperty} — enum backed by a custom type marshaller + * </ul> + * + * <p>Use {@code instanceof HibernateEnumProperty} instead of {@code isEnumType()} to branch on + * enum properties at binding time. + */ +public interface HibernateEnumProperty extends HibernatePersistentProperty {} diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingFactory.groovy b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingFactory.groovy index 2b70647979..dc1a80c224 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingFactory.groovy +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingFactory.groovy @@ -80,7 +80,9 @@ class HibernateMappingFactory extends AbstractGormMappingFactory<Mapping, Proper if (customTypeMarshaller == null && propertyType.isEnum()) { customTypeMarshaller = findCustomType(context, Enum) } - HibernateCustomProperty custom = new HibernateCustomProperty(owner, context, pd, customTypeMarshaller) + HibernateCustomProperty custom = propertyType.isEnum() + ? new HibernateCustomEnumProperty(owner, context, pd, customTypeMarshaller) + : new HibernateCustomProperty(owner, context, pd, customTypeMarshaller) custom.setMapping(createPropertyMapping(custom, owner)) custom } @@ -88,7 +90,9 @@ class HibernateMappingFactory extends AbstractGormMappingFactory<Mapping, Proper @Override Simple<PropertyConfig> createSimple( PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { - HibernateSimpleProperty simple = new HibernateSimpleProperty(owner, context, pd) + HibernateSimpleProperty simple = pd.propertyType.isEnum() + ? new HibernateSimpleEnumProperty(owner, context, pd) + : new HibernateSimpleProperty(owner, context, pd) simple.setMapping(createPropertyMapping(simple, owner)) simple } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateSimpleEnumProperty.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateSimpleEnumProperty.java new file mode 100644 index 0000000000..3e0a09d221 --- /dev/null +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateSimpleEnumProperty.java @@ -0,0 +1,35 @@ +/* + * 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.hibernate; + +import java.beans.PropertyDescriptor; +import org.grails.datastore.mapping.model.MappingContext; +import org.grails.datastore.mapping.model.PersistentEntity; + +/** + * Hibernate simple property whose Java type is an enum (no custom type marshaller). Created by + * {@link HibernateMappingFactory#createSimple} when {@code pd.propertyType.isEnum()} is true. + */ +public class HibernateSimpleEnumProperty extends HibernateSimpleProperty + implements HibernateEnumProperty { + public HibernateSimpleEnumProperty( + PersistentEntity entity, MappingContext context, PropertyDescriptor property) { + super(entity, context, property); + } +} diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/util/PropertyFromValueCreator.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/util/PropertyFromValueCreator.java index 916bd13837..cb6795e0d4 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/util/PropertyFromValueCreator.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/util/PropertyFromValueCreator.java @@ -19,6 +19,7 @@ package org.grails.orm.hibernate.cfg.domainbinding.util; import org.grails.orm.hibernate.cfg.domainbinding.binder.PropertyBinder; +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEnumProperty; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty; import org.hibernate.mapping.Property; import org.hibernate.mapping.Value; @@ -37,7 +38,7 @@ public class PropertyFromValueCreator { public Property createProperty(Value value, HibernatePersistentProperty grailsProperty) { // set type - if (!grailsProperty.isEnumType()) { + if (!(grailsProperty instanceof HibernateEnumProperty)) { value.setTypeUsingReflection(grailsProperty.getOwnerClassName(), grailsProperty.getName()); } diff --git a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateMappingFactorySpec.groovy b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateMappingFactorySpec.groovy index 66863795a0..3e9db5e60f 100644 --- a/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateMappingFactorySpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateMappingFactorySpec.groovy @@ -39,7 +39,7 @@ class HibernateMappingFactorySpec extends HibernateGormDatastoreSpec { def setupSpec() { manager.addAllDomainClasses([MappingFactoryBook, MappingFactoryAuthor, MappingFactoryTag, - MappingFactoryArticle]) + MappingFactoryArticle, MappingFactoryEnumBook]) } // --- unit-style tests (standalone factory) --- @@ -167,6 +167,29 @@ class HibernateMappingFactorySpec extends HibernateGormDatastoreSpec { addrProp instanceof HibernateEmbeddedProperty } + void "createSimple creates HibernateSimpleEnumProperty for a plain enum field"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryEnumBook.name) + def statusProp = entity.persistentProperties.find { it.name == 'status' } + + then: + statusProp instanceof HibernateSimpleEnumProperty + } + + void "createCustom creates HibernateCustomEnumProperty for an enum field with a registered marshaller"() { + given: + HibernateConnectionSourceSettings settings = new HibernateConnectionSourceSettings() + settings.custom.types = [new MappingFactoryEnumMarshaller()] + def ctx = new HibernateMappingContext(settings) + PersistentEntity entity = ctx.addPersistentEntity(MappingFactoryCustomEnumBook) + + when: + def statusProp = entity.persistentProperties.find { it.name == 'status' } + + then: + statusProp instanceof HibernateCustomEnumProperty + } + @Rollback void "factory-created entities can be persisted and retrieved"() { when: @@ -234,3 +257,29 @@ class FactoryTypeMarshaller extends AbstractMappingAwareCustomTypeMarshaller { @Override protected Object readInternal(PersistentProperty property, String key, Object nativeSource) { nativeSource } } + +enum MappingFactoryBookStatus { AVAILABLE, CHECKED_OUT } + +@Entity +class MappingFactoryEnumBook implements HibernateEntity<MappingFactoryEnumBook> { + String title + MappingFactoryBookStatus status +} + +@Entity +class MappingFactoryCustomEnumBook implements HibernateEntity<MappingFactoryCustomEnumBook> { + String title + MappingFactoryBookStatus status +} + +class MappingFactoryEnumMarshaller extends AbstractMappingAwareCustomTypeMarshaller { + MappingFactoryEnumMarshaller() { super(MappingFactoryBookStatus) } + + @Override + protected Object writeInternal(PersistentProperty property, String key, Object value, Object nativeTarget) { value?.name() } + + @Override + protected Object readInternal(PersistentProperty property, String key, Object nativeSource) { + nativeSource ? MappingFactoryBookStatus.valueOf(nativeSource.toString()) : null + } +} diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy index b76048a4d6..2989687ecb 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/GrailsPropertyBinderSpec.groovy @@ -18,6 +18,7 @@ import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateBasicProper import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateSimpleEnumProperty import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateSimpleProperty import org.grails.orm.hibernate.cfg.domainbinding.binder.CollectionBinder import org.grails.orm.hibernate.cfg.domainbinding.binder.ClassBinder @@ -98,6 +99,12 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { } } + abstract static class TestSimpleEnum extends HibernateSimpleEnumProperty { + TestSimpleEnum(PersistentEntity owner, MappingContext context, java.beans.PropertyDescriptor descriptor) { + super(owner, context, descriptor); + } + } + private void setupProperty(PersistentProperty prop, String name, Mapping mapping, PersistentEntity owner) { prop.getName() >> name _ * prop.getOwner() >> owner @@ -249,10 +256,9 @@ class GrailsPropertyBinderSpec extends HibernateGormDatastoreSpec { rootClass.setEntityName(persistentEntity.name) rootClass.setTable(collector.addTable(null, null, "ENUM_BOOK", null, false, binder.getMetadataBuildingContext())) - def statusProp = Mock(TestBasic) + def statusProp = Mock(TestSimpleEnum) setupProperty(statusProp, "status", new Mapping(), persistentEntity) statusProp.getType() >> java.util.concurrent.TimeUnit - statusProp.isEnumType() >> true statusProp.isHibernateOneToOne() >> false statusProp.isHibernateManyToOne() >> false
