This is an automated email from the ASF dual-hosted git repository. borinquenkid pushed a commit to branch merge-hibernate6 in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 41a37104467ac25bedcd17c626e8e41b8cf82d3f Author: Walter Duque de Estrada <[email protected]> AuthorDate: Mon Sep 29 19:34:01 2025 -0500 More refactoring --- .../orm/hibernate/cfg/GrailsDomainBinder.java | 135 ++------------- .../org/grails/orm/hibernate/cfg/Mapping.groovy | 4 + .../ForeignKeyColumnCountCalculator.java | 28 ++++ .../cfg/domainbinding/SimpleValueBinder.java | 119 +++++++++++++ .../ForeignKeyColumnCountCalculatorSpec.groovy | 52 ++++++ .../cfg/domainbinding/SimpleValueBinderSpec.groovy | 184 +++++++++++++++++++++ 6 files changed, 404 insertions(+), 118 deletions(-) diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java index dd1a546e57..d8df2b6449 100644 --- a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/GrailsDomainBinder.java @@ -33,12 +33,9 @@ import org.grails.orm.hibernate.cfg.domainbinding.ClassBinder; import org.grails.orm.hibernate.cfg.domainbinding.ColumnConfigToColumnBinder; import org.grails.orm.hibernate.cfg.domainbinding.ConfigureDerivedPropertiesConsumer; import org.grails.orm.hibernate.cfg.domainbinding.EnumTypeBinder; -import org.grails.orm.hibernate.cfg.domainbinding.IndexBinder; import org.grails.orm.hibernate.cfg.domainbinding.NamingStrategyProvider; -import org.grails.orm.hibernate.cfg.domainbinding.NumericColumnConstraintsBinder; import org.grails.orm.hibernate.cfg.domainbinding.PersistentPropertyToPropertyConfig; import org.grails.orm.hibernate.cfg.domainbinding.SimpleValueColumnBinder; -import org.grails.orm.hibernate.cfg.domainbinding.StringColumnConstraintsBinder; import org.grails.orm.hibernate.cfg.domainbinding.TypeNameProvider; import org.grails.orm.hibernate.cfg.domainbinding.*; import org.hibernate.FetchMode; @@ -92,7 +89,6 @@ import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -518,7 +514,7 @@ public class GrailsDomainBinder implements MetadataContributor { bindDependentKeyValue(property, key, mappings, sessionFactoryBeanName); } } else { - if (hasJoinKeyMapping(propConfig)) { + if (propConfig.hasJoinKeyMapping()) { String columnName = propConfig.getJoinTable().getKey().getName(); new SimpleValueColumnBinder().bindSimpleValue(key, "long", columnName, false); } else { @@ -780,7 +776,7 @@ public class GrailsDomainBinder implements MetadataContributor { final PersistentEntity domainClass = property.getAssociatedEntity(); Mapping m = new HibernateEntityWrapper().getMappedForm(domainClass); - if (hasCompositeIdentifier(m)) { + if (m.hasCompositeIdentifier()) { CompositeIdentity ci = (CompositeIdentity) m.getIdentity(); bindCompositeIdentifierToManyToOne(property, element, ci, domainClass, EMPTY_PATH, sessionFactoryBeanName); @@ -856,7 +852,7 @@ public class GrailsDomainBinder implements MetadataContributor { PersistentEntity refDomainClass = property.getOwner(); final Mapping mapping = new HibernateEntityWrapper().getMappedForm(refDomainClass); - boolean hasCompositeIdentifier = hasCompositeIdentifier(mapping); + boolean hasCompositeIdentifier = mapping.hasCompositeIdentifier(); if ((shouldCollectionBindWithJoinColumn((ToMany) property) && hasCompositeIdentifier) || (hasCompositeIdentifier && ( property instanceof ManyToMany))) { CompositeIdentity ci = (CompositeIdentity) mapping.getIdentity(); @@ -864,7 +860,7 @@ public class GrailsDomainBinder implements MetadataContributor { } else { // set type - bindSimpleValue(property, null, key, EMPTY_PATH, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(property, null, key, EMPTY_PATH); } } @@ -1793,7 +1789,7 @@ public class GrailsDomainBinder implements MetadataContributor { } value = new BasicValue(metadataBuildingContext, table); // set type - bindSimpleValue(currentGrailsProp, null, (SimpleValue) value, EMPTY_PATH, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(currentGrailsProp, null, (SimpleValue) value, EMPTY_PATH); } else if (collectionType != null) { String typeName = new TypeNameProvider().getTypeName(currentGrailsProp, gormMapping); @@ -1861,7 +1857,7 @@ public class GrailsDomainBinder implements MetadataContributor { } value = new BasicValue(metadataBuildingContext, table); // set type - bindSimpleValue(currentGrailsProp, null, (SimpleValue) value, EMPTY_PATH, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(currentGrailsProp, null, (SimpleValue) value, EMPTY_PATH); } if (value != null) { @@ -1998,7 +1994,7 @@ public class GrailsDomainBinder implements MetadataContributor { } else { // set type - bindSimpleValue(currentGrailsProp, componentProperty, (SimpleValue) value, path, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(currentGrailsProp, componentProperty, (SimpleValue) value, path); } } @@ -2054,7 +2050,8 @@ public class GrailsDomainBinder implements MetadataContributor { bindManyToOneValues(property, manyToOne); PersistentEntity refDomainClass = property instanceof ManyToMany ? property.getOwner() : property.getAssociatedEntity(); Mapping mapping = new HibernateEntityWrapper().getMappedForm(refDomainClass); - boolean isComposite = hasCompositeIdentifier(mapping); + + boolean isComposite = mapping.hasCompositeIdentifier(); if (isComposite) { CompositeIdentity ci = (CompositeIdentity) mapping.getIdentity(); bindCompositeIdentifierToManyToOne(property, manyToOne, ci, refDomainClass, path, sessionFactoryBeanName); @@ -2067,7 +2064,7 @@ public class GrailsDomainBinder implements MetadataContributor { if (pc.getColumns().isEmpty()) { mapping.getColumns().put(property.getName(), pc); } - if (!hasJoinKeyMapping(pc) ) { + if (!pc.hasJoinKeyMapping()) { JoinTable jt = new JoinTable(); final ColumnConfig columnConfig = new ColumnConfig(); columnConfig.setName(getNamingStrategy().resolveColumnName(property.getName()) + UNDERSCORE + FOREIGN_KEY_SUFFIX); @@ -2076,12 +2073,12 @@ public class GrailsDomainBinder implements MetadataContributor { } new PersistentPropertyToPropertyConfig().apply(property); // set type - bindSimpleValue(property, null, manyToOne, path, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(property, null, manyToOne, path); } else { // bind column // set type - bindSimpleValue(property, null, manyToOne, path, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(property, null, manyToOne, path); } } @@ -2108,7 +2105,7 @@ public class GrailsDomainBinder implements MetadataContributor { List<ColumnConfig> columns = config.getColumns(); int i = columns.size(); - int expectedForeignKeyColumnLength = calculateForeignKeyColumnCount(refDomainClass, propertyNames); + int expectedForeignKeyColumnLength = new ForeignKeyColumnCountCalculator().calculateForeignKeyColumnCount(refDomainClass, propertyNames); if (i != expectedForeignKeyColumnLength) { int j = 0; for (String propertyName : propertyNames) { @@ -2157,35 +2154,11 @@ public class GrailsDomainBinder implements MetadataContributor { } new PersistentPropertyToPropertyConfig().apply(property); // set type - bindSimpleValue(property, null, value, path, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(property, null, value, path); } // each property may consist of one or many columns (due to composite ids) so in order to get the // number of columns required for a column key we have to perform the calculation here - private int calculateForeignKeyColumnCount(PersistentEntity refDomainClass, String[] propertyNames) { - int expectedForeignKeyColumnLength = 0; - for (String propertyName : propertyNames) { - PersistentProperty referencedProperty = refDomainClass.getPropertyByName(propertyName); - if(referencedProperty instanceof ToOne) { - ToOne toOne = (ToOne) referencedProperty; - PersistentProperty[] compositeIdentity = toOne.getAssociatedEntity().getCompositeIdentity(); - if(compositeIdentity != null) { - expectedForeignKeyColumnLength += compositeIdentity.length; - } - else { - expectedForeignKeyColumnLength++; - } - } - else { - expectedForeignKeyColumnLength++; - } - } - return expectedForeignKeyColumnLength; - } - - private boolean hasCompositeIdentifier(Mapping mapping) { - return mapping != null && (mapping.getIdentity() instanceof CompositeIdentity); - } private void bindOneToOne(final org.grails.datastore.mapping.model.types.OneToOne property, OneToOne oneToOne, String path, String sessionFactoryBeanName) { @@ -2217,7 +2190,7 @@ public class GrailsDomainBinder implements MetadataContributor { PropertyConfig pc = new PersistentPropertyToPropertyConfig().apply(property); new PersistentPropertyToPropertyConfig().apply(property); // set type - bindSimpleValue(property, null, oneToOne, path, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(property, null, oneToOne, path); } else { oneToOne.setReferencedPropertyName(otherSide.getName()); @@ -2261,7 +2234,7 @@ public class GrailsDomainBinder implements MetadataContributor { BasicValue val = new BasicValue(metadataBuildingContext, entity.getTable()); // set type - bindSimpleValue(version, null, val, EMPTY_PATH, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(version, null, val, EMPTY_PATH); if (val.isTypeSpecified()) { // if (!(val.getType() instanceof IntegerType || @@ -2341,7 +2314,7 @@ public class GrailsDomainBinder implements MetadataContributor { // bind value // set type - bindSimpleValue(identifier, null, id, EMPTY_PATH, sessionFactoryBeanName); + new SimpleValueBinder(namingStrategy).bindSimpleValue(identifier, null, id, EMPTY_PATH); // create property Property prop = new Property(); @@ -2357,80 +2330,6 @@ public class GrailsDomainBinder implements MetadataContributor { } - /** - * Binds a simple value to the Hibernate metamodel. A simple value is - * any type within the Hibernate type system - * - * @param property - * @param parentProperty - * @param simpleValue The simple value to bind - * @param path - * @param sessionFactoryBeanName the session factory bean name - */ - private void bindSimpleValue( - PersistentProperty property - , PersistentProperty parentProperty - , SimpleValue simpleValue - , String path - , String sessionFactoryBeanName - ) { - PropertyConfig propertyConfig = new PersistentPropertyToPropertyConfig().apply(property); - Mapping mapping = new HibernateEntityWrapper().getMappedForm(property.getOwner()); - final String typeName = new TypeNameProvider().getTypeName(property, mapping); - if (typeName == null) { - simpleValue.setTypeName(property.getType().getName()); - } - else { - simpleValue.setTypeName(typeName); - simpleValue.setTypeParameters(propertyConfig.getTypeParams()); - } - if ( propertyConfig.isDerived() && !(property instanceof TenantId)) { - Formula formula = new Formula(); - formula.setFormula(propertyConfig.getFormula()); - simpleValue.addFormula(formula); - } else { - Table table = simpleValue.getTable(); - - String generator = propertyConfig.getGenerator(); - if(generator != null) { - simpleValue.setIdentifierGeneratorStrategy(generator); - Properties params = propertyConfig.getTypeParams(); - if(params != null) { - Properties generatorProps = new Properties(); - generatorProps.putAll(params); - - if(generatorProps.containsKey(SEQUENCE_KEY)) { - generatorProps.put(SequenceStyleGenerator.SEQUENCE_PARAM, generatorProps.getProperty(SEQUENCE_KEY)); - } - simpleValue.setIdentifierGeneratorProperties( generatorProps ); - } - } - - // Add the column definitions for this value/property. Note that - // not all custom mapped properties will have column definitions, - // in which case we still need to create a Hibernate column for - // this value. - var columnConfigToColumnBinder = new ColumnConfigToColumnBinder(); - var columnBinder = new ColumnBinder(namingStrategy); - Optional.ofNullable(propertyConfig.getColumns()). - filter(list-> !list.isEmpty()) - .orElse(Arrays.asList(new ColumnConfig[] { null })) - .forEach( cc -> { - Column column = new Column(); - columnConfigToColumnBinder.bindColumnConfigToColumn(column,cc,propertyConfig); - columnBinder.bindColumn(property, parentProperty, column, cc, path, table); - if (table != null) { - table.addColumn(column); - } - simpleValue.addColumn(column); - }); - } - } - - private boolean hasJoinKeyMapping(PropertyConfig c) { - return c.hasJoinKeyMapping(); - } - private String getIndexColumnName(PersistentProperty property, String sessionFactoryBeanName) { PropertyConfig pc = new PersistentPropertyToPropertyConfig().apply(property); if (pc != null && pc.getIndexColumn() != null && pc.getIndexColumn().getColumn() != null) { diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/Mapping.groovy b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/Mapping.groovy index bb2fe77195..f8171cb0aa 100644 --- a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/Mapping.groovy +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/Mapping.groovy @@ -596,4 +596,8 @@ class Mapping extends Entity<PropertyConfig> { } cloned } + + boolean hasCompositeIdentifier() { + return identity instanceof CompositeIdentity + } } diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculator.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculator.java new file mode 100644 index 0000000000..879ee061e9 --- /dev/null +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculator.java @@ -0,0 +1,28 @@ +package org.grails.orm.hibernate.cfg.domainbinding; + +import org.grails.datastore.mapping.model.PersistentEntity; +import org.grails.datastore.mapping.model.PersistentProperty; +import org.grails.datastore.mapping.model.types.ToOne; +// each property may consist of one or many columns (due to composite ids) so in order to get the +// number of columns required for a column key we have to perform the calculation here +public class ForeignKeyColumnCountCalculator { + public int calculateForeignKeyColumnCount(PersistentEntity refDomainClass, String[] propertyNames) { + int expectedForeignKeyColumnLength = 0; + for (String propertyName : propertyNames) { + PersistentProperty referencedProperty = refDomainClass.getPropertyByName(propertyName); + if(referencedProperty instanceof ToOne toOne) { + PersistentProperty[] compositeIdentity = toOne.getAssociatedEntity().getCompositeIdentity(); + if(compositeIdentity != null) { + expectedForeignKeyColumnLength += compositeIdentity.length; + } + else { + expectedForeignKeyColumnLength++; + } + } + else { + expectedForeignKeyColumnLength++; + } + } + return expectedForeignKeyColumnLength; + } +} diff --git a/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java new file mode 100644 index 0000000000..94fdaaab5e --- /dev/null +++ b/grails-data-hibernate6/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinder.java @@ -0,0 +1,119 @@ +package org.grails.orm.hibernate.cfg.domainbinding; + +import java.util.Arrays; +import java.util.Optional; +import java.util.Properties; + +import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.mapping.Column; +import org.hibernate.mapping.Formula; +import org.hibernate.mapping.SimpleValue; +import org.hibernate.mapping.Table; + +import org.grails.datastore.mapping.model.PersistentProperty; +import org.grails.datastore.mapping.model.types.TenantId; +import org.grails.orm.hibernate.cfg.ColumnConfig; +import org.grails.orm.hibernate.cfg.Mapping; +import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy; +import org.grails.orm.hibernate.cfg.PropertyConfig; + +public class SimpleValueBinder { + + private final ColumnConfigToColumnBinder columnConfigToColumnBinder ; + private final ColumnBinder columnBinder; + private final PersistentPropertyToPropertyConfig persistentPropertyToPropertyConfig; + private final HibernateEntityWrapper hibernateEntityWrapper; + private final TypeNameProvider typeNameProvider; + + + private static final String SEQUENCE_KEY = "sequence"; + + public SimpleValueBinder(PersistentEntityNamingStrategy namingStrategy) { + this.columnConfigToColumnBinder = new ColumnConfigToColumnBinder(); + this.columnBinder = new ColumnBinder(namingStrategy); + this.persistentPropertyToPropertyConfig = new PersistentPropertyToPropertyConfig(); + this.hibernateEntityWrapper = new HibernateEntityWrapper(); + this.typeNameProvider = new TypeNameProvider(); + + } + + protected SimpleValueBinder(ColumnConfigToColumnBinder columnConfigToColumnBinder + , ColumnBinder columnBinder + , PersistentPropertyToPropertyConfig persistentPropertyToPropertyConfig + , HibernateEntityWrapper hibernateEntityWrapper + ,TypeNameProvider typeNameProvider) { + this.columnConfigToColumnBinder = columnConfigToColumnBinder; + this.columnBinder = columnBinder; + this.persistentPropertyToPropertyConfig = persistentPropertyToPropertyConfig; + this.typeNameProvider = typeNameProvider; + this.hibernateEntityWrapper = hibernateEntityWrapper; + } + + + /** + * Binds a simple value to the Hibernate metamodel. A simple value is + * any type within the Hibernate type system + * + * @param property + * @param parentProperty + * @param simpleValue The simple value to bind + * @param path + */ + + public void bindSimpleValue( + PersistentProperty property + , PersistentProperty parentProperty + , SimpleValue simpleValue + , String path + ) { + PropertyConfig propertyConfig = persistentPropertyToPropertyConfig.apply(property); + Mapping mapping = hibernateEntityWrapper.getMappedForm(property.getOwner()); + final String typeName = typeNameProvider.getTypeName(property, mapping); + if (typeName == null) { + simpleValue.setTypeName(property.getType().getName()); + } + else { + simpleValue.setTypeName(typeName); + simpleValue.setTypeParameters(propertyConfig.getTypeParams()); + } + if ( propertyConfig.isDerived() && !(property instanceof TenantId)) { + Formula formula = new Formula(); + formula.setFormula(propertyConfig.getFormula()); + simpleValue.addFormula(formula); + } else { + Table table = simpleValue.getTable(); + + String generator = propertyConfig.getGenerator(); + if(generator != null) { + simpleValue.setIdentifierGeneratorStrategy(generator); + Properties params = propertyConfig.getTypeParams(); + if(params != null) { + Properties generatorProps = new Properties(); + generatorProps.putAll(params); + + if(generatorProps.containsKey(SEQUENCE_KEY)) { + generatorProps.put(SequenceStyleGenerator.SEQUENCE_PARAM, generatorProps.getProperty(SEQUENCE_KEY)); + } + simpleValue.setIdentifierGeneratorProperties( generatorProps ); + } + } + + // Add the column definitions for this value/property. Note that + // not all custom mapped properties will have column definitions, + // in which case we still need to create a Hibernate column for + // this value. + Optional.ofNullable(propertyConfig.getColumns()). + filter(list-> !list.isEmpty()) + .orElse(Arrays.asList(new ColumnConfig[] { null })) + .forEach( cc -> { + Column column = new Column(); + columnConfigToColumnBinder.bindColumnConfigToColumn(column,cc,propertyConfig); + columnBinder.bindColumn(property, parentProperty, column, cc, path, table); + if (table != null) { + table.addColumn(column); + } + simpleValue.addColumn(column); + }); + } + } +} diff --git a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculatorSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculatorSpec.groovy new file mode 100644 index 0000000000..a327560755 --- /dev/null +++ b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ForeignKeyColumnCountCalculatorSpec.groovy @@ -0,0 +1,52 @@ +package org.grails.orm.hibernate.cfg.domainbinding + +import org.grails.datastore.mapping.model.PersistentEntity +import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.datastore.mapping.model.types.ToOne +import org.grails.orm.hibernate.cfg.HibernatePersistentEntity +import spock.lang.Specification +import spock.lang.Unroll + +class ForeignKeyColumnCountCalculatorSpec extends Specification { + + @Unroll + def "Test calculateForeignKeyColumnCount with #scenario"() { + given: + def calculator = new ForeignKeyColumnCountCalculator() + def refDomainClass = Mock(HibernatePersistentEntity) as PersistentEntity + + // Mock for a simple property + def simpleProp = Mock(PersistentProperty) + refDomainClass.getPropertyByName("simple") >> simpleProp + + // Mocks for a ToOne association with a simple ID + def toOneSimpleIdProp = Mock(ToOne) + def associatedEntitySimpleId = Mock(HibernatePersistentEntity) + refDomainClass.getPropertyByName("toOneSimple") >> toOneSimpleIdProp + toOneSimpleIdProp.getAssociatedEntity() >> associatedEntitySimpleId + associatedEntitySimpleId.getCompositeIdentity() >> null + + // Mocks for a ToOne association with a composite ID of length 2 + def toOneCompositeIdProp = Mock(ToOne) + def associatedEntityCompositeId = Mock(HibernatePersistentEntity) + def compositeId = [Mock(PersistentProperty), Mock(PersistentProperty)] as PersistentProperty[] + refDomainClass.getPropertyByName("toOneComposite") >> toOneCompositeIdProp + toOneCompositeIdProp.getAssociatedEntity() >> associatedEntityCompositeId + associatedEntityCompositeId.getCompositeIdentity() >> compositeId + + when: + int columnCount = calculator.calculateForeignKeyColumnCount(refDomainClass, propertyNames as String[]) + + then: + columnCount == expectedCount + + where: + scenario | propertyNames | expectedCount + "a single simple property" | ["simple"] | 1 + "a ToOne with a simple ID" | ["toOneSimple"] | 1 + "a ToOne with a composite ID" | ["toOneComposite"] | 2 + "a mix of all property types" | ["simple", "toOneSimple", "toOneComposite"] | 4 + "multiple simple properties" | ["simple", "simple"] | 2 + "multiple composite ID properties" | ["toOneComposite", "toOneComposite"] | 4 + } +} diff --git a/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinderSpec.groovy b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinderSpec.groovy new file mode 100644 index 0000000000..423b496ac5 --- /dev/null +++ b/grails-data-hibernate6/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/SimpleValueBinderSpec.groovy @@ -0,0 +1,184 @@ +package org.grails.orm.hibernate.cfg.domainbinding + +import org.grails.datastore.mapping.model.PersistentEntity +import org.grails.datastore.mapping.model.PersistentProperty +import org.grails.datastore.mapping.model.types.TenantId +import org.grails.orm.hibernate.cfg.ColumnConfig +import org.grails.orm.hibernate.cfg.Mapping +import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy +import org.grails.orm.hibernate.cfg.PropertyConfig +import org.hibernate.id.enhanced.SequenceStyleGenerator +import org.hibernate.mapping.Column +import org.hibernate.mapping.SimpleValue +import org.hibernate.mapping.Table +import spock.lang.Specification + +class SimpleValueBinderSpec extends Specification { + + def namingStrategy = Mock(PersistentEntityNamingStrategy) + def columnConfigToColumnBinder = Mock(ColumnConfigToColumnBinder) + def columnBinder = Mock(ColumnBinder) + def persistentPropertyToPropertyConfig = Mock(PersistentPropertyToPropertyConfig) + def hibernateEntityWrapper = Mock(HibernateEntityWrapper) + def typeNameProvider = Mock(TypeNameProvider) + + def binder = new SimpleValueBinder(columnConfigToColumnBinder, + columnBinder, + persistentPropertyToPropertyConfig, + hibernateEntityWrapper, + typeNameProvider) + + def "sets type from provider when present and applies type params"() { + given: + def prop = Mock(PersistentProperty) + def owner = Mock(PersistentEntity) + def mapping = Mock(Mapping) + def pc = Mock(PropertyConfig) + def sv = Mock(SimpleValue) + sv.getTable() >> null + def props = new Properties(); props.setProperty('p1','v1') + + // stubs + persistentPropertyToPropertyConfig.apply(prop) >> pc + prop.getOwner() >> owner + hibernateEntityWrapper.getMappedForm(owner) >> mapping + typeNameProvider.getTypeName(prop, mapping) >> "custom.Type" + pc.getTypeParams() >> props + pc.isDerived() >> false + pc.getColumns() >> null + prop.getType() >> String + prop.isNullable() >> true + + when: + binder.bindSimpleValue(prop, null, sv, "p") + + then: + 1 * sv.setTypeName("custom.Type") + 1 * sv.setTypeParameters({ it.getProperty('p1') == 'v1' }) + 1 * columnBinder.bindColumn(prop, null, _ as Column, null, 'p', null) + 1 * columnConfigToColumnBinder.bindColumnConfigToColumn(_ as Column, null, pc) + } + + def "falls back to property type when provider returns null"() { + given: + def prop = Mock(PersistentProperty) + def owner = Mock(PersistentEntity) + def mapping = Mock(Mapping) + def pc = Mock(PropertyConfig) + def sv = Mock(SimpleValue) + sv.getTable() >> null + + persistentPropertyToPropertyConfig.apply(prop) >> pc + prop.getOwner() >> owner + hibernateEntityWrapper.getMappedForm(owner) >> mapping + typeNameProvider.getTypeName(prop, mapping) >> null + pc.isDerived() >> false + pc.getColumns() >> null + prop.getType() >> Integer + prop.isNullable() >> true + + when: + binder.bindSimpleValue(prop, null, sv, null) + + then: + 1 * sv.setTypeName(Integer.name) + 1 * columnBinder.bindColumn(prop, null, _ as Column, null, null, null) + } + + def "derived property adds no columns but adds formula, except TenantId"() { + given: + def prop = Mock(PersistentProperty) + def tenantProp = Mock(TenantId) + def owner = Mock(PersistentEntity) + def mapping = Mock(Mapping) + def pc = Mock(PropertyConfig) + def tenantPc = Mock(PropertyConfig) + def sv = Mock(SimpleValue) + def sv2 = Mock(SimpleValue) + + persistentPropertyToPropertyConfig.apply(prop) >> pc + persistentPropertyToPropertyConfig.apply(tenantProp) >> tenantPc + prop.getOwner() >> owner + tenantProp.getOwner() >> owner + hibernateEntityWrapper.getMappedForm(owner) >> mapping + typeNameProvider.getTypeName(_, _) >> 'X' + + pc.isDerived() >> true + pc.getFormula() >> 'x+y' + tenantPc.isDerived() >> true + tenantPc.getFormula() >> 'ignored' + + when: + binder.bindSimpleValue(prop, null, sv, null) + + then: + 1 * sv.addFormula({ it.getFormula() == 'x+y' }) + 0 * columnBinder.bindColumn(_, _, _, _, _, _) + + when: + binder.bindSimpleValue(tenantProp, null, sv2, null) + + then: + 0 * sv2.addFormula(_) + 1 * columnBinder.bindColumn(_, _, _ as Column, _, _, _) + } + + def "applies generator and maps sequence param to SequenceStyleGenerator.SEQUENCE_PARAM"() { + given: + def prop = Mock(PersistentProperty) + def owner = Mock(PersistentEntity) + def mapping = Mock(Mapping) + def pc = Mock(PropertyConfig) + def sv = Mock(SimpleValue) + sv.getTable() >> null + def genProps = new Properties(); genProps.setProperty('sequence','seq_name'); genProps.setProperty('foo','bar') + + persistentPropertyToPropertyConfig.apply(prop) >> pc + prop.getOwner() >> owner + hibernateEntityWrapper.getMappedForm(owner) >> mapping + typeNameProvider.getTypeName(prop, mapping) >> 'Y' + pc.isDerived() >> false + pc.getColumns() >> null + pc.getGenerator() >> 'sequence' + pc.getTypeParams() >> genProps + + when: + binder.bindSimpleValue(prop, null, sv, null) + + then: + 1 * sv.setIdentifierGeneratorStrategy('sequence') + 1 * sv.setIdentifierGeneratorProperties({ it.getProperty(SequenceStyleGenerator.SEQUENCE_PARAM) == 'seq_name' && it.getProperty('foo') == 'bar' }) + } + + def "binds for each provided column config and adds to table and simple value"() { + given: + def prop = Mock(PersistentProperty) + def parent = Mock(PersistentProperty) + def owner = Mock(PersistentEntity) + def mapping = Mock(Mapping) + def pc = Mock(PropertyConfig) + def cc1 = new ColumnConfig(name: 'c1') + def cc2 = new ColumnConfig(name: 'c2') + def sv = Mock(SimpleValue) + sv.getTable() >> null + + persistentPropertyToPropertyConfig.apply(prop) >> pc + prop.getOwner() >> owner + hibernateEntityWrapper.getMappedForm(owner) >> mapping + typeNameProvider.getTypeName(prop, mapping) >> 'Z' + pc.isDerived() >> false + pc.getColumns() >> [cc1, cc2] + prop.isNullable() >> true + parent.isNullable() >> false + + when: + binder.bindSimpleValue(prop, parent, sv, 'path') + + then: + 1 * columnConfigToColumnBinder.bindColumnConfigToColumn(_ as Column, cc1, pc) + 1 * columnConfigToColumnBinder.bindColumnConfigToColumn(_ as Column, cc2, pc) + 1 * columnBinder.bindColumn(prop, parent, _ as Column, cc1, 'path', null) + 1 * columnBinder.bindColumn(prop, parent, _ as Column, cc2, 'path', null) + 2 * sv.addColumn(_ as Column) + } +}
