From 2f2a005cbdd72d2dd6556e896d18eec79f5bce8d Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 27 Aug 2025 10:59:57 +0200 Subject: [PATCH 1/2] HHH-19739 Testcase for duplicate unowned one-to-one attribute --- .../inheritance/MultipleInheritanceTest.java | 134 ++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/MultipleInheritanceTest.java diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/MultipleInheritanceTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/MultipleInheritanceTest.java new file mode 100644 index 000000000000..b0db2ccd65f7 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/inheritance/MultipleInheritanceTest.java @@ -0,0 +1,134 @@ +/* + * 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.inheritance; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.MappedSuperclass; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; + +import org.hibernate.annotations.NotFound; +import org.hibernate.annotations.NotFoundAction; + +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.junit.Test; + +import java.io.Serializable; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class MultipleInheritanceTest extends BaseCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + CarOptional.class, + CarPart.class, + BasicCar.class, + SuperCar.class, + Car.class + }; + } + + @Test + public void test() { + inTransaction( session -> { + + Car car = new Car(); + CarPart carPart = new CarPart(); + + + CarPK id = new CarPK(); + id.carId1 = "1"; + carPart.id = id; + session.persist( carPart ); + + car.id = id; + car.parts = carPart; + ((BasicCar) car).parts = carPart; + session.persist( car ); + session.flush(); + session.clear(); + + Car loadedCar = session.find( Car.class, id ); + assertNotNull( loadedCar.parts ); + } ); + } + + @Embeddable + public static class CarPK implements Serializable { + @Column(name = "CAR_ID_1") + protected String carId1; + } + + @Entity(name = "BasicCar") + @Inheritance(strategy = InheritanceType.SINGLE_TABLE) + public static class BasicCar { + @EmbeddedId + protected CarPK id; + + @OneToOne + @JoinColumn(name = "CAR_ID_1", referencedColumnName = "CAR_ID_1", insertable = false, updatable = false) + CarPart parts; + } + + @Entity(name = "SuperCar") + public static class SuperCar extends BasicCar { + @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true) +// @JoinColumn(name = "CAR_ID_1", referencedColumnName = "CAR_ID_1") + private List optionals; + } + + @MappedSuperclass + public static abstract class AbstractCar extends BasicCar { + @OneToOne + @NotFound(action = NotFoundAction.IGNORE) + @JoinColumn(name = "CAR_ID_1", referencedColumnName = "CAR_ID_1", insertable = false, updatable = false) + CarPart parts ; + } + + @Entity(name = "CarPart") + public static class CarPart { + @EmbeddedId + private CarPK id; + + String name; + } + + @Entity(name = "Car") + public static class Car extends AbstractCar { + + } + + + + @Entity(name = "CarOptional") + public static class CarOptional { + + @EmbeddedId + private CarOptionalPK id; + + private String name; + + @Embeddable + public class CarOptionalPK implements Serializable { + + @Column(name = "OPTIONAL_ID1") + private String id1; + + } + } +} From 0413cb52f3dd32a21200af27304f7fc83c60b614 Mon Sep 17 00:00:00 2001 From: Christian Beikov Date: Wed, 5 Nov 2025 18:02:58 +0100 Subject: [PATCH 2/2] HHH-19739 Construct property for OneToOne early to allow field deduplication --- .../model/internal/ClassPropertyHolder.java | 13 +++ .../internal/CollectionPropertyHolder.java | 5 + .../internal/ComponentPropertyHolder.java | 10 ++ .../model/internal/OneToOneSecondPass.java | 98 ++++------------ .../boot/model/internal/PropertyHolder.java | 2 + .../boot/model/internal/ToOneBinder.java | 106 ++++++++++++++---- .../hibernate/mapping/PersistentClass.java | 14 +++ .../java/org/hibernate/mapping/Subclass.java | 15 +++ 8 files changed, 169 insertions(+), 94 deletions(-) diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java index 995d43a6e763..720e9e76614b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java @@ -224,6 +224,19 @@ public void addProperty(Property prop, XClass declaringClass) { } } + @Override + public void movePropertyToJoin(Property property, Join join, XClass declaringClass) { + if ( property.getValue() instanceof Component ) { + //TODO handle quote and non quote table comparison + final String tableName = property.getValue().getTable().getName(); + if ( getJoinsPerRealTableName().get( tableName ) == join ) { + // Skip moving the property, since it was already added to the join + return; + } + } + persistentClass.movePropertyToJoin( property, join ); + } + @Override public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) { final Join join = entityBinder.addJoin( joinTableAnn, this, noDelayInPkColumnCreation ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionPropertyHolder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionPropertyHolder.java index ca42acc34155..fba3931e83ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionPropertyHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionPropertyHolder.java @@ -286,6 +286,11 @@ public void addProperty(Property prop, XClass declaringClass) { throw new AssertionFailure( "Cannot add property to a collection" ); } + @Override + public void movePropertyToJoin(Property prop, Join join, XClass declaringClass) { + throw new AssertionFailure( "Cannot add property to a collection" ); + } + @Override public KeyValue getIdentifier() { throw new AssertionFailure( "Identifier collection not yet managed" ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ComponentPropertyHolder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ComponentPropertyHolder.java index 3fc789b2f906..b94ea059a71b 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ComponentPropertyHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ComponentPropertyHolder.java @@ -335,6 +335,16 @@ public void addProperty(Property prop, XClass declaringClass) { component.addProperty( prop, declaringClass ); } + @Override + public void movePropertyToJoin(Property prop, Join join, XClass declaringClass) { + // or maybe only throw if component.getTable() != join.getTable() + throw new AnnotationException( + "Embeddable class '" + component.getComponentClassName() + + "' has an unowned @OneToOne property " + prop.getName() + + "mapped to a join table which is unsupported" + ); + } + @Override public KeyValue getIdentifier() { return component.getOwner().getIdentifier(); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/OneToOneSecondPass.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/OneToOneSecondPass.java index fc04221faaa1..4baa1dc1f25f 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/OneToOneSecondPass.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/OneToOneSecondPass.java @@ -27,6 +27,7 @@ import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; import org.hibernate.mapping.SortableValue; +import org.hibernate.mapping.Value; import org.hibernate.type.ForeignKeyDirection; import jakarta.persistence.ForeignKey; @@ -34,11 +35,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.checkMappedByType; import static org.hibernate.boot.model.internal.BinderHelper.findPropertyByName; import static org.hibernate.boot.model.internal.BinderHelper.getPath; -import static org.hibernate.boot.model.internal.ToOneBinder.bindForeignKeyNameAndDefinition; -import static org.hibernate.boot.model.internal.ToOneBinder.defineFetchingStrategy; import static org.hibernate.internal.util.StringHelper.qualify; -import static org.hibernate.type.ForeignKeyDirection.FROM_PARENT; -import static org.hibernate.type.ForeignKeyDirection.TO_PARENT; /** * We have to handle {@link jakarta.persistence.OneToOne} associations @@ -46,96 +43,55 @@ */ public class OneToOneSecondPass implements SecondPass { private final MetadataBuildingContext buildingContext; + private final OneToOne oneToOne; + private final PropertyBinder binder; + private final Property property; private final String mappedBy; private final String ownerEntity; private final PropertyHolder propertyHolder; - private final NotFoundAction notFoundAction; private final PropertyData inferredData; - private final OnDeleteAction onDeleteAction; - private final boolean optional; - private final String cascadeStrategy; + private final NotFoundAction notFoundAction; private final AnnotatedJoinColumns joinColumns; - private final String referencedEntityName; private final boolean annotatedEntity; public OneToOneSecondPass( + PropertyBinder binder, + Property property, + OneToOne oneToOne, String mappedBy, String ownerEntity, PropertyHolder propertyHolder, PropertyData inferredData, - String referencedEntityName, boolean annotatedEntity, NotFoundAction notFoundAction, - OnDeleteAction onDeleteAction, - boolean optional, - String cascadeStrategy, AnnotatedJoinColumns columns, MetadataBuildingContext buildingContext) { + this.binder = binder; + this.property = property; + this.oneToOne = oneToOne; this.ownerEntity = ownerEntity; this.mappedBy = mappedBy; this.propertyHolder = propertyHolder; - this.referencedEntityName = referencedEntityName; this.buildingContext = buildingContext; this.notFoundAction = notFoundAction; this.inferredData = inferredData; this.annotatedEntity = annotatedEntity; - this.onDeleteAction = onDeleteAction; - this.optional = optional; - this.cascadeStrategy = cascadeStrategy; this.joinColumns = columns; } @Override public void doSecondPass(Map persistentClasses) throws MappingException { - final OneToOne value = new OneToOne( - buildingContext, - propertyHolder.getTable(), - propertyHolder.getPersistentClass() - ); - final String propertyName = inferredData.getPropertyName(); - value.setPropertyName( propertyName ); - value.setReferencedEntityName( referencedEntityName ); - XProperty property = inferredData.getProperty(); - defineFetchingStrategy( value, property, inferredData, propertyHolder ); - //value.setFetchMode( fetchMode ); - value.setOnDeleteAction( onDeleteAction ); - //value.setLazy( fetchMode != FetchMode.JOIN ); - - value.setConstrained( !optional ); - value.setForeignKeyType( getForeignKeyDirection() ); - bindForeignKeyNameAndDefinition( value, property, property.getAnnotation( ForeignKey.class ), buildingContext ); - - final PropertyBinder binder = new PropertyBinder(); - binder.setName( propertyName ); - binder.setProperty( property ); - binder.setValue( value ); - binder.setCascade( cascadeStrategy ); - binder.setAccessType( inferredData.getDefaultAccess() ); - binder.setBuildingContext( buildingContext ); - binder.setHolder( propertyHolder ); - - final LazyGroup lazyGroupAnnotation = property.getAnnotation( LazyGroup.class ); - if ( lazyGroupAnnotation != null ) { - binder.setLazyGroup( lazyGroupAnnotation.value() ); - } - - final Property result = binder.makeProperty(); - result.setOptional( optional ); if ( mappedBy == null ) { - bindOwned( persistentClasses, value, propertyName, result ); + bindOwned( persistentClasses, oneToOne, inferredData.getPropertyName() ); } else { - bindUnowned( persistentClasses, value, result ); + bindUnowned( persistentClasses, oneToOne ); } - binder.callAttributeBindersInSecondPass( result ); - value.sortProperties(); - } - - private ForeignKeyDirection getForeignKeyDirection() { - return mappedBy == null ? FROM_PARENT : TO_PARENT; + binder.callAttributeBindersInSecondPass( property ); + oneToOne.sortProperties(); } - private void bindUnowned(Map persistentClasses, OneToOne oneToOne, Property property) { + private void bindUnowned(Map persistentClasses, OneToOne oneToOne) { oneToOne.setMappedByProperty( mappedBy ); final String targetEntityName = oneToOne.getReferencedEntityName(); final PersistentClass targetEntity = persistentClasses.get( targetEntityName ); @@ -147,13 +103,11 @@ private void bindUnowned(Map persistentClasses, OneToOn + "' targets the type '" + targetEntityName + "'" + problem ); } final Property targetProperty = targetProperty( oneToOne, targetEntity ); - if ( targetProperty.getValue() instanceof OneToOne ) { - propertyHolder.addProperty( property, inferredData.getDeclaringClass() ); - } - else if ( targetProperty.getValue() instanceof ManyToOne ) { - bindTargetManyToOne( persistentClasses, oneToOne, property, targetEntity, targetProperty ); + final Value targetPropertyValue = targetProperty.getValue(); + if ( targetPropertyValue instanceof ManyToOne ) { + bindTargetManyToOne( persistentClasses, oneToOne, targetEntity, targetProperty ); } - else { + else if ( !(targetPropertyValue instanceof OneToOne) ) { throw new AnnotationException( "Association '" + getPath( propertyHolder, inferredData ) + "' is 'mappedBy' a property named '" + mappedBy + "' of the target entity type '" + targetEntityName @@ -171,7 +125,6 @@ else if ( targetProperty.getValue() instanceof ManyToOne ) { private void bindTargetManyToOne( Map persistentClasses, OneToOne oneToOne, - Property property, PersistentClass targetEntity, Property targetProperty) { Join otherSideJoin = null; @@ -201,10 +154,9 @@ private void bindTargetManyToOne( copy.setValue( manyToOne ); manyToOne.addColumn( copy ); } - mappedByJoin.addProperty( property ); - } - else { - propertyHolder.addProperty( property, inferredData.getDeclaringClass() ); + // The property was added to the propertyHolder eagerly to have knowledge about this property, + // in order for de-duplication to kick in, but we move it to a join if necessary + propertyHolder.movePropertyToJoin( property, mappedByJoin, inferredData.getDeclaringClass() ); } oneToOne.setReferencedPropertyName( mappedBy ); @@ -243,8 +195,7 @@ private Property targetProperty(OneToOne oneToOne, PersistentClass targetEntity) private void bindOwned( Map persistentClasses, OneToOne oneToOne, - String propertyName, - Property property) { + String propertyName) { final ToOneFkSecondPass secondPass = new ToOneFkSecondPass( oneToOne, joinColumns, @@ -256,7 +207,6 @@ private void bindOwned( ); secondPass.doSecondPass(persistentClasses); //no column associated since it's a one to one - propertyHolder.addProperty( property, inferredData.getDeclaringClass() ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyHolder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyHolder.java index ec486bef2087..6275e0cb36b6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyHolder.java @@ -37,6 +37,8 @@ public interface PropertyHolder { void addProperty(Property prop, AnnotatedColumns columns, XClass declaringClass); + void movePropertyToJoin(Property prop, Join join, XClass declaringClass); + KeyValue getIdentifier(); /** diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java index 5af830477815..19a3d20a7743 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/ToOneBinder.java @@ -20,6 +20,7 @@ import org.hibernate.annotations.FetchProfileOverrides; import org.hibernate.annotations.LazyToOne; import org.hibernate.annotations.LazyToOneOption; +import org.hibernate.annotations.LazyGroup; import org.hibernate.annotations.NotFound; import org.hibernate.annotations.NotFoundAction; import org.hibernate.annotations.OnDelete; @@ -33,6 +34,7 @@ import org.hibernate.internal.CoreMessageLogger; import org.hibernate.mapping.Join; import org.hibernate.mapping.KeyValue; +import org.hibernate.mapping.Property; import org.hibernate.mapping.SimpleValue; import org.hibernate.mapping.ToOne; @@ -61,6 +63,8 @@ import static org.hibernate.internal.util.StringHelper.isNotEmpty; import static org.hibernate.internal.util.StringHelper.nullIfEmpty; import static org.hibernate.internal.util.StringHelper.qualify; +import static org.hibernate.type.ForeignKeyDirection.FROM_PARENT; +import static org.hibernate.type.ForeignKeyDirection.TO_PARENT; /** * Responsible for interpreting {@link ManyToOne} and {@link OneToOne} associations @@ -527,30 +531,21 @@ private static void bindOneToOne( //column.getTable() => persistentClass.getTable() final String propertyName = inferredData.getPropertyName(); LOG.tracev( "Fetching {0} with {1}", propertyName, fetchMode ); - if ( isMapToPK( joinColumns, propertyHolder, trueOneToOne ) - || mappedBy != null ) { - //is a true one-to-one - //FIXME referencedColumnName ignored => ordering may fail. - final OneToOneSecondPass secondPass = new OneToOneSecondPass( - mappedBy, - propertyHolder.getEntityName(), - propertyHolder, - inferredData, - getReferenceEntityName( inferredData, targetEntity, context ), - isTargetAnnotatedEntity( targetEntity, annotatedProperty, context ), - notFoundAction, - cascadeOnDelete, - optional, + if ( mappedBy != null || isMapToPK( joinColumns, propertyHolder, trueOneToOne ) ) { + bindTrueOneToOne( cascadeStrategy, joinColumns, + optional, + notFoundAction, + cascadeOnDelete, + targetEntity, + annotatedProperty, + propertyHolder, + inferredData, + mappedBy, + inSecondPass, context ); - if ( inSecondPass ) { - secondPass.doSecondPass( context.getMetadataCollector().getEntityBindingMap() ); - } - else { - context.getMetadataCollector().addSecondPass( secondPass, mappedBy == null ); - } } else { //has a FK on the table @@ -572,6 +567,77 @@ private static void bindOneToOne( } } + private static void bindTrueOneToOne( + String cascadeStrategy, + AnnotatedJoinColumns joinColumns, + boolean optional, + NotFoundAction notFoundAction, + OnDeleteAction cascadeOnDelete, + XClass targetEntity, + XProperty annotatedProperty, + PropertyHolder propertyHolder, + PropertyData inferredData, + String mappedBy, + boolean inSecondPass, + MetadataBuildingContext context) { + //is a true one-to-one + //FIXME referencedColumnName ignored => ordering may fail. + final org.hibernate.mapping.OneToOne oneToOne = + new org.hibernate.mapping.OneToOne( context, propertyHolder.getTable(), + propertyHolder.getPersistentClass() ); + final String propertyName = inferredData.getPropertyName(); + oneToOne.setPropertyName( propertyName ); + oneToOne.setReferencedEntityName( getReferenceEntityName( inferredData, targetEntity, context ) ); + defineFetchingStrategy( oneToOne, annotatedProperty, inferredData, propertyHolder ); + //value.setFetchMode( fetchMode ); + oneToOne.setOnDeleteAction( cascadeOnDelete ); + //value.setLazy( fetchMode != FetchMode.JOIN ); + + oneToOne.setConstrained( !optional ); + oneToOne.setForeignKeyType( mappedBy == null ? FROM_PARENT : TO_PARENT ); + bindForeignKeyNameAndDefinition( oneToOne, annotatedProperty, + annotatedProperty.getAnnotation( ForeignKey.class ), + context ); + + final PropertyBinder binder = new PropertyBinder(); + binder.setName( inferredData.getPropertyName() ); + binder.setProperty( annotatedProperty ); + binder.setValue( oneToOne ); + binder.setCascade( cascadeStrategy ); + binder.setAccessType( inferredData.getDefaultAccess() ); + binder.setBuildingContext( context ); + binder.setHolder( propertyHolder ); + + final LazyGroup lazyGroupAnnotation = annotatedProperty.getAnnotation( LazyGroup.class ); + if ( lazyGroupAnnotation != null ) { + binder.setLazyGroup( lazyGroupAnnotation.value() ); + } + + final Property property = binder.makeProperty(); + property.setOptional( optional ); + propertyHolder.addProperty( property, joinColumns, inferredData.getDeclaringClass() ); + + final OneToOneSecondPass secondPass = new OneToOneSecondPass( + binder, + property, + oneToOne, + mappedBy, + propertyHolder.getEntityName(), + propertyHolder, + inferredData, + isTargetAnnotatedEntity( targetEntity, annotatedProperty, context ), + notFoundAction, + joinColumns, + context + ); + if ( inSecondPass ) { + secondPass.doSecondPass( context.getMetadataCollector().getEntityBindingMap() ); + } + else { + context.getMetadataCollector().addSecondPass( secondPass, mappedBy == null ); + } + } + private static boolean isMapToPK(AnnotatedJoinColumns joinColumns, PropertyHolder propertyHolder, boolean trueOneToOne) { if ( trueOneToOne ) { return true; diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java index e3faa1fcad73..9d4890ddb2f2 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/PersistentClass.java @@ -287,6 +287,20 @@ public void addProperty(Property property) { property.setPersistentClass( this ); } + @Internal + public void movePropertyToJoin(Property property, Join join) { + assert joins.contains( join ); + assert property.getPersistentClass() == this; + properties.remove( property ); + declaredProperties.remove( property ); + join.addProperty( property ); + } + + @Internal + protected void moveSubclassPropertyToJoin(Property property) { + subclassProperties.remove( property ); + } + @Override public boolean contains(Property property) { return properties.contains( property ); diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java b/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java index 4649c09e1053..401391edc72d 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Subclass.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.List; +import org.hibernate.Internal; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.internal.FilterConfiguration; @@ -110,6 +111,20 @@ public void addProperty(Property property) { getSuperclass().addSubclassProperty( property ); } + @Internal + @Override + public void movePropertyToJoin(Property property, Join join) { + super.movePropertyToJoin( property, join ); + getSuperclass().moveSubclassPropertyToJoin( property ); + } + + @Internal + @Override + protected void moveSubclassPropertyToJoin(Property property) { + super.moveSubclassPropertyToJoin( property ); + getSuperclass().moveSubclassPropertyToJoin( property ); + } + @Override public void addMappedSuperclassProperty(Property property) { super.addMappedSuperclassProperty( property );