HHH-18060 - HbmXmlTransformer

* <join/> -> <secondary-table/>
* <list/>
This commit is contained in:
Steve Ebersole 2024-06-11 11:14:20 -05:00
parent bcf8d7bee8
commit fd5de56c19
15 changed files with 687 additions and 33 deletions

View File

@ -9,8 +9,10 @@ package org.hibernate.boot.jaxb.hbm.transform;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Supplier;
@ -216,6 +218,12 @@ public class HbmXmlTransformer {
private final TransformationState transformationState;
private final UnsupportedFeatureHandling unsupportedFeatureHandling;
private final Map<String,JaxbEmbeddableImpl> jaxbEmbeddableByClassName = new HashMap<>();
/**
* Used to handle secondary table transformations
*/
private String currentTableName = "";
private HbmXmlTransformer(
Binding<JaxbHbmHibernateMapping> hbmJaxbBinding,
@ -853,6 +861,8 @@ public class HbmXmlTransformer {
origin
);
currentTableName = "";
transferBaseEntityInformation( hbmClass, entity );
entity.setMutable( hbmClass.isMutable() );
@ -1089,6 +1099,9 @@ public class HbmXmlTransformer {
origin
);
currentTableName = "";
transferBaseEntityInformation( hbmSubclass, subclassEntity );
transferEntityElementAttributes( hbmSubclass, subclassEntity );
@ -1108,6 +1121,9 @@ public class HbmXmlTransformer {
origin
);
currentTableName = "";
transferBaseEntityInformation( hbmSubclass, subclassEntity );
transferEntityElementAttributes( hbmSubclass, subclassEntity );
@ -1152,7 +1168,7 @@ public class HbmXmlTransformer {
column.setTable( tableName );
target.addColumn( column );
}
else {
else if ( !source.getColumnOrFormula().isEmpty() ) {
for ( Serializable columnOrFormula : source.getColumnOrFormula() ) {
if ( columnOrFormula instanceof String ) {
target.addFormula( (String) columnOrFormula );
@ -1166,6 +1182,13 @@ public class HbmXmlTransformer {
}
}
}
else if ( StringHelper.isNotEmpty( tableName ) ) {
// this is the case of transforming a <join/> where the property did not specify columns or formula.
// we need to create a column still to pass along the secondary table name
final TargetColumnAdapter column = target.makeColumnAdapter( columnDefaults );
column.setTable( tableName );
target.addColumn( column );
}
}
private void transferColumn(
@ -1455,14 +1478,19 @@ public class HbmXmlTransformer {
return hbmProp.isUpdate();
}
},
// todo : need to push the table name down into this method to pass along
// todo : need to push the table name down into this method to pass along
null
currentTableName
);
}
private JaxbEmbeddableImpl applyEmbeddable(JaxbEntityMappingsImpl ormRoot, JaxbHbmCompositeAttributeType hbmComponent) {
final String embeddableClassName = hbmComponent.getClazz();
if ( StringHelper.isNotEmpty( embeddableClassName ) ) {
final JaxbEmbeddableImpl existing = jaxbEmbeddableByClassName.get( embeddableClassName );
if ( existing != null ) {
return existing;
}
}
final String embeddableName = determineEmbeddableName( embeddableClassName, hbmComponent.getName() );
final JaxbEmbeddableImpl jaxbEmbeddable = convertEmbeddable(
embeddableName,
@ -1470,6 +1498,12 @@ public class HbmXmlTransformer {
hbmComponent
);
ormRoot.getEmbeddables().add( jaxbEmbeddable );
if ( StringHelper.isNotEmpty( embeddableClassName ) ) {
jaxbEmbeddableByClassName.put( embeddableClassName, jaxbEmbeddable );
}
return jaxbEmbeddable;
}
@ -1485,7 +1519,6 @@ public class HbmXmlTransformer {
embeddable.setAttributes( new JaxbEmbeddableAttributesContainerImpl() );
transferAttributes( hbmComponent.getAttributes(), embeddable.getAttributes() );
return embeddable;
}
@ -1602,7 +1635,7 @@ public class HbmXmlTransformer {
}
},
ColumnDefaultsBasicImpl.INSTANCE,
null
currentTableName
);
m2o.setName( hbmNode.getName() );
@ -2371,40 +2404,39 @@ public class HbmXmlTransformer {
private void transferJoins(JaxbHbmRootEntityType source, JaxbEntityImpl target) {
for ( JaxbHbmSecondaryTableType hbmJoin : source.getJoin() ) {
if ( !hbmJoin.isInverse() ) {
final JaxbSecondaryTableImpl secondaryTable = new JaxbSecondaryTableImpl();
secondaryTable.setCatalog( hbmJoin.getCatalog() );
secondaryTable.setComment( hbmJoin.getComment() );
secondaryTable.setName( hbmJoin.getTable() );
secondaryTable.setSchema( hbmJoin.getSchema() );
secondaryTable.setOptional( hbmJoin.isOptional() );
if ( hbmJoin.getKey() != null ) {
final JaxbPrimaryKeyJoinColumnImpl joinColumn = new JaxbPrimaryKeyJoinColumnImpl();
joinColumn.setName( hbmJoin.getKey().getColumnAttribute() );
secondaryTable.getPrimaryKeyJoinColumn().add( joinColumn );
}
target.getSecondaryTables().add( secondaryTable );
currentTableName = hbmJoin.getTable();
assert StringHelper.isNotEmpty( currentTableName );
final JaxbSecondaryTableImpl secondaryTable = new JaxbSecondaryTableImpl();
secondaryTable.setCatalog( hbmJoin.getCatalog() );
secondaryTable.setComment( hbmJoin.getComment() );
secondaryTable.setName( hbmJoin.getTable() );
secondaryTable.setSchema( hbmJoin.getSchema() );
secondaryTable.setOptional( hbmJoin.isOptional() );
secondaryTable.setOwned( !hbmJoin.isInverse() );
if ( hbmJoin.getKey() != null ) {
final JaxbPrimaryKeyJoinColumnImpl joinColumn = new JaxbPrimaryKeyJoinColumnImpl();
joinColumn.setName( hbmJoin.getKey().getColumnAttribute() );
secondaryTable.getPrimaryKeyJoinColumn().add( joinColumn );
}
target.getSecondaryTables().add( secondaryTable );
for ( Serializable attributeMapping : hbmJoin.getAttributes() ) {
if ( attributeMapping instanceof JaxbHbmBasicAttributeType ) {
final JaxbBasicImpl prop = transformBasicAttribute( (JaxbHbmBasicAttributeType) attributeMapping );
if ( prop.getColumn() != null ) {
prop.getColumn().setTable( hbmJoin.getTable() );
if ( prop.getColumn() == null && prop.getFormula() == null ) {
prop.setColumn( new JaxbColumnImpl() );
prop.getColumn().setTable( currentTableName );
}
target.getAttributes().getBasicAttributes().add( prop );
}
else if ( attributeMapping instanceof JaxbHbmCompositeAttributeType ) {
throw new MappingException(
"transformation of <component/> as part of <join/> (secondary-table) not yet implemented",
origin
);
else if ( attributeMapping instanceof JaxbHbmCompositeAttributeType hbmComponent ) {
final JaxbEmbeddableImpl jaxbEmbeddable = applyEmbeddable( ormRoot, hbmComponent );
target.getAttributes().getEmbeddedAttributes().add( transformEmbedded( jaxbEmbeddable, hbmComponent ) );
}
else if ( attributeMapping instanceof JaxbHbmManyToOneType ) {
throw new MappingException(
"transformation of <many-to-one/> as part of <join/> (secondary-table) not yet implemented",
origin
);
else if ( attributeMapping instanceof JaxbHbmManyToOneType hbmManyToOne ) {
final JaxbManyToOneImpl jaxbManyToOne = transformManyToOne( hbmManyToOne );
target.getAttributes().getManyToOneAttributes().add( jaxbManyToOne );
}
else if ( attributeMapping instanceof JaxbHbmAnyAssociationType ) {
throw new MappingException(
@ -2433,6 +2465,7 @@ public class HbmXmlTransformer {
for ( JaxbHbmColumnType hbmColumn : hbmM2O.getColumn() ) {
final JaxbJoinColumnImpl joinColumn = new JaxbJoinColumnImpl();
joinColumn.setName( hbmColumn.getName() );
joinColumn.setTable( currentTableName );
joinColumn.setNullable( hbmColumn.isNotNull() == null ? null : !hbmColumn.isNotNull() );
joinColumn.setUnique( hbmColumn.isUnique() );
m2o.getJoinColumns().add( joinColumn );
@ -2447,6 +2480,7 @@ public class HbmXmlTransformer {
else {
joinColumn.setName( hbmM2O.getColumnAttribute() );
}
joinColumn.setTable( currentTableName );
m2o.getJoinColumns().add( joinColumn );
}
@ -2469,6 +2503,9 @@ public class HbmXmlTransformer {
origin
);
currentTableName = "";
subclassEntity.setProxy( hbmSubclass.getProxy() );
transferBaseEntityInformation( hbmSubclass, subclassEntity );
transferEntityElementAttributes( hbmSubclass, subclassEntity );

View File

@ -533,7 +533,8 @@ public interface HibernateAnnotations {
);
OrmAnnotationDescriptor<SecondaryRow,SecondaryRowAnnotation> SECONDARY_ROW = new OrmAnnotationDescriptor<>(
SecondaryRow.class,
SecondaryRowAnnotation.class
SecondaryRowAnnotation.class,
SECONDARY_ROWS
);
OrmAnnotationDescriptor<SelectBeforeUpdate,SelectBeforeUpdateAnnotation> SELECT_BEFORE_UPDATE = new OrmAnnotationDescriptor<>(
SelectBeforeUpdate.class,

View File

@ -42,6 +42,7 @@ import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SecondaryRow;
import org.hibernate.boot.jaxb.mapping.spi.JaxbAssociationOverrideImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbAttributeOverrideImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbBasicMapping;
@ -126,6 +127,8 @@ import org.hibernate.boot.models.annotations.internal.PrimaryKeyJoinColumnsJpaAn
import org.hibernate.boot.models.annotations.internal.RowIdAnnotation;
import org.hibernate.boot.models.annotations.internal.SQLJoinTableRestrictionAnnotation;
import org.hibernate.boot.models.annotations.internal.SQLRestrictionAnnotation;
import org.hibernate.boot.models.annotations.internal.SecondaryRowAnnotation;
import org.hibernate.boot.models.annotations.internal.SecondaryRowsAnnotation;
import org.hibernate.boot.models.annotations.internal.SecondaryTableJpaAnnotation;
import org.hibernate.boot.models.annotations.internal.SecondaryTablesJpaAnnotation;
import org.hibernate.boot.models.annotations.internal.SequenceGeneratorJpaAnnotation;
@ -172,10 +175,13 @@ import jakarta.persistence.UniqueConstraint;
import org.checkerframework.checker.nullness.qual.Nullable;
import static java.lang.Boolean.FALSE;
import static java.lang.Boolean.TRUE;
import static java.util.Collections.emptyList;
import static org.hibernate.boot.models.HibernateAnnotations.FILTER;
import static org.hibernate.boot.models.HibernateAnnotations.FILTER_JOIN_TABLE;
import static org.hibernate.boot.models.HibernateAnnotations.PARAMETER;
import static org.hibernate.boot.models.HibernateAnnotations.SECONDARY_ROW;
import static org.hibernate.boot.models.HibernateAnnotations.SECONDARY_ROWS;
import static org.hibernate.boot.models.HibernateAnnotations.SQL_RESTRICTION;
import static org.hibernate.boot.models.JpaAnnotations.ASSOCIATION_OVERRIDE;
import static org.hibernate.boot.models.JpaAnnotations.ASSOCIATION_OVERRIDES;
@ -1672,16 +1678,29 @@ public class XmlAnnotationHelper {
JpaAnnotations.SECONDARY_TABLES,
xmlDocumentContext.getModelBuildingContext()
);
final SecondaryTable[] tableUsages = new SecondaryTable[jaxbSecondaryTables.size()];
tablesUsage.value( tableUsages );
final SecondaryRowsAnnotation rowsUsage = (SecondaryRowsAnnotation) target.replaceAnnotationUsage(
SECONDARY_ROW,
SECONDARY_ROWS,
xmlDocumentContext.getModelBuildingContext()
);
final SecondaryRow[] rowUsages = new SecondaryRow[jaxbSecondaryTables.size()];
rowsUsage.value( rowUsages );
for ( int i = 0; i < jaxbSecondaryTables.size(); i++ ) {
final SecondaryTableJpaAnnotation tableUsage = SECONDARY_TABLE.createUsage( xmlDocumentContext.getModelBuildingContext() );
tableUsages[i] = tableUsage;
final JaxbSecondaryTableImpl jaxbSecondaryTable = jaxbSecondaryTables.get( i );
tableUsage.apply( jaxbSecondaryTable, xmlDocumentContext );
final SecondaryRowAnnotation rowUsage = SECONDARY_ROW.createUsage( xmlDocumentContext.getModelBuildingContext() );
rowUsages[i] = rowUsage;
rowUsage.table( tableUsage.name() );
rowUsage.optional( jaxbSecondaryTable.isOptional() == TRUE );
rowUsage.owned( jaxbSecondaryTable.isOwned() == TRUE );
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.collections.list;
import jakarta.persistence.Embeddable;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
/**
* @author Steve Ebersole
*/
@Embeddable
public class Category {
private String name;
@ManyToOne
@JoinColumn(name = "owner_fk")
private User owner;
}

View File

@ -0,0 +1,110 @@
/*
* 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.collections.list;
import org.hibernate.cfg.MappingSettings;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.List;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Value;
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;
import static org.assertj.core.api.Assertions.fail;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("JUnitMalformedDeclaration")
public class ListTests {
@Test
@DomainModel( xmlMappings = "mappings/models/hbm/list/hbm.xml" )
void testHbmXml(DomainModelScope domainModelScope) {
final PersistentClass rootBinding = domainModelScope.getDomainModel().getEntityBinding( Root.class.getName() );
validateTags( rootBinding.getProperty( "tags" ) );
validateCategories( rootBinding.getProperty( "categories" ) );
}
@Test
@ServiceRegistry( settings = @Setting( name = MappingSettings.TRANSFORM_HBM_XML, value = "true" ) )
@DomainModel( xmlMappings = "mappings/models/hbm/list/hbm.xml" )
void testTransformation(DomainModelScope domainModelScope) {
final PersistentClass rootBinding = domainModelScope.getDomainModel().getEntityBinding( Root.class.getName() );
validateTags( rootBinding.getProperty( "tags" ) );
validateCategories( rootBinding.getProperty( "categories" ) );
}
private void validateTags(Property tags) {
final List listValue = (List) tags.getValue();
final Value indexValue = listValue.getIndex();
assertThat( indexValue.getColumns() ).hasSize( 1 );
final Value elementValue = listValue.getElement();
assertThat( elementValue.getColumns() ).hasSize( 1 );
}
private void validateCategories(Property categories) {
final List listValue = (List) categories.getValue();
assertThat( listValue.getCollectionTable().getName() ).isEqualTo( "root_categories" );
final BasicValue indexValue = (BasicValue) listValue.getIndex();
assertThat( indexValue.getColumns() ).hasSize( 1 );
final Component elementValue = (Component) listValue.getElement();
assertThat( elementValue.getColumns() ).hasSize( 2 );
assertThat( elementValue.getComponentClass() ).isEqualTo( Category.class );
for ( Property subProperty : elementValue.getProperties() ) {
if ( "name".equals( subProperty.getName() ) ) {
validateCategoryName( subProperty );
}
else if ( "owner".equals( subProperty.getName() ) ) {
validateCategoryOwner( subProperty );
}
else if ( "admins".equals( subProperty.getName() ) ) {
validateAdmins( subProperty );
}
else {
fail( "Unexpected Category property :" + subProperty.getName() );
}
}
}
private void validateCategoryName(Property nameProperty) {
assertThat( nameProperty.getColumns() ).hasSize( 1 );
assertThat( nameProperty.getColumns().get( 0 ).getName() ).isEqualTo( "name" );
assertThat( nameProperty.getValue().getTable().getName() ).isEqualTo( "root_categories" );
}
private void validateCategoryOwner(Property owenerProperty) {
assertThat( owenerProperty.getColumns() ).hasSize( 1 );
assertThat( owenerProperty.getColumns().get( 0 ).getName() ).isEqualTo( "owner_fk" );
assertThat( owenerProperty.getValue().getTable().getName() ).isEqualTo( "root_categories" );
}
private void validateAdmins(Property adminsProperty) {
assertThat( adminsProperty.getColumns() ).hasSize( 1 );
assertThat( adminsProperty.getColumns().get( 0 ).getName() ).isEqualTo( "root_fk" );
final List listValue = (List) adminsProperty.getValue();
assertThat( listValue.getCollectionTable().getName() ).isEqualTo( "root_admins" );
final BasicValue indexValue = (BasicValue) listValue.getIndex();
assertThat( indexValue.getColumns() ).hasSize( 1 );
final ManyToOne element = (ManyToOne) listValue.getElement();
}
}

View File

@ -0,0 +1,59 @@
/*
* 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.collections.list;
import java.util.List;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Basic;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OrderColumn;
/**
* @author Steve Ebersole
*/
@Entity
public class Root {
@Id
private Integer id;
@Basic
private String name;
@ElementCollection
private List<String> tags;
@ElementCollection
@CollectionTable(name="root_categories")
@OrderColumn(name = "position")
private List<String> categories;
@ManyToMany
@CollectionTable(name="root_admins")
private List<User> admins;
protected Root() {
// for Hibernate use
}
public Root(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.collections.list;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Basic;
import jakarta.persistence.Table;
/**
* @author Steve Ebersole
*/
@Entity
@Table(name = "`users`")
public class User {
@Id
private Integer id;
@Basic
private String name;
protected User() {
// for Hibernate use
}
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,18 @@
/*
* 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.join;
import jakarta.persistence.Embeddable;
/**
* @author Steve Ebersole
*/
@Embeddable
public class Data {
private String first;
private String second;
}

View File

@ -0,0 +1,52 @@
/*
* 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.join;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Basic;
import jakarta.persistence.ManyToOne;
/**
*
* @author Steve Ebersole
*/
@Entity
public class Person {
@Id
private Integer id;
@Basic
private String name;
private String stuff;
@ManyToOne
private SupplementalDetails details;
@Embedded
private Data data;
private String datum;
protected Person() {
// for Hibernate use
}
public Person(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.join;
import org.hibernate.cfg.MappingSettings;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
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;
import static org.junit.jupiter.api.Assertions.fail;
/**
* @author Steve Ebersole
*/
@SuppressWarnings("JUnitMalformedDeclaration")
public class SecondaryTableTests {
@Test
@DomainModel(xmlMappings = "mappings/models/hbm/join/hbm.xml")
void baseline(DomainModelScope domainModelScope) {
verifyModel( domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ) );
}
@Test
@DomainModel(xmlMappings = "mappings/models/hbm/join/mapping.xml")
void testMappingXml(DomainModelScope domainModelScope) {
verifyModel( domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ) );
}
@Test
@ServiceRegistry(settings = @Setting(name = MappingSettings.TRANSFORM_HBM_XML, value = "true"))
@DomainModel(xmlMappings = "mappings/models/hbm/join/hbm.xml")
void testJoinTransformations(DomainModelScope domainModelScope) {
verifyModel( domainModelScope.getDomainModel().getEntityBinding( Person.class.getName() ) );
}
private void verifyModel(PersistentClass entityBinding) {
assertThat( entityBinding.getJoins() ).hasSize( 2 );
for ( Join join : entityBinding.getJoins() ) {
if ( "supplemental1".equals( join.getTable().getName() ) ) {
verifySupplemental1( join );
}
else if ( "supplemental2".equals( join.getTable().getName() ) ) {
verifySupplemental2( join );
}
else {
fail( "Unexpected secondary table : " + join.getTable().getName() );
}
}
}
private void verifySupplemental1(Join join) {
assertThat( join.isOptional() ).isFalse();
assertThat( join.isInverse() ).isFalse();
assertThat( join.getKey().getColumns() ).hasSize( 1 );
assertThat( join.getKey().getColumns().get(0).getName() ).isEqualTo( "supp1_fk" );
assertThat( join.getProperties() ).hasSize( 3 );
for ( Property property : join.getProperties() ) {
if ( "stuff".equals( property.getName() ) ) {
validateStuffProperty( property );
}
else if ( "data".equals( property.getName() ) ) {
validateDataProperty( property );
}
else if ( "details".equals( property.getName() ) ) {
validateDetailsProperty( property );
}
else {
fail( "Unexpected property : " + property.getName() );
}
}
}
private void validateStuffProperty(Property property) {
assertThat( property.getValue().getTable().getName() ).isEqualTo( "supplemental1" );
}
private void validateDataProperty(Property property) {
assertThat( property.getValue().getTable().getName() ).isEqualTo( "supplemental1" );
assertThat( property.getValue() ).isInstanceOf( Component.class );
final Component component = (Component) property.getValue();
for ( Property subProperty : component.getProperties() ) {
assertThat( subProperty.getValue().getTable().getName() ).isEqualTo( "supplemental1" );
}
}
private void validateDetailsProperty(Property property) {
assertThat( property.getValue().getTable().getName() ).isEqualTo( "supplemental1" );
}
private void verifySupplemental2(Join join) {
assertThat( join.isOptional() ).isTrue();
assertThat( join.isInverse() ).isTrue();
assertThat( join.getKey().getColumns() ).hasSize( 1 );
assertThat( join.getKey().getColumns().get(0).getName() ).isEqualTo( "supp2_fk" );
assertThat( join.getProperties() ).hasSize( 1 );
assertThat( join.getProperties().get(0).getName() ).isEqualTo( "datum" );
assertThat( join.getProperties().get(0).getValue().getTable().getName() ).isEqualTo( "supplemental2" );
}
}

View File

@ -0,0 +1,22 @@
/*
* 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.join;
import jakarta.persistence.Basic;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
/**
* @author Steve Ebersole
*/
@Entity
public class SupplementalDetails {
@Id
private Integer id;
@Basic
private String name;
}

View File

@ -0,0 +1,13 @@
/*
* 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.
*/
/**
* Tests for transforming {@code <join/>} -> {@code <secondary-table/>}.
*
* @author Steve Ebersole
*/
package org.hibernate.orm.test.boot.models.hbm.join;

View File

@ -0,0 +1,39 @@
<?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.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping
package="org.hibernate.orm.test.boot.models.hbm.join"
default-access="field">
<class name="Person">
<id name="id"/>
<property name="name" type="string"/>
<join table="supplemental1">
<key column="supp1_fk"/>
<property name="stuff" type="string"/>
<many-to-one name="details" entity-name="org.hibernate.orm.test.boot.models.hbm.join.SupplementalDetails" column="details_fk"/>
<component name="data" class="Data">
<property name="first" type="string"/>
<property name="second" type="string"/>
</component>
</join>
<join table="supplemental2" inverse="true" optional="true">
<key column="supp2_fk"/>
<property name="datum" type="string"/>
</join>
</class>
<class name="SupplementalDetails">
<id type="integer"/>
<property name="name" type="string"/>
</class>
</hibernate-mapping>

View File

@ -0,0 +1,57 @@
<?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"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="7.0">
<entity class="org.hibernate.orm.test.boot.models.hbm.join.Person" access="FIELD">
<secondary-table name="supplemental1" optional="false" owned="true">
<primary-key-join-column name="supp1_fk"/>
</secondary-table>
<secondary-table name="supplemental2" optional="true" owned="false">
<primary-key-join-column name="supp2_fk"/>
</secondary-table>
<attributes>
<id name="id"/>
<basic name="name"/>
<basic name="stuff">
<column table="supplemental1"/>
</basic>
<basic name="datum">
<column table="supplemental2"/>
</basic>
<many-to-one name="details" target-entity="org.hibernate.orm.test.boot.models.hbm.join.SupplementalDetails">
<join-column table="supplemental1"/>
</many-to-one>
<embedded name="data">
<target>org.hibernate.orm.test.boot.models.hbm.join.Data</target>
<attribute-override name="first">
<column table="supplemental1"/>
</attribute-override>
<attribute-override name="second">
<column table="supplemental1"/>
</attribute-override>
</embedded>
</attributes>
</entity>
<entity class="org.hibernate.orm.test.boot.models.hbm.join.SupplementalDetails">
<attributes>
<id name="id"/>
<basic name="name"/>
</attributes>
</entity>
<embeddable class="org.hibernate.orm.test.boot.models.hbm.join.Data">
<attributes>
<basic name="first"/>
<basic name="second"/>
</attributes>
</embeddable>
</entity-mappings>

View File

@ -0,0 +1,44 @@
<?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.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping default-access="FIELD">
<class name="org.hibernate.orm.test.boot.models.hbm.collections.list.Root">
<id type="integer"/>
<property name="name" type="string"/>
<list name="tags">
<key column="root_fk"/>
<list-index/>
<element column="txt" type="string"/>
</list>
<list name="categories" table="root_categories">
<key column="root_fk"/>
<list-index column="position"/>
<composite-element class="org.hibernate.orm.test.boot.models.hbm.collections.list.Category">
<property name="name"/>
<many-to-one name="owner" class="org.hibernate.orm.test.boot.models.hbm.collections.list.User" column="owner_fk"/>
</composite-element>
</list>
<list name="admins" table="root_admins">
<key column="root_fk"/>
<list-index/>
<many-to-many class="org.hibernate.orm.test.boot.models.hbm.collections.list.User" column="user_fk"/>
</list>
</class>
<class name="org.hibernate.orm.test.boot.models.hbm.collections.list.User">
<id/>
<property name="name"/>
</class>
</hibernate-mapping>