HHH-18060 - HbmXmlTransformer

<join-formula/>
This commit is contained in:
Steve Ebersole 2024-06-12 09:47:15 -05:00
parent 5ef46f04d8
commit 6da73869ee
10 changed files with 247 additions and 30 deletions

View File

@ -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(
"<many-to-one/> [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();

View File

@ -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,

View File

@ -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 ) {

View File

@ -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<JaxbJoinColumnImpl> jaxbJoinColumns,
List<String> 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
*

View File

@ -1640,10 +1640,9 @@
<xsd:sequence>
<xsd:choice>
<xsd:sequence>
<xsd:element name="join-column" type="orm:join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="foreign-key" type="orm:foreign-key"
minOccurs="0"/>
<xsd:element name="join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="join-formula" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="foreign-key" type="orm:foreign-key" minOccurs="0"/>
</xsd:sequence>
<xsd:element name="property-ref" type="orm:property-ref"/>
</xsd:choice>
@ -2090,10 +2089,9 @@
minOccurs="0"/>
</xsd:sequence>
<xsd:sequence>
<xsd:element name="join-column" type="orm:join-column"
minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="foreign-key" type="orm:foreign-key"
minOccurs="0"/>
<xsd:element name="join-column" type="orm:join-column" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="join-formula" type="xsd:string" minOccurs="0" maxOccurs="unbounded"/>
<xsd:element name="foreign-key" type="orm:foreign-key" minOccurs="0"/>
</xsd:sequence>
<xsd:element name="join-table" type="orm:join-table"
minOccurs="0"/>

View File

@ -289,6 +289,12 @@
<inheritance:implements>org.hibernate.boot.jaxb.mapping.spi.JaxbLockableAttribute</inheritance:implements>
<inheritance:implements>org.hibernate.boot.jaxb.mapping.spi.JaxbAssociationAttribute</inheritance:implements>
<inheritance:implements>org.hibernate.boot.jaxb.mapping.spi.JaxbJoinTableCapable</inheritance:implements>
<bindings node=".//xsd:element[@name='join-column']">
<property name="joinColumns"/>
</bindings>
<bindings node=".//xsd:element[@name='join-formula']">
<property name="joinFormulas"/>
</bindings>
</bindings>
<bindings node="//xsd:complexType[@name='many-to-one']">
@ -301,8 +307,8 @@
<bindings node=".//xsd:element[@name='join-column']">
<property name="joinColumns"/>
</bindings>
<bindings node=".//xsd:element[@name='foreign-key']">
<property name="foreignKeys"/>
<bindings node=".//xsd:element[@name='join-formula']">
<property name="joinFormulas"/>
</bindings>
</bindings>

View File

@ -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" );
}
}

View File

@ -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) {
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<entity-mappings xmlns="http://www.hibernate.org/xsd/orm/mapping"
version="7.0">
<package>org.hibernate.orm.test.unconstrained</package>
<entity class="Person" metadata-complete="true">
<attributes>
<id name="name"/>
<many-to-one name="employee" target-entity="Employee">
<join-formula>employeeId</join-formula>
<cascade>
<cascade-all/>
</cascade>
</many-to-one>
</attributes>
</entity>
<entity class="Employee" metadata-complete="true">
<attributes>
<id name="id"/>
</attributes>
</entity>
</entity-mappings>

View File

@ -0,0 +1,37 @@
<?xml version="1.0"?>
<!--
~ 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.
-->
<entity-mappings xmlns="http://www.hibernate.org/xsd/orm/mapping"
version="7.0">
<package>org.hibernate.orm.test.onetoone.formula</package>
<access>FIELD</access>
<entity class="Person" metadata-complete="true">
<attributes>
<id name="name"/>
<one-to-one name="address">
<join-formula>name</join-formula>
<join-formula>'HOME'</join-formula>
</one-to-one>
</attributes>
</entity>
<entity class="Address">
<table>
<check-constraint constraint="addressType in ('MAILING', 'HOME', 'BUSINESS')"/>
</table>
<attributes>
<id name="person"/>
<id name="type"/>
<basic name="street"/>
<basic name="state"/>
<basic name="zip"/>
</attributes>
</entity>
</entity-mappings>