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 12f7fb8e2f61743f53eb778bcd192bc34b4977f3 Author: Walter Duque de Estrada <[email protected]> AuthorDate: Mon Mar 2 11:35:52 2026 -0600 refactor methods into HibernateOneToOneProperty --- .../cfg/domainbinding/binder/ManyToOneBinder.java | 5 +- .../cfg/domainbinding/binder/OneToOneBinder.java | 40 +----- .../hibernate/HibernateOneToOneProperty.java | 52 +++++++ .../HibernateOneToOnePropertySpec.groovy | 149 +++++++++++++++++++++ .../cfg/domainbinding/ManyToOneBinderSpec.groovy | 8 +- .../cfg/domainbinding/OneToOneBinderSpec.groovy | 48 +++---- 6 files changed, 231 insertions(+), 71 deletions(-) diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java index bad0ebc63e..35e0af023a 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/ManyToOneBinder.java @@ -31,7 +31,6 @@ import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateAssociation 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.HibernateOneToOneProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateToOneProperty; import org.grails.orm.hibernate.cfg.domainbinding.util.SimpleValueColumnFetcher; import org.hibernate.MappingException; import org.hibernate.boot.spi.MetadataBuildingContext; @@ -116,7 +115,6 @@ public class ManyToOneBinder { return mapping != null && mapping.hasCompositeIdentifier(); } - @SuppressWarnings("unchecked") private ManyToOne doBind( HibernateAssociation property, GrailsHibernatePersistentEntity refDomainClass, @@ -146,8 +144,7 @@ public class ManyToOneBinder { if (!config.isUniqueWithinGroup()) { c.setUnique(config.isUnique()); } else if (property.isBidirectional() - && property.getHibernateInverseSide() instanceof HibernateToOneProperty inverseSide - && inverseSide.isValidHibernateOneToOne()) { + && property.getHibernateInverseSide().isValidHibernateOneToOne()) { c.setUnique(true); } } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/OneToOneBinder.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/OneToOneBinder.java index 236e2a6e07..583f6dd2e9 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/OneToOneBinder.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/binder/OneToOneBinder.java @@ -18,17 +18,10 @@ */ package org.grails.orm.hibernate.cfg.domainbinding.binder; -import org.grails.datastore.mapping.model.types.Association; -import org.grails.orm.hibernate.cfg.PersistentEntityNamingStrategy; -import org.grails.orm.hibernate.cfg.PropertyConfig; import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty; -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernatePersistentProperty; -import org.hibernate.FetchMode; import org.hibernate.boot.spi.MetadataBuildingContext; -import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment; import org.hibernate.mapping.OneToOne; import org.hibernate.mapping.PersistentClass; -import org.hibernate.type.ForeignKeyDirection; public class OneToOneBinder { @@ -41,14 +34,6 @@ public class OneToOneBinder { this.simpleValueBinder = simpleValueBinder; } - public OneToOneBinder( - MetadataBuildingContext metadataBuildingContext, - PersistentEntityNamingStrategy namingStrategy, - JdbcEnvironment jdbcEnvironment) { - this( - metadataBuildingContext, - new SimpleValueBinder(metadataBuildingContext, namingStrategy, jdbcEnvironment)); - } public OneToOne bindOneToOne( final HibernateOneToOneProperty property, @@ -56,32 +41,19 @@ public class OneToOneBinder { org.hibernate.mapping.Table table, String path) { OneToOne oneToOne = new OneToOne(metadataBuildingContext, table, owner); - PropertyConfig config = property.getMappedForm(); - final Association otherSide = property.getInverseSide(); - final boolean hasOne = otherSide != null && otherSide.isHasOne(); - oneToOne.setConstrained(hasOne); - oneToOne.setForeignKeyType( - oneToOne.isConstrained() ? ForeignKeyDirection.FROM_PARENT : ForeignKeyDirection.TO_PARENT); + oneToOne.setConstrained(property.isHibernateConstrained()); + oneToOne.setForeignKeyType(property.getHibernateForeignKeyDirection()); oneToOne.setAlternateUniqueKey(true); - - if (config != null && config.getFetchMode() != null) { - oneToOne.setFetchMode(config.getFetchMode()); - } else { - oneToOne.setFetchMode(FetchMode.DEFAULT); - } - - oneToOne.setReferencedEntityName( - otherSide != null - ? otherSide.getOwner().getName() - : property.getAssociatedEntity().getName()); + oneToOne.setFetchMode(property.getHibernateFetchMode()); + oneToOne.setReferencedEntityName(property.getHibernateReferencedEntityName()); oneToOne.setPropertyName(property.getName()); oneToOne.setReferenceToPrimaryKey(false); - if (hasOne || otherSide == null) { + if (property.needsSimpleValueBinding()) { simpleValueBinder.bindSimpleValue(property, null, oneToOne, path); } else { - oneToOne.setReferencedPropertyName(otherSide.getName()); + oneToOne.setReferencedPropertyName(property.getHibernateReferencedPropertyName()); } return oneToOne; } diff --git a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateOneToOneProperty.java b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateOneToOneProperty.java index 2328fe1707..ac85c3407f 100644 --- a/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateOneToOneProperty.java +++ b/grails-data-hibernate7/core/src/main/groovy/org/grails/orm/hibernate/cfg/domainbinding/hibernate/HibernateOneToOneProperty.java @@ -23,7 +23,9 @@ import org.grails.datastore.mapping.model.MappingContext; import org.grails.datastore.mapping.model.PersistentEntity; import org.grails.datastore.mapping.model.types.mapping.OneToOneWithMapping; import org.grails.orm.hibernate.cfg.PropertyConfig; +import org.hibernate.FetchMode; import org.hibernate.MappingException; +import org.hibernate.type.ForeignKeyDirection; /** Hibernate implementation of {@link org.grails.datastore.mapping.model.types.OneToOne} */ public class HibernateOneToOneProperty extends OneToOneWithMapping<PropertyConfig> @@ -49,6 +51,56 @@ public class HibernateOneToOneProperty extends OneToOneWithMapping<PropertyConfi return (GrailsHibernatePersistentEntity) super.getAssociatedEntity(); } + @Override + public HibernateOneToOneProperty getHibernateInverseSide() { + return (HibernateOneToOneProperty) getInverseSide(); + } + + /** True when the FK is on this side (hasOne on the other side). Maps to Hibernate constrained. */ + public boolean isHibernateConstrained() { + HibernateOneToOneProperty otherSide = getHibernateInverseSide(); + return otherSide != null && otherSide.isHasOne(); + } + + /** + * The entity name that Hibernate should reference. When the other side exists, it is the other + * side's owner; otherwise the directly associated entity. + */ + public String getHibernateReferencedEntityName() { + HibernateOneToOneProperty otherSide = getHibernateInverseSide(); + return otherSide != null + ? otherSide.getOwner().getName() + : getAssociatedEntity().getName(); + } + + /** + * The property name on the referenced entity that back-references this association. Only + * meaningful when {@link #isHibernateConstrained()} is false and the other side exists. + */ + public String getHibernateReferencedPropertyName() { + HibernateOneToOneProperty otherSide = getHibernateInverseSide(); + return otherSide != null ? otherSide.getName() : null; + } + + /** FK direction: FROM_PARENT when constrained (hasOne on other side), TO_PARENT otherwise. */ + public ForeignKeyDirection getHibernateForeignKeyDirection() { + return isHibernateConstrained() ? ForeignKeyDirection.FROM_PARENT : ForeignKeyDirection.TO_PARENT; + } + + /** Resolved fetch mode: uses the configured value or falls back to {@link FetchMode#DEFAULT}. */ + public FetchMode getHibernateFetchMode() { + PropertyConfig config = getMappedForm(); + return (config != null && config.getFetchMode() != null) ? config.getFetchMode() : FetchMode.DEFAULT; + } + + /** + * True when Hibernate should bind a simple column value rather than a referenced property name. + * This is the case when the FK is on this side (constrained) or no inverse side exists. + */ + public boolean needsSimpleValueBinding() { + return isHibernateConstrained() || getHibernateReferencedPropertyName() == null; + } + public boolean isValidHibernateOneToOne() { validateAssociation(); return canBindOneToOneWithSingleColumnAndForeignKey() || isHasOne() && isBidirectional() && getInverseSide() != null; diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/HibernateOneToOnePropertySpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/HibernateOneToOnePropertySpec.groovy new file mode 100644 index 0000000000..d178cfa3d8 --- /dev/null +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/HibernateOneToOnePropertySpec.groovy @@ -0,0 +1,149 @@ +/* + * 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 + +import grails.gorm.annotation.Entity +import grails.gorm.hibernate.HibernateEntity +import grails.gorm.specs.HibernateGormDatastoreSpec +import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty +import org.hibernate.FetchMode +import org.hibernate.type.ForeignKeyDirection + +class HibernateOneToOnePropertySpec extends HibernateGormDatastoreSpec { + + def setupSpec() { + manager.addAllDomainClasses([OneToOneFace, OneToOneNose]) + } + + void "getHibernateInverseSide returns HibernateOneToOneProperty"() { + when: + def faceEntity = mappingContext.getPersistentEntity(OneToOneFace.name) + def noseProp = faceEntity.persistentProperties.find { it.name == 'nose' } as HibernateOneToOneProperty + + then: + noseProp.getHibernateInverseSide() instanceof HibernateOneToOneProperty + noseProp.getHibernateInverseSide().name == 'face' + } + + void "isHibernateConstrained is false when other side does not have hasOne"() { + when: + def faceEntity = mappingContext.getPersistentEntity(OneToOneFace.name) + def noseProp = faceEntity.persistentProperties.find { it.name == 'nose' } as HibernateOneToOneProperty + + then: + !noseProp.isHibernateConstrained() + } + + void "isHibernateConstrained is true when other side has hasOne"() { + when: + def noseEntity = mappingContext.getPersistentEntity(OneToOneNose.name) + def faceProp = noseEntity.persistentProperties.find { it.name == 'face' } as HibernateOneToOneProperty + + then: + faceProp.isHibernateConstrained() + } + + void "getHibernateReferencedEntityName returns other side owner name when inverse exists"() { + when: + def faceEntity = mappingContext.getPersistentEntity(OneToOneFace.name) + def noseProp = faceEntity.persistentProperties.find { it.name == 'nose' } as HibernateOneToOneProperty + + then: + noseProp.getHibernateReferencedEntityName() == OneToOneNose.name + } + + void "getHibernateReferencedPropertyName returns inverse side name when inverse exists"() { + when: + def faceEntity = mappingContext.getPersistentEntity(OneToOneFace.name) + def noseProp = faceEntity.persistentProperties.find { it.name == 'nose' } as HibernateOneToOneProperty + + then: + noseProp.getHibernateReferencedPropertyName() == 'face' + } + + void "getHibernateReferencedPropertyName returns null when no inverse"() { + when: + def noseEntity = mappingContext.getPersistentEntity(OneToOneNose.name) + // face belongs to OneToOneFace via hasOne — it has no inverse side from nose's perspective + def faceProp = noseEntity.persistentProperties.find { it.name == 'face' } as HibernateOneToOneProperty + + then: + // face's inverse is the nose prop on the owning side, so referencedPropertyName is 'nose' + faceProp.getHibernateReferencedPropertyName() == 'nose' + } + + void "getHibernateForeignKeyDirection returns TO_PARENT when not constrained"() { + when: + def faceEntity = mappingContext.getPersistentEntity(OneToOneFace.name) + def noseProp = faceEntity.persistentProperties.find { it.name == 'nose' } as HibernateOneToOneProperty + + then: + noseProp.getHibernateForeignKeyDirection() == ForeignKeyDirection.TO_PARENT + } + + void "getHibernateForeignKeyDirection returns FROM_PARENT when constrained"() { + when: + def noseEntity = mappingContext.getPersistentEntity(OneToOneNose.name) + def faceProp = noseEntity.persistentProperties.find { it.name == 'face' } as HibernateOneToOneProperty + + then: + faceProp.getHibernateForeignKeyDirection() == ForeignKeyDirection.FROM_PARENT + } + + void "getHibernateFetchMode returns DEFAULT when no fetch config"() { + when: + def faceEntity = mappingContext.getPersistentEntity(OneToOneFace.name) + def noseProp = faceEntity.persistentProperties.find { it.name == 'nose' } as HibernateOneToOneProperty + + then: + noseProp.getHibernateFetchMode() == FetchMode.DEFAULT + } + + void "needsSimpleValueBinding is false when not constrained and inverse exists"() { + when: + def faceEntity = mappingContext.getPersistentEntity(OneToOneFace.name) + def noseProp = faceEntity.persistentProperties.find { it.name == 'nose' } as HibernateOneToOneProperty + + then: + !noseProp.needsSimpleValueBinding() + } + + void "needsSimpleValueBinding is true when constrained"() { + when: + def noseEntity = mappingContext.getPersistentEntity(OneToOneNose.name) + def faceProp = noseEntity.persistentProperties.find { it.name == 'face' } as HibernateOneToOneProperty + + then: + faceProp.needsSimpleValueBinding() + } +} + +@Entity +class OneToOneFace implements HibernateEntity<OneToOneFace> { + String name + OneToOneNose nose + static hasOne = [nose: OneToOneNose] +} + +@Entity +class OneToOneNose implements HibernateEntity<OneToOneNose> { + Boolean hasFreckles + OneToOneFace face + static belongsTo = [face: OneToOneFace] +} diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy index 6b368af0aa..e358200774 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/ManyToOneBinderSpec.groovy @@ -117,7 +117,7 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec { } def propertyConfig = Mock(PropertyConfig) def column = new Column('test') - def inverseSide = Mock(HibernateToOneProperty) + def inverseSide = Mock(TestOneToOneInverse) property.getHibernateAssociatedEntity() >> refDomainClass mapping.setIdentity(null) @@ -179,3 +179,9 @@ class ManyToOneBinderSpec extends HibernateGormDatastoreSpec { thrown(MappingException) } } + +abstract class TestOneToOneInverse extends HibernateOneToOneProperty { + TestOneToOneInverse(org.grails.datastore.mapping.model.PersistentEntity owner, org.grails.datastore.mapping.model.MappingContext context, java.beans.PropertyDescriptor descriptor) { + super(owner, context, descriptor) + } +} diff --git a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/OneToOneBinderSpec.groovy b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/OneToOneBinderSpec.groovy index 4b5e9d2073..f2759532ea 100644 --- a/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/OneToOneBinderSpec.groovy +++ b/grails-data-hibernate7/core/src/test/groovy/org/grails/orm/hibernate/cfg/domainbinding/OneToOneBinderSpec.groovy @@ -1,12 +1,9 @@ package org.grails.orm.hibernate.cfg.domainbinding import grails.gorm.specs.HibernateGormDatastoreSpec -import org.grails.datastore.mapping.model.types.OneToOne as GormOneToOne import org.grails.datastore.mapping.model.MappingContext import org.grails.datastore.mapping.model.PersistentEntity import org.grails.orm.hibernate.cfg.domainbinding.hibernate.HibernateOneToOneProperty -import org.grails.orm.hibernate.cfg.domainbinding.hibernate.GrailsHibernatePersistentEntity -import org.grails.orm.hibernate.cfg.PropertyConfig import org.hibernate.FetchMode import org.hibernate.mapping.OneToOne as HibernateOneToOne import org.hibernate.mapping.RootClass @@ -33,20 +30,16 @@ class OneToOneBinderSpec extends HibernateGormDatastoreSpec { def ownerRoot = new RootClass(metadataBuildingContext) def gormOneToOne = Mock(TestOneToOne) - def otherSide = Mock(GormOneToOne) def owner = Mock(PersistentEntity) - def otherOwner = Mock(PersistentEntity) - gormOneToOne.getInverseSide() >> otherSide gormOneToOne.getName() >> "myOneToOne" gormOneToOne.getOwner() >> owner - gormOneToOne.getMappedForm() >> new PropertyConfig() - - otherSide.isHasOne() >> false - otherSide.getOwner() >> otherOwner - otherSide.getName() >> "otherSide" - - otherOwner.getName() >> "OtherEntity" + gormOneToOne.isHibernateConstrained() >> false + gormOneToOne.getHibernateForeignKeyDirection() >> ForeignKeyDirection.TO_PARENT + gormOneToOne.getHibernateFetchMode() >> FetchMode.DEFAULT + gormOneToOne.getHibernateReferencedEntityName() >> "OtherEntity" + gormOneToOne.getHibernateReferencedPropertyName() >> "otherSide" + gormOneToOne.needsSimpleValueBinding() >> false when: def hibernateOneToOne = binder.bindOneToOne(gormOneToOne, ownerRoot, null, "") @@ -68,19 +61,15 @@ class OneToOneBinderSpec extends HibernateGormDatastoreSpec { def ownerRoot = new RootClass(metadataBuildingContext) def gormOneToOne = Mock(TestOneToOne) - def otherSide = Mock(GormOneToOne) def owner = Mock(PersistentEntity) - def otherOwner = Mock(PersistentEntity) - gormOneToOne.getInverseSide() >> otherSide gormOneToOne.getName() >> "myOneToOne" gormOneToOne.getOwner() >> owner - gormOneToOne.getMappedForm() >> new PropertyConfig() - - otherSide.isHasOne() >> true - otherSide.getOwner() >> otherOwner - - otherOwner.getName() >> "OtherEntity" + gormOneToOne.isHibernateConstrained() >> true + gormOneToOne.getHibernateForeignKeyDirection() >> ForeignKeyDirection.FROM_PARENT + gormOneToOne.getHibernateFetchMode() >> FetchMode.DEFAULT + gormOneToOne.getHibernateReferencedEntityName() >> "OtherEntity" + gormOneToOne.needsSimpleValueBinding() >> true when: def hibernateOneToOne = binder.bindOneToOne(gormOneToOne, ownerRoot, null, "") @@ -96,20 +85,15 @@ class OneToOneBinderSpec extends HibernateGormDatastoreSpec { def metadataBuildingContext = getGrailsDomainBinder().getMetadataBuildingContext() def ownerRoot = new RootClass(metadataBuildingContext) - def propertyConfig = new PropertyConfig() - propertyConfig.setFetch("join") - def gormOneToOne = Mock(TestOneToOne) - def otherSide = Mock(GormOneToOne) def owner = Mock(PersistentEntity) - def otherOwner = Mock(PersistentEntity) - gormOneToOne.getInverseSide() >> otherSide gormOneToOne.getOwner() >> owner - gormOneToOne.getMappedForm() >> propertyConfig - - otherSide.getOwner() >> otherOwner - otherSide.isHasOne() >> false + gormOneToOne.isHibernateConstrained() >> false + gormOneToOne.getHibernateForeignKeyDirection() >> ForeignKeyDirection.TO_PARENT + gormOneToOne.getHibernateFetchMode() >> FetchMode.JOIN + gormOneToOne.getHibernateReferencedEntityName() >> "OtherEntity" + gormOneToOne.needsSimpleValueBinding() >> true when: def hibernateOneToOne = binder.bindOneToOne(gormOneToOne, ownerRoot, null, "")
