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 668a0af1c199aaa9f053e692b86e80ce33b0e67a Author: Walter Duque de Estrada <[email protected]> AuthorDate: Sat Feb 21 22:33:38 2026 -0600 refactor HibernateMappingFactory to standalone class --- .../orm/hibernate/cfg/HibernateMappingContext.java | 261 ++------------------- .../hibernate/HibernateMappingFactory.java | 200 ++++++++++++++++ .../gorm/specs/HibernateMappingFactorySpec.groovy | 236 +++++++++++++++++++ .../cfg/HibernateMappingContextSpec.groovy | 4 +- 4 files changed, 454 insertions(+), 247 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContext.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContext.java index e8d86775e8..f23f9d443b 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContext.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContext.java @@ -1,61 +1,32 @@ /* - * 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 + * 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 + * 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. + * 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; import grails.gorm.hibernate.HibernateEntity; import groovy.lang.Closure; - -import java.beans.PropertyDescriptor; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.grails.datastore.gorm.GormEntity; -import org.grails.datastore.mapping.config.AbstractGormMappingFactory; -import org.grails.datastore.mapping.config.Property; -import org.grails.datastore.mapping.config.groovy.MappingConfigurationBuilder; -import org.grails.datastore.mapping.engine.types.CustomTypeMarshaller; import org.grails.datastore.mapping.model.*; -import org.grails.datastore.mapping.reflect.ClassUtils; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsJpaMappingConfigurationStrategy; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateBasicProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateCustomProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedCollectionProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedPersistentEntity; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateEmbeddedProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentity; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentityMapping; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateIdentityProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToManyProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateManyToOneProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateMappingBuilder; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToManyProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentEntity; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateSimpleProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateTenantIdProperty; +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.*; import org.grails.orm.hibernate.connections.HibernateConnectionSourceSettings; import org.grails.orm.hibernate.proxy.HibernateProxyHandler; /** - * A Mapping context for Hibernate + * A Mapping context for Hibernate optimized for Java to Groovy conversion. * * @author Graeme Rocher * @since 5.0 @@ -65,22 +36,9 @@ public class HibernateMappingContext extends AbstractMappingContext { private final HibernateMappingFactory mappingFactory; private final MappingConfigurationStrategy syntaxStrategy; - /** - * Construct a HibernateMappingContext for the given arguments - * - * @param settings The {@link HibernateConnectionSourceSettings} settings - * @param contextObject The context object (for example a Spring ApplicationContext) - * @param persistentClasses The persistent classes - */ - public HibernateMappingContext( - HibernateConnectionSourceSettings settings, - Object contextObject, - Class... persistentClasses) { + public HibernateMappingContext(HibernateConnectionSourceSettings settings, Object contextObject, Class... persistentClasses) { this.mappingFactory = new HibernateMappingFactory(); - - // The mapping factory needs to be configured before initialize can be safely called initialize(settings); - if (settings != null) { this.mappingFactory.setDefaultMapping(settings.getDefault().getMapping()); this.mappingFactory.setDefaultConstraints(settings.getDefault().getConstraints()); @@ -91,8 +49,7 @@ public class HibernateMappingContext extends AbstractMappingContext { addPersistentEntities(persistentClasses); } - public HibernateMappingContext( - HibernateConnectionSourceSettings settings, Class... persistentClasses) { + public HibernateMappingContext(HibernateConnectionSourceSettings settings, Class... persistentClasses) { this(settings, null, persistentClasses); } @@ -100,11 +57,6 @@ public class HibernateMappingContext extends AbstractMappingContext { this(new HibernateConnectionSourceSettings()); } - /** - * Sets the default constraints to be used - * - * @param defaultConstraints The default constraints - */ public void setDefaultConstraints(Closure defaultConstraints) { this.mappingFactory.setDefaultConstraints(defaultConstraints); } @@ -132,8 +84,7 @@ public class HibernateMappingContext extends AbstractMappingContext { @Override protected boolean isValidMappingStrategy(Class javaClass, Object mappingStrategy) { - return HibernateEntity.class.isAssignableFrom(javaClass) - || super.isValidMappingStrategy(javaClass, mappingStrategy); + return HibernateEntity.class.isAssignableFrom(javaClass) || super.isValidMappingStrategy(javaClass, mappingStrategy); } @Override @@ -157,8 +108,7 @@ public class HibernateMappingContext extends AbstractMappingContext { return super.getPersistentEntity(name); } - public Collection<GrailsHibernatePersistentEntity> getHibernatePersistentEntities( - String dataSourceName) { + public Collection<GrailsHibernatePersistentEntity> getHibernatePersistentEntities(String dataSourceName) { List<GrailsHibernatePersistentEntity> result = new ArrayList<>(); if (persistentEntities != null) { for (PersistentEntity entity : persistentEntities) { @@ -170,183 +120,4 @@ public class HibernateMappingContext extends AbstractMappingContext { } return result; } - - class HibernateMappingFactory extends AbstractGormMappingFactory<Mapping, PropertyConfig> { - - public HibernateMappingFactory() {} - - @Override - protected MappingConfigurationBuilder createConfigurationBuilder( - PersistentEntity entity, Mapping mapping) { - return new HibernateMappingBuilder(mapping, entity.getName(), defaultConstraints); - } - - @Override - public org.grails.datastore.mapping.model.types.Identity<PropertyConfig> createIdentity( - PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { - HibernateIdentityProperty identity = new HibernateIdentityProperty(owner, context, pd); - PropertyMapping<PropertyConfig> propertyMapping = createPropertyMapping(identity, owner); - identity.setMapping(propertyMapping); - return identity; - } - - @Override - public org.grails.datastore.mapping.model.types.TenantId<PropertyConfig> createTenantId( - PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { - HibernateTenantIdProperty tenantId = new HibernateTenantIdProperty(owner, context, pd); - tenantId.setMapping(createDerivedPropertyMapping(tenantId, owner)); - return tenantId; - } - - @Override - public org.grails.datastore.mapping.model.types.Custom<PropertyConfig> createCustom( - PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { - final Class<?> propertyType = pd.getPropertyType(); - CustomTypeMarshaller customTypeMarshaller = findCustomType(context, propertyType); - if (customTypeMarshaller == null && propertyType.isEnum()) { - customTypeMarshaller = findCustomType(context, Enum.class); - } - HibernateCustomProperty custom = - new HibernateCustomProperty(owner, context, pd, customTypeMarshaller); - custom.setMapping(createPropertyMapping(custom, owner)); - return custom; - } - - @Override - public org.grails.datastore.mapping.model.types.Simple<PropertyConfig> createSimple( - PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { - HibernateSimpleProperty simple = new HibernateSimpleProperty(owner, context, pd); - simple.setMapping(createPropertyMapping(simple, owner)); - return simple; - } - - @Override - public org.grails.datastore.mapping.model.types.ToOne createOneToOne( - PersistentEntity entity, MappingContext context, PropertyDescriptor property) { - HibernateOneToOneProperty oneToOne = new HibernateOneToOneProperty(entity, context, property); - oneToOne.setMapping(createPropertyMapping(oneToOne, entity)); - return oneToOne; - } - - @Override - public org.grails.datastore.mapping.model.types.ToOne createManyToOne( - PersistentEntity entity, MappingContext context, PropertyDescriptor property) { - HibernateManyToOneProperty manyToOne = - new HibernateManyToOneProperty(entity, context, property); - manyToOne.setMapping(createPropertyMapping(manyToOne, entity)); - return manyToOne; - } - - @Override - public org.grails.datastore.mapping.model.types.OneToMany createOneToMany( - PersistentEntity entity, MappingContext context, PropertyDescriptor property) { - HibernateOneToManyProperty oneToMany = - new HibernateOneToManyProperty(entity, context, property); - oneToMany.setMapping(createPropertyMapping(oneToMany, entity)); - return oneToMany; - } - - @Override - public org.grails.datastore.mapping.model.types.ManyToMany createManyToMany( - PersistentEntity entity, MappingContext context, PropertyDescriptor property) { - HibernateManyToManyProperty manyToMany = - new HibernateManyToManyProperty(entity, context, property); - manyToMany.setMapping(createPropertyMapping(manyToMany, entity)); - return manyToMany; - } - - @Override - public org.grails.datastore.mapping.model.types.Embedded createEmbedded( - PersistentEntity entity, MappingContext context, PropertyDescriptor property) { - HibernateEmbeddedProperty embedded = new HibernateEmbeddedProperty(entity, context, property); - embedded.setMapping(createPropertyMapping(embedded, entity)); - return embedded; - } - - @Override - public org.grails.datastore.mapping.model.types.EmbeddedCollection createEmbeddedCollection( - PersistentEntity entity, MappingContext context, PropertyDescriptor property) { - HibernateEmbeddedCollectionProperty embedded = - new HibernateEmbeddedCollectionProperty(entity, context, property); - embedded.setMapping(createPropertyMapping(embedded, entity)); - return embedded; - } - - @Override - public org.grails.datastore.mapping.model.types.Basic createBasicCollection( - PersistentEntity entity, - MappingContext context, - PropertyDescriptor property, - Class collectionType) { - HibernateBasicProperty basic = - new HibernateBasicProperty((GrailsHibernatePersistentEntity) entity, context, property); - basic.setMapping(createPropertyMapping(basic, entity)); - - CustomTypeMarshaller customTypeMarshaller = - findCustomType(context, property.getPropertyType()); - if (collectionType != null && collectionType.isEnum()) { - customTypeMarshaller = findCustomType(context, collectionType); - if (customTypeMarshaller == null) { - customTypeMarshaller = findCustomType(context, Enum.class); - } - } - - if (customTypeMarshaller != null) { - basic.setCustomTypeMarshaller(customTypeMarshaller); - } - - return basic; - } - - @Override - public IdentityMapping createIdentityMapping(final ClassMapping classMapping) { - final Mapping mappedForm = (Mapping) createMappedForm(classMapping.getEntity()); - final HibernateIdentity identity = mappedForm.getIdentity(); - final ValueGenerator generator; - if (identity instanceof Identity) { - Identity id = (Identity) identity; - String generatorName = id.getGenerator(); - if (generatorName != null) { - ValueGenerator resolvedGenerator; - try { - resolvedGenerator = - ValueGenerator.valueOf(generatorName.toUpperCase(java.util.Locale.ENGLISH)); - } catch (IllegalArgumentException e) { - if (generatorName.equalsIgnoreCase("table")) { - resolvedGenerator = ValueGenerator.CUSTOM; - } else if (ClassUtils.isPresent(generatorName)) { - resolvedGenerator = ValueGenerator.CUSTOM; - } else { - throw new DatastoreConfigurationException( - "Invalid id generation strategy for entity [" - + classMapping.getEntity().getName() - + "]: " - + generatorName); - } - } - generator = resolvedGenerator; - } else { - generator = ValueGenerator.AUTO; - } - } else { - generator = ValueGenerator.AUTO; - } - return new HibernateIdentityMapping(identity, generator, classMapping); - } - - @Override - protected boolean allowArbitraryCustomTypes() { - return true; - } - - @Override - protected Class<PropertyConfig> getPropertyMappedFormType() { - return PropertyConfig.class; - } - - @Override - protected Class<Mapping> getEntityMappedFormType() { - return Mapping.class; - } - } -} +} \ No newline at end of file diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingFactory.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingFactory.java new file mode 100644 index 0000000000..7df263a4ef --- /dev/null +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateMappingFactory.java @@ -0,0 +1,200 @@ +/* + * 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 java.util.Locale; + +import org.grails.datastore.mapping.config.AbstractGormMappingFactory; +import org.grails.datastore.mapping.config.groovy.MappingConfigurationBuilder; +import org.grails.datastore.mapping.engine.types.CustomTypeMarshaller; +import org.grails.datastore.mapping.model.*; +import org.grails.datastore.mapping.model.types.*; +import org.grails.datastore.mapping.reflect.ClassUtils; +import org.grails.orm.hibernate.cfg.Identity; +import org.grails.orm.hibernate.cfg.Mapping; +import org.grails.orm.hibernate.cfg.PropertyConfig; + +/** + * The {@link AbstractGormMappingFactory} implementation for Hibernate, responsible for + * creating all Hibernate-specific persistent property and identity mapping instances. + */ +public class HibernateMappingFactory extends AbstractGormMappingFactory<Mapping, PropertyConfig> { + + public HibernateMappingFactory() { + } + + @Override + protected MappingConfigurationBuilder createConfigurationBuilder(PersistentEntity entity, Mapping mapping) { + return new HibernateMappingBuilder(mapping, entity.getName(), defaultConstraints); + } + + @Override + public org.grails.datastore.mapping.model.types.Identity<PropertyConfig> createIdentity( + PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { + HibernateIdentityProperty identity = new HibernateIdentityProperty(owner, context, pd); + identity.setMapping(createPropertyMapping(identity, owner)); + return identity; + } + + @Override + public TenantId<PropertyConfig> createTenantId( + PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { + HibernateTenantIdProperty tenantId = new HibernateTenantIdProperty(owner, context, pd); + tenantId.setMapping(createDerivedPropertyMapping(tenantId, owner)); + return tenantId; + } + + @Override + public Custom<PropertyConfig> createCustom( + PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { + final Class<?> propertyType = pd.getPropertyType(); + CustomTypeMarshaller customTypeMarshaller = findCustomType(context, propertyType); + if (customTypeMarshaller == null && propertyType.isEnum()) { + customTypeMarshaller = findCustomType(context, Enum.class); + } + HibernateCustomProperty custom = new HibernateCustomProperty(owner, context, pd, customTypeMarshaller); + custom.setMapping(createPropertyMapping(custom, owner)); + return custom; + } + + @Override + public Simple<PropertyConfig> createSimple( + PersistentEntity owner, MappingContext context, PropertyDescriptor pd) { + HibernateSimpleProperty simple = new HibernateSimpleProperty(owner, context, pd); + simple.setMapping(createPropertyMapping(simple, owner)); + return simple; + } + + @Override + public ToOne createOneToOne( + PersistentEntity entity, MappingContext context, PropertyDescriptor property) { + HibernateOneToOneProperty oneToOne = new HibernateOneToOneProperty(entity, context, property); + oneToOne.setMapping(createPropertyMapping(oneToOne, entity)); + return oneToOne; + } + + @Override + public ToOne createManyToOne( + PersistentEntity entity, MappingContext context, PropertyDescriptor property) { + HibernateManyToOneProperty manyToOne = new HibernateManyToOneProperty(entity, context, property); + manyToOne.setMapping(createPropertyMapping(manyToOne, entity)); + return manyToOne; + } + + @Override + public OneToMany createOneToMany( + PersistentEntity entity, MappingContext context, PropertyDescriptor property) { + HibernateOneToManyProperty oneToMany = new HibernateOneToManyProperty(entity, context, property); + oneToMany.setMapping(createPropertyMapping(oneToMany, entity)); + return oneToMany; + } + + @Override + public ManyToMany createManyToMany( + PersistentEntity entity, MappingContext context, PropertyDescriptor property) { + HibernateManyToManyProperty manyToMany = new HibernateManyToManyProperty(entity, context, property); + manyToMany.setMapping(createPropertyMapping(manyToMany, entity)); + return manyToMany; + } + + @Override + public Embedded createEmbedded( + PersistentEntity entity, MappingContext context, PropertyDescriptor property) { + HibernateEmbeddedProperty embedded = new HibernateEmbeddedProperty(entity, context, property); + embedded.setMapping(createPropertyMapping(embedded, entity)); + return embedded; + } + + @Override + public EmbeddedCollection createEmbeddedCollection( + PersistentEntity entity, MappingContext context, PropertyDescriptor property) { + HibernateEmbeddedCollectionProperty embedded = + new HibernateEmbeddedCollectionProperty(entity, context, property); + embedded.setMapping(createPropertyMapping(embedded, entity)); + return embedded; + } + + @Override + public Basic createBasicCollection( + PersistentEntity entity, MappingContext context, PropertyDescriptor property, Class collectionType) { + if (entity instanceof GrailsHibernatePersistentEntity ghpEntity) { + HibernateBasicProperty basic = new HibernateBasicProperty(ghpEntity, context, property); + basic.setMapping(createPropertyMapping(basic, entity)); + CustomTypeMarshaller customTypeMarshaller = findCustomType(context, property.getPropertyType()); + if (collectionType != null && collectionType.isEnum()) { + customTypeMarshaller = findCustomType(context, collectionType); + if (customTypeMarshaller == null) { + customTypeMarshaller = findCustomType(context, Enum.class); + } + } + if (customTypeMarshaller != null) { + basic.setCustomTypeMarshaller(customTypeMarshaller); + } + return basic; + } + return null; + } + + @Override + public IdentityMapping createIdentityMapping(final ClassMapping classMapping) { + final Mapping mappedForm = (Mapping) createMappedForm(classMapping.getEntity()); + final HibernateIdentity identity = mappedForm.getIdentity(); + final ValueGenerator generator; + + if (identity instanceof Identity id) { + String generatorName = id.getGenerator(); + if (generatorName != null) { + ValueGenerator resolvedGenerator; + try { + resolvedGenerator = ValueGenerator.valueOf(generatorName.toUpperCase(Locale.ENGLISH)); + } catch (IllegalArgumentException e) { + if (generatorName.equalsIgnoreCase("table") || ClassUtils.isPresent(generatorName)) { + resolvedGenerator = ValueGenerator.CUSTOM; + } else { + throw new DatastoreConfigurationException( + String.format("Invalid id generation strategy for entity [%s]: %s", + classMapping.getEntity().getName(), generatorName)); + } + } + generator = resolvedGenerator; + } else { + generator = ValueGenerator.AUTO; + } + } else { + generator = ValueGenerator.AUTO; + } + return new HibernateIdentityMapping(identity, generator, classMapping); + } + + @Override + protected boolean allowArbitraryCustomTypes() { + return true; + } + + @Override + protected Class<PropertyConfig> getPropertyMappedFormType() { + return PropertyConfig.class; + } + + @Override + protected Class<Mapping> getEntityMappedFormType() { + return Mapping.class; + } +} 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 new file mode 100644 index 0000000000..66863795a0 --- /dev/null +++ b/grails-data-hibernate7/core/src/test/groovy/grails/gorm/specs/HibernateMappingFactorySpec.groovy @@ -0,0 +1,236 @@ +/* + * 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 grails.gorm.specs + +import grails.gorm.annotation.Entity +import grails.gorm.hibernate.HibernateEntity +import grails.gorm.transactions.Rollback +import org.grails.datastore.mapping.engine.types.AbstractMappingAwareCustomTypeMarshaller +import org.grails.datastore.mapping.model.MappingContext +import org.grails.datastore.mapping.model.PersistentEntity +import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.datastore.mapping.model.ValueGenerator +import org.grails.datastore.mapping.model.types.* +import org.grails.orm.hibernate.cfg.HibernateMappingContext +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.* +import org.grails.orm.hibernate.connections.HibernateConnectionSourceSettings + +/** + * Spec for {@link HibernateMappingFactory}, verifying that it creates + * the correct Hibernate-specific property and identity mapping instances. + */ +class HibernateMappingFactorySpec extends HibernateGormDatastoreSpec { + + def setupSpec() { + manager.addAllDomainClasses([MappingFactoryBook, MappingFactoryAuthor, MappingFactoryTag, + MappingFactoryArticle]) + } + + // --- unit-style tests (standalone factory) --- + + void "factory can be instantiated standalone"() { + when: + def factory = new HibernateMappingFactory() + + then: + factory != null + factory.getPropertyMappedFormType() == org.grails.orm.hibernate.cfg.PropertyConfig + factory.getEntityMappedFormType() == org.grails.orm.hibernate.cfg.Mapping + } + + void "allowArbitraryCustomTypes returns true"() { + expect: + new HibernateMappingFactory().allowArbitraryCustomTypes() + } + + void "custom type marshaller is registered and detectable"() { + given: + HibernateConnectionSourceSettings settings = new HibernateConnectionSourceSettings() + settings.custom.types = [new FactoryTypeMarshaller(FactoryCustomType)] + def ctx = new HibernateMappingContext(settings) + + expect: + ctx.mappingFactory.isCustomType(FactoryCustomType) + } + + void "custom type marshaller is NOT registered for unrelated type"() { + given: + HibernateConnectionSourceSettings settings = new HibernateConnectionSourceSettings() + settings.custom.types = [new FactoryTypeMarshaller(FactoryCustomType)] + def ctx = new HibernateMappingContext(settings) + + expect: + !ctx.mappingFactory.isCustomType(String) + } + + // --- integration-style tests using live datastore --- + + void "mappingFactory is a HibernateMappingFactory"() { + expect: + mappingContext.mappingFactory instanceof HibernateMappingFactory + } + + void "createSimple produces HibernateSimpleProperty for a String field"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryBook.name) + def titleProp = entity.persistentProperties.find { it.name == 'title' } + + then: + titleProp instanceof HibernateSimpleProperty + } + + void "createManyToOne produces HibernateManyToOneProperty for a many-to-one association"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryBook.name) + def authorProp = entity.persistentProperties.find { it.name == 'author' } + + then: + authorProp instanceof HibernateManyToOneProperty + } + + void "createOneToMany produces HibernateOneToManyProperty for a one-to-many association"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryAuthor.name) + def booksProp = entity.persistentProperties.find { it.name == 'books' } + + then: + booksProp instanceof HibernateOneToManyProperty + } + + void "createManyToMany produces HibernateManyToManyProperty"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryBook.name) + def tagsProp = entity.persistentProperties.find { it.name == 'tags' } + + then: + tagsProp instanceof HibernateManyToManyProperty + } + + void "createIdentity produces HibernateIdentityProperty"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryBook.name) + + then: + entity.identity instanceof HibernateIdentityProperty + } + + void "createIdentityMapping resolves NATIVE generator by default"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryBook.name) + + then: + entity.mapping.identifier.generator == ValueGenerator.NATIVE + } + + void "createIdentityMapping resolves CUSTOM generator for a custom class name"() { + when: + def ctx = new HibernateMappingContext() + PersistentEntity entity = ctx.addPersistentEntity(MappingFactoryCustomIdEntity) + + then: + entity.mapping.identifier.generator == ValueGenerator.CUSTOM + } + + void "createIdentityMapping returns HibernateIdentityMapping instance"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryBook.name) + def idMapping = entity.mapping.identifier + + then: + idMapping instanceof HibernateIdentityMapping + idMapping.identifierName != null + idMapping.identifierName.length > 0 + } + + void "createEmbedded produces HibernateEmbeddedProperty"() { + when: + PersistentEntity entity = mappingContext.getPersistentEntity(MappingFactoryArticle.name) + def addrProp = entity.persistentProperties.find { it.name == 'metadata' } + + then: + addrProp instanceof HibernateEmbeddedProperty + } + + @Rollback + void "factory-created entities can be persisted and retrieved"() { + when: + def author = new MappingFactoryAuthor(name: 'Test Author').save(flush: true) + def book = new MappingFactoryBook(title: 'Test Book', author: author).save(flush: true) + + then: + MappingFactoryBook.count() >= 1 + MappingFactoryBook.findByTitle('Test Book')?.author?.name == 'Test Author' + } +} + +// --- domain classes --- + +@Entity +class MappingFactoryAuthor implements HibernateEntity<MappingFactoryAuthor> { + String name + static hasMany = [books: MappingFactoryBook] +} + +@Entity +class MappingFactoryBook implements HibernateEntity<MappingFactoryBook> { + String title + MappingFactoryAuthor author + static belongsTo = [author: MappingFactoryAuthor] + static hasMany = [tags: MappingFactoryTag] +} + +@Entity +class MappingFactoryTag implements HibernateEntity<MappingFactoryTag> { + String name + static hasMany = [books: MappingFactoryBook] + static belongsTo = MappingFactoryBook +} + +@Entity +class MappingFactoryArticle implements HibernateEntity<MappingFactoryArticle> { + String title + MappingFactoryMetadata metadata + static embedded = ['metadata'] +} + +class MappingFactoryMetadata { + String description +} + +@Entity +class MappingFactoryCustomIdEntity implements HibernateEntity<MappingFactoryCustomIdEntity> { + String name + static mapping = { + id generator: 'grails.gorm.specs.FactoryCustomType', type: 'uuid-binary' + } +} + +// --- helpers --- + +class FactoryCustomType {} + +class FactoryTypeMarshaller extends AbstractMappingAwareCustomTypeMarshaller { + FactoryTypeMarshaller(Class targetType) { super(targetType) } + + @Override + protected Object writeInternal(PersistentProperty property, String key, Object value, Object nativeTarget) { value } + + @Override + protected Object readInternal(PersistentProperty property, String key, Object nativeSource) { nativeSource } +} diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContextSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContextSpec.groovy index d5f78349f5..a102789dd1 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContextSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/HibernateMappingContextSpec.groovy @@ -161,10 +161,10 @@ class HibernateMappingContextSpec extends HibernateGormDatastoreSpec { entity.persistentProperties.find { it.name == "books" } != null } - void "getMappingFactory returns the HibernateMappingFactory"() { + void "getMappingFactory returns a HibernateMappingFactory"() { expect: mappingContext.mappingFactory != null - mappingContext.mappingFactory.class.simpleName == "HibernateMappingFactory" + mappingContext.mappingFactory instanceof org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateMappingFactory } }
