From 6da73869ee3ced616e5a0572b24e1afc62ff92fe Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Wed, 12 Jun 2024 09:47:15 -0500 Subject: [PATCH] HHH-18060 - HbmXmlTransformer --- .../jaxb/hbm/transform/HbmXmlTransformer.java | 28 ++++----- .../attr/ManyToOneAttributeProcessing.java | 2 +- .../attr/OneToOneAttributeProcessing.java | 2 +- .../xml/internal/db/JoinColumnProcessing.java | 41 +++++++++++++ .../org/hibernate/xsd/mapping/mapping-7.0.xsd | 14 ++--- .../src/main/xjb/mapping-bindings.xjb | 10 +++- .../hbm/joinformula/ManyToOneTests.java | 58 +++++++++++++++++++ .../models/hbm/joinformula/OneToOneTests.java | 57 ++++++++++++++++++ .../models/hbm/joinformula/many-to-one.xml | 28 +++++++++ .../models/hbm/joinformula/one-to-one.xml | 37 ++++++++++++ 10 files changed, 247 insertions(+), 30 deletions(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/ManyToOneTests.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/OneToOneTests.java create mode 100644 hibernate-core/src/test/resources/mappings/models/hbm/joinformula/many-to-one.xml create mode 100644 hibernate-core/src/test/resources/mappings/models/hbm/joinformula/one-to-one.xml diff --git a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java index d59646fa71..43d97e003d 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/jaxb/hbm/transform/HbmXmlTransformer.java @@ -1541,14 +1541,6 @@ public class HbmXmlTransformer { } private void transferOneToOne(JaxbHbmOneToOneType hbmOneToOne, JaxbAttributesContainer attributes) { - if ( !hbmOneToOne.getFormula().isEmpty() || !StringHelper.isEmpty( hbmOneToOne.getFormulaAttribute() ) ) { - handleUnsupported( - "Transformation of formulas within one-to-ones are not supported - `%s`", - origin - ); - return; - } - final JaxbOneToOneImpl oneToOne = new JaxbOneToOneImpl(); oneToOne.setAttributeAccessor( hbmOneToOne.getAccess() ); oneToOne.setCascade( convertCascadeType( hbmOneToOne.getCascade() ) ); @@ -1558,7 +1550,10 @@ public class HbmXmlTransformer { if (! StringHelper.isEmpty( hbmOneToOne.getPropertyRef() ) ) { final JaxbJoinColumnImpl joinColumn = new JaxbJoinColumnImpl(); joinColumn.setReferencedColumnName( hbmOneToOne.getPropertyRef() ); - oneToOne.getJoinColumn().add( joinColumn ); + oneToOne.getJoinColumns().add( joinColumn ); + } + for ( String formula : hbmOneToOne.getFormula() ) { + oneToOne.getJoinFormulas().add( formula ); } oneToOne.setName( hbmOneToOne.getName() ); if ( isNotEmpty( hbmOneToOne.getEntityName() ) ) { @@ -1578,12 +1573,12 @@ public class HbmXmlTransformer { m2o.setAttributeAccessor( hbmNode.getAccess() ); m2o.setCascade( convertCascadeType( hbmNode.getCascade() ) ); if ( hbmNode.getForeignKey() != null ) { - m2o.setForeignKeys( new JaxbForeignKeyImpl() ); + m2o.setForeignKey( new JaxbForeignKeyImpl() ); if ( "none".equalsIgnoreCase( hbmNode.getForeignKey() ) ) { - m2o.getForeignKeys().setConstraintMode( ConstraintMode.NO_CONSTRAINT ); + m2o.getForeignKey().setConstraintMode( ConstraintMode.NO_CONSTRAINT ); } else { - m2o.getForeignKeys().setName( hbmNode.getForeignKey() ); + m2o.getForeignKey().setName( hbmNode.getForeignKey() ); } } @@ -1628,10 +1623,7 @@ public class HbmXmlTransformer { @Override public void addFormula(String formula) { - handleUnsupportedContent( - " [name=" + hbmNode.getName() + "] specified formula [" + formula + - "] which is not supported for transformation; skipping" - ); + m2o.getJoinFormulas().add( formula ); } }, ColumnDefaultsBasicImpl.INSTANCE, @@ -2459,8 +2451,8 @@ public class HbmXmlTransformer { m2o.setName( hbmM2O.getName() ); m2o.setAttributeAccessor( hbmM2O.getAccess() ); m2o.setFetch( convert( hbmM2O.getLazy() ) ); - m2o.setForeignKeys( new JaxbForeignKeyImpl() ); - m2o.getForeignKeys().setName( hbmM2O.getForeignKey() ); + m2o.setForeignKey( new JaxbForeignKeyImpl() ); + m2o.getForeignKey().setName( hbmM2O.getForeignKey() ); if ( !hbmM2O.getColumn().isEmpty() ) { for ( JaxbHbmColumnType hbmColumn : hbmM2O.getColumn() ) { final JaxbJoinColumnImpl joinColumn = new JaxbJoinColumnImpl(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/ManyToOneAttributeProcessing.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/ManyToOneAttributeProcessing.java index 6c1c5af21c..1f68cfbb8c 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/ManyToOneAttributeProcessing.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/ManyToOneAttributeProcessing.java @@ -64,7 +64,7 @@ public class ManyToOneAttributeProcessing { // todo (7.0) : cascades? TableProcessing.transformJoinTable( jaxbManyToOne.getJoinTable(), memberDetails, xmlDocumentContext ); - JoinColumnProcessing.applyJoinColumns( jaxbManyToOne.getJoinColumns(), memberDetails, xmlDocumentContext ); + JoinColumnProcessing.applyJoinColumnsOrFormula( jaxbManyToOne.getJoinColumns(), jaxbManyToOne.getJoinFormulas(), memberDetails, xmlDocumentContext ); if ( jaxbManyToOne.getPropertyRef() != null ) { final PropertyRefAnnotation propertyRefUsage = (PropertyRefAnnotation) memberDetails.applyAnnotationUsage( HibernateAnnotations.PROPERTY_REF, diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/OneToOneAttributeProcessing.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/OneToOneAttributeProcessing.java index 2705953ba1..24dcf72387 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/OneToOneAttributeProcessing.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/attr/OneToOneAttributeProcessing.java @@ -59,7 +59,7 @@ public class OneToOneAttributeProcessing { applyCascading( jaxbOneToOne.getCascade(), memberDetails, xmlDocumentContext ); TableProcessing.transformJoinTable( jaxbOneToOne.getJoinTable(), memberDetails, xmlDocumentContext ); - JoinColumnProcessing.applyJoinColumns( jaxbOneToOne.getJoinColumn(), memberDetails, xmlDocumentContext ); + JoinColumnProcessing.applyJoinColumnsOrFormula( jaxbOneToOne.getJoinColumns(), jaxbOneToOne.getJoinFormulas(), memberDetails, xmlDocumentContext ); JoinColumnProcessing.applyPrimaryKeyJoinColumns( jaxbOneToOne.getPrimaryKeyJoinColumn(), memberDetails, xmlDocumentContext ); if ( jaxbOneToOne.isId() == Boolean.TRUE ) { diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/JoinColumnProcessing.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/JoinColumnProcessing.java index a40fb946fb..36c7d97621 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/JoinColumnProcessing.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/db/JoinColumnProcessing.java @@ -8,18 +8,24 @@ package org.hibernate.boot.models.xml.internal.db; import java.util.List; +import org.hibernate.annotations.JoinColumnOrFormula; import org.hibernate.boot.jaxb.mapping.spi.JaxbJoinColumnImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbMapKeyJoinColumnImpl; import org.hibernate.boot.jaxb.mapping.spi.JaxbPrimaryKeyJoinColumnImpl; import org.hibernate.boot.jaxb.mapping.spi.db.JaxbColumnJoined; +import org.hibernate.boot.models.HibernateAnnotations; import org.hibernate.boot.models.JpaAnnotations; import org.hibernate.boot.models.annotations.internal.JoinColumnJpaAnnotation; +import org.hibernate.boot.models.annotations.internal.JoinColumnOrFormulaAnnotation; import org.hibernate.boot.models.annotations.internal.JoinColumnsJpaAnnotation; +import org.hibernate.boot.models.annotations.internal.JoinColumnsOrFormulasAnnotation; +import org.hibernate.boot.models.annotations.internal.JoinFormulaAnnotation; import org.hibernate.boot.models.annotations.internal.MapKeyJoinColumnJpaAnnotation; import org.hibernate.boot.models.annotations.internal.MapKeyJoinColumnsJpaAnnotation; import org.hibernate.boot.models.annotations.internal.PrimaryKeyJoinColumnJpaAnnotation; import org.hibernate.boot.models.annotations.internal.PrimaryKeyJoinColumnsJpaAnnotation; import org.hibernate.boot.models.xml.spi.XmlDocumentContext; +import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.models.spi.MutableMemberDetails; @@ -76,6 +82,41 @@ public class JoinColumnProcessing { return joinColumns; } + public static void applyJoinColumnsOrFormula( + List jaxbJoinColumns, + List jaxbJoinFormulas, + MutableMemberDetails memberDetails, + XmlDocumentContext xmlDocumentContext) { + if ( CollectionHelper.isEmpty( jaxbJoinFormulas ) ) { + applyJoinColumns( jaxbJoinColumns, memberDetails, xmlDocumentContext ); + } + else { + + final JoinColumnsOrFormulasAnnotation joinColumnsOrFormulasUsage = (JoinColumnsOrFormulasAnnotation) memberDetails.replaceAnnotationUsage( + HibernateAnnotations.JOIN_COLUMN_OR_FORMULA, + HibernateAnnotations.JOIN_COLUMNS_OR_FORMULAS, + xmlDocumentContext.getModelBuildingContext() + ); + + final JoinColumn[] joinColumns = transformJoinColumnList( jaxbJoinColumns, xmlDocumentContext ); + final JoinColumnOrFormula[] values = new JoinColumnOrFormula[ jaxbJoinColumns.size() + jaxbJoinFormulas.size() ]; + joinColumnsOrFormulasUsage.value( values ); + + for ( int i = 0; i < joinColumns.length; i++ ) { + final JoinColumnOrFormulaAnnotation joinColumnOrFormula = HibernateAnnotations.JOIN_COLUMN_OR_FORMULA.createUsage( xmlDocumentContext.getModelBuildingContext() ); + joinColumnOrFormula.column( joinColumns[i] ); + } + + for ( int i = 0; i < jaxbJoinFormulas.size(); i++ ) { + final JoinFormulaAnnotation joinFormula = HibernateAnnotations.JOIN_FORMULA.createUsage( xmlDocumentContext.getModelBuildingContext() ); + joinFormula.value( jaxbJoinFormulas.get( i ) ); + final JoinColumnOrFormulaAnnotation joinColumnOrFormula = HibernateAnnotations.JOIN_COLUMN_OR_FORMULA.createUsage( xmlDocumentContext.getModelBuildingContext() ); + joinColumnOrFormula.formula( joinFormula ); + values[joinColumns.length + i] = joinColumnOrFormula; + } + } + } + /** * Support for {@linkplain JaxbPrimaryKeyJoinColumnImpl} to {@linkplain PrimaryKeyJoinColumns} transformation * diff --git a/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-7.0.xsd b/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-7.0.xsd index 023bb49e4f..87fe6306ee 100644 --- a/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-7.0.xsd +++ b/hibernate-core/src/main/resources/org/hibernate/xsd/mapping/mapping-7.0.xsd @@ -1640,10 +1640,9 @@ - - + + + @@ -2090,10 +2089,9 @@ minOccurs="0"/> - - + + + diff --git a/hibernate-core/src/main/xjb/mapping-bindings.xjb b/hibernate-core/src/main/xjb/mapping-bindings.xjb index 130621c5ee..c04cb84e10 100644 --- a/hibernate-core/src/main/xjb/mapping-bindings.xjb +++ b/hibernate-core/src/main/xjb/mapping-bindings.xjb @@ -289,6 +289,12 @@ org.hibernate.boot.jaxb.mapping.spi.JaxbLockableAttribute org.hibernate.boot.jaxb.mapping.spi.JaxbAssociationAttribute org.hibernate.boot.jaxb.mapping.spi.JaxbJoinTableCapable + + + + + + @@ -301,8 +307,8 @@ - - + + diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/ManyToOneTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/ManyToOneTests.java new file mode 100644 index 0000000000..865a85398b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/ManyToOneTests.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.boot.models.hbm.joinformula; + +import org.hibernate.cfg.MappingSettings; +import org.hibernate.mapping.Formula; +import org.hibernate.mapping.ManyToOne; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.orm.test.unconstrained.Person; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +public class ManyToOneTests { + @Test + @DomainModel(xmlMappings = "org/hibernate/orm/test/unconstrained/Person.hbm.xml") + void testHbmXml(DomainModelScope domainModelScope) { + final PersistentClass personEntityBinding = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ); + validate( personEntityBinding.getProperty( "employee" ) ); + } + + @Test + @ServiceRegistry( settings = @Setting( name= MappingSettings.TRANSFORM_HBM_XML, value = "true" ) ) + @DomainModel(xmlMappings = "org/hibernate/orm/test/unconstrained/Person.hbm.xml") + void testTransformation(DomainModelScope domainModelScope) { + final PersistentClass personEntityBinding = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ); + validate( personEntityBinding.getProperty( "employee" ) ); + } + + @Test + @DomainModel(xmlMappings = "mappings/models/hbm/joinformula/many-to-one.xml") + void testMappingXml(DomainModelScope domainModelScope) { + final PersistentClass personEntityBinding = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ); + validate( personEntityBinding.getProperty( "employee" ) ); + } + + private void validate(Property property) { + final ManyToOne manyToOne = (ManyToOne) property.getValue(); + assertThat( manyToOne.getSelectables() ).hasSize( 1 ); + assertThat( manyToOne.getSelectables().get(0) ).isInstanceOf( Formula.class ); + + assertThat( property.getCascade() ).isEqualTo( "all" ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/OneToOneTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/OneToOneTests.java new file mode 100644 index 0000000000..52a0428956 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/boot/models/hbm/joinformula/OneToOneTests.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.orm.test.boot.models.hbm.joinformula; + +import org.hibernate.cfg.MappingSettings; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; +import org.hibernate.orm.test.onetoone.formula.Person; + +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.DomainModelScope; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.Setting; +import org.junit.jupiter.api.Test; + +/** + * @author Steve Ebersole + */ +@SuppressWarnings("JUnitMalformedDeclaration") +public class OneToOneTests { + @Test + @DomainModel(xmlMappings = "org/hibernate/orm/test/onetoone/formula/Person.hbm.xml") + void testHbmXml(DomainModelScope domainModelScope) { + final PersistentClass personEntityBinding = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ); + validateAddress( personEntityBinding.getProperty( "address" ) ); + validateMailingAddress( personEntityBinding.getProperty( "mailingAddress" ) ); + } + + @Test + @ServiceRegistry( settings = @Setting( name= MappingSettings.TRANSFORM_HBM_XML, value = "true" ) ) + @DomainModel(xmlMappings = "org/hibernate/orm/test/onetoone/formula/Person.hbm.xml") + void testTransformation(DomainModelScope domainModelScope) { + final PersistentClass personEntityBinding = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ); + validateAddress( personEntityBinding.getProperty( "address" ) ); + validateMailingAddress( personEntityBinding.getProperty( "mailingAddress" ) ); + } + + @Test + @DomainModel(xmlMappings = "mappings/models/hbm/joinformula/one-to-one.xml") + void testMappingXml(DomainModelScope domainModelScope) { + final PersistentClass personEntityBinding = domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ); + validateAddress( personEntityBinding.getProperty( "address" ) ); + validateMailingAddress( personEntityBinding.getProperty( "mailingAddress" ) ); + } + + private void validateAddress(Property property) { + + } + + private void validateMailingAddress(Property property) { + + } +} diff --git a/hibernate-core/src/test/resources/mappings/models/hbm/joinformula/many-to-one.xml b/hibernate-core/src/test/resources/mappings/models/hbm/joinformula/many-to-one.xml new file mode 100644 index 0000000000..0120046f29 --- /dev/null +++ b/hibernate-core/src/test/resources/mappings/models/hbm/joinformula/many-to-one.xml @@ -0,0 +1,28 @@ + + + + + org.hibernate.orm.test.unconstrained + + + + + + employeeId + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hibernate-core/src/test/resources/mappings/models/hbm/joinformula/one-to-one.xml b/hibernate-core/src/test/resources/mappings/models/hbm/joinformula/one-to-one.xml new file mode 100644 index 0000000000..0899a1c223 --- /dev/null +++ b/hibernate-core/src/test/resources/mappings/models/hbm/joinformula/one-to-one.xml @@ -0,0 +1,37 @@ + + + + + org.hibernate.orm.test.onetoone.formula + FIELD + + + + + + + name + 'HOME' + + + + + + + +
+ + + + + + + +
+