Skip to content

Commit 0fa8fed

Browse files
committed
HHH-19739 Construct property for OneToOne early to allow field deduplication
1 parent 365fef7 commit 0fa8fed

File tree

8 files changed

+188
-106
lines changed

8 files changed

+188
-106
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/internal/ClassPropertyHolder.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,19 @@ public void addProperty(Property prop, XClass declaringClass) {
224224
}
225225
}
226226

227+
@Override
228+
public void movePropertyToJoin(Property property, Join join, XClass declaringClass) {
229+
if ( property.getValue() instanceof Component ) {
230+
//TODO handle quote and non quote table comparison
231+
final String tableName = property.getValue().getTable().getName();
232+
if ( getJoinsPerRealTableName().get( tableName ) == join ) {
233+
// Skip moving the property, since it was already added to the join
234+
return;
235+
}
236+
}
237+
persistentClass.movePropertyToJoin( property, join );
238+
}
239+
227240
@Override
228241
public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
229242
Join join = entityBinder.addJoin( joinTableAnn, this, noDelayInPkColumnCreation );

hibernate-core/src/main/java/org/hibernate/boot/model/internal/CollectionPropertyHolder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,11 @@ public void addProperty(Property prop, XClass declaringClass) {
286286
throw new AssertionFailure( "Cannot add property to a collection" );
287287
}
288288

289+
@Override
290+
public void movePropertyToJoin(Property prop, Join join, XClass declaringClass) {
291+
throw new AssertionFailure( "Cannot add property to a collection" );
292+
}
293+
289294
@Override
290295
public KeyValue getIdentifier() {
291296
throw new AssertionFailure( "Identifier collection not yet managed" );

hibernate-core/src/main/java/org/hibernate/boot/model/internal/ComponentPropertyHolder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,16 @@ public void addProperty(Property prop, XClass declaringClass) {
306306
component.addProperty( prop );
307307
}
308308

309+
@Override
310+
public void movePropertyToJoin(Property prop, Join join, XClass declaringClass) {
311+
// or maybe only throw if component.getTable() != join.getTable()
312+
throw new AnnotationException(
313+
"Embeddable class '" + component.getComponentClassName()
314+
+ "' has an unowned @OneToOne property " + prop.getName()
315+
+ "mapped to a join table which is unsupported"
316+
);
317+
}
318+
309319
@Override
310320
public KeyValue getIdentifier() {
311321
return component.getOwner().getIdentifier();

hibernate-core/src/main/java/org/hibernate/boot/model/internal/OneToOneSecondPass.java

Lines changed: 35 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,7 @@
1010

1111
import org.hibernate.AnnotationException;
1212
import org.hibernate.MappingException;
13-
import org.hibernate.annotations.LazyGroup;
1413
import org.hibernate.annotations.NotFoundAction;
15-
import org.hibernate.annotations.OnDeleteAction;
16-
import org.hibernate.annotations.common.reflection.XClass;
17-
import org.hibernate.annotations.common.reflection.XProperty;
1814
import org.hibernate.boot.spi.MetadataBuildingContext;
1915
import org.hibernate.boot.spi.PropertyData;
2016
import org.hibernate.boot.spi.SecondPass;
@@ -28,131 +24,87 @@
2824
import org.hibernate.mapping.PersistentClass;
2925
import org.hibernate.mapping.Property;
3026
import org.hibernate.mapping.SortableValue;
31-
import org.hibernate.type.ForeignKeyDirection;
32-
33-
import jakarta.persistence.ForeignKey;
27+
import org.hibernate.mapping.Value;
3428

3529
import static org.hibernate.boot.model.internal.BinderHelper.checkMappedByType;
3630
import static org.hibernate.boot.model.internal.BinderHelper.findPropertyByName;
3731
import static org.hibernate.boot.model.internal.BinderHelper.getPath;
38-
import static org.hibernate.boot.model.internal.ToOneBinder.bindForeignKeyNameAndDefinition;
39-
import static org.hibernate.boot.model.internal.ToOneBinder.defineFetchingStrategy;
40-
import static org.hibernate.boot.model.internal.ToOneBinder.getReferenceEntityName;
4132
import static org.hibernate.internal.util.StringHelper.qualify;
42-
import static org.hibernate.type.ForeignKeyDirection.FROM_PARENT;
43-
import static org.hibernate.type.ForeignKeyDirection.TO_PARENT;
4433

4534
/**
4635
* We have to handle {@link jakarta.persistence.OneToOne} associations
4736
* in a second pass.
4837
*/
4938
public class OneToOneSecondPass implements SecondPass {
5039
private final MetadataBuildingContext buildingContext;
40+
private final OneToOne oneToOne;
41+
private final PropertyBinder binder;
42+
private final Property property;
5143
private final String mappedBy;
5244
private final String ownerEntity;
5345
private final PropertyHolder propertyHolder;
54-
private final NotFoundAction notFoundAction;
5546
private final PropertyData inferredData;
56-
private final XClass targetEntity;
57-
private final OnDeleteAction onDeleteAction;
58-
private final boolean optional;
59-
private final String cascadeStrategy;
47+
private final NotFoundAction notFoundAction;
6048
private final AnnotatedJoinColumns joinColumns;
49+
private final boolean annotatedEntity;
6150

6251
public OneToOneSecondPass(
52+
PropertyBinder binder,
53+
Property property,
54+
OneToOne oneToOne,
6355
String mappedBy,
6456
String ownerEntity,
65-
@SuppressWarnings("unused") String ownerProperty,
6657
PropertyHolder propertyHolder,
6758
PropertyData inferredData,
68-
XClass targetEntity,
59+
boolean annotatedEntity,
6960
NotFoundAction notFoundAction,
70-
OnDeleteAction onDeleteAction,
71-
boolean optional,
72-
String cascadeStrategy,
7361
AnnotatedJoinColumns columns,
7462
MetadataBuildingContext buildingContext) {
63+
this.binder = binder;
64+
this.property = property;
65+
this.oneToOne = oneToOne;
7566
this.ownerEntity = ownerEntity;
7667
this.mappedBy = mappedBy;
7768
this.propertyHolder = propertyHolder;
7869
this.buildingContext = buildingContext;
7970
this.notFoundAction = notFoundAction;
8071
this.inferredData = inferredData;
81-
this.targetEntity = targetEntity;
82-
this.onDeleteAction = onDeleteAction;
83-
this.optional = optional;
84-
this.cascadeStrategy = cascadeStrategy;
72+
this.annotatedEntity = annotatedEntity;
8573
this.joinColumns = columns;
8674
}
8775

8876
@Override
8977
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
90-
final OneToOne value = new OneToOne(
91-
buildingContext,
92-
propertyHolder.getTable(),
93-
propertyHolder.getPersistentClass()
94-
);
95-
final String propertyName = inferredData.getPropertyName();
96-
value.setPropertyName( propertyName );
97-
final String referencedEntityName = getReferenceEntityName( inferredData, targetEntity, buildingContext );
98-
value.setReferencedEntityName( referencedEntityName );
99-
XProperty property = inferredData.getProperty();
100-
defineFetchingStrategy( value, property, inferredData, propertyHolder );
101-
//value.setFetchMode( fetchMode );
102-
value.setOnDeleteAction( onDeleteAction );
103-
//value.setLazy( fetchMode != FetchMode.JOIN );
104-
105-
value.setConstrained( !optional );
106-
value.setForeignKeyType( getForeignKeyDirection() );
107-
bindForeignKeyNameAndDefinition( value, property, property.getAnnotation( ForeignKey.class ), buildingContext );
108-
109-
final PropertyBinder binder = new PropertyBinder();
110-
binder.setName( propertyName );
111-
binder.setProperty( property );
112-
binder.setValue( value );
113-
binder.setCascade( cascadeStrategy );
114-
binder.setAccessType( inferredData.getDefaultAccess() );
115-
binder.setBuildingContext( buildingContext );
116-
117-
final LazyGroup lazyGroupAnnotation = property.getAnnotation( LazyGroup.class );
118-
if ( lazyGroupAnnotation != null ) {
119-
binder.setLazyGroup( lazyGroupAnnotation.value() );
120-
}
121-
122-
final Property result = binder.makeProperty();
123-
result.setOptional( optional );
12478
if ( mappedBy == null ) {
125-
bindOwned( persistentClasses, value, propertyName, result );
79+
bindOwned( persistentClasses, oneToOne, inferredData.getPropertyName() );
12680
}
12781
else {
128-
bindUnowned( persistentClasses, value, result );
82+
bindUnowned( persistentClasses, oneToOne );
12983
}
130-
value.sortProperties();
84+
oneToOne.sortProperties();
13185
}
13286

133-
private ForeignKeyDirection getForeignKeyDirection() {
134-
return mappedBy == null ? FROM_PARENT : TO_PARENT;
135-
}
136-
137-
private void bindUnowned(Map<String, PersistentClass> persistentClasses, OneToOne oneToOne, Property property) {
87+
private void bindUnowned(Map<String, PersistentClass> persistentClasses, OneToOne oneToOne) {
13888
oneToOne.setMappedByProperty( mappedBy );
139-
final PersistentClass targetEntity = persistentClasses.get( oneToOne.getReferencedEntityName() );
89+
final String targetEntityName = oneToOne.getReferencedEntityName();
90+
final PersistentClass targetEntity = persistentClasses.get( targetEntityName );
14091
if ( targetEntity == null ) {
92+
final String problem = annotatedEntity
93+
? " which does not belong to the same persistence unit"
94+
: " which is not an '@Entity' type";
14195
throw new MappingException( "Association '" + getPath( propertyHolder, inferredData )
142-
+ "' targets unknown entity type '" + oneToOne.getReferencedEntityName() + "'" );
96+
+ "' targets the type '" + targetEntityName + "'" + problem );
14397
}
14498
final Property targetProperty = targetProperty( oneToOne, targetEntity );
145-
if ( targetProperty.getValue() instanceof OneToOne ) {
146-
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
147-
}
148-
else if ( targetProperty.getValue() instanceof ManyToOne ) {
149-
bindTargetManyToOne( persistentClasses, oneToOne, property, targetEntity, targetProperty );
99+
final Value targetPropertyValue = targetProperty.getValue();
100+
if ( targetPropertyValue instanceof ManyToOne ) {
101+
bindTargetManyToOne( persistentClasses, oneToOne, targetEntity, targetProperty );
150102
}
151-
else {
103+
else if ( !(targetPropertyValue instanceof OneToOne) ) {
152104
throw new AnnotationException( "Association '" + getPath( propertyHolder, inferredData )
153-
+ "' is 'mappedBy' a property named '" + mappedBy
154-
+ "' of the target entity type '" + oneToOne.getReferencedEntityName()
155-
+ "' which is not a '@OneToOne' or '@ManyToOne' association" );
105+
+ "' is 'mappedBy' a property named '" + mappedBy
106+
+ "' of the target entity type '" + targetEntityName
107+
+ "' which is not a '@OneToOne' or '@ManyToOne' association" );
156108
}
157109
checkMappedByType(
158110
mappedBy,
@@ -166,7 +118,6 @@ else if ( targetProperty.getValue() instanceof ManyToOne ) {
166118
private void bindTargetManyToOne(
167119
Map<String, PersistentClass> persistentClasses,
168120
OneToOne oneToOne,
169-
Property property,
170121
PersistentClass targetEntity,
171122
Property targetProperty) {
172123
Join otherSideJoin = null;
@@ -196,10 +147,9 @@ private void bindTargetManyToOne(
196147
copy.setValue( manyToOne );
197148
manyToOne.addColumn( copy );
198149
}
199-
mappedByJoin.addProperty( property );
200-
}
201-
else {
202-
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
150+
// The property was added to the propertyHolder eagerly to have knowledge about this property,
151+
// in order for de-duplication to kick in, but we move it to a join if necessary
152+
propertyHolder.movePropertyToJoin( property, mappedByJoin, inferredData.getDeclaringClass() );
203153
}
204154

205155
oneToOne.setReferencedPropertyName( mappedBy );
@@ -235,7 +185,7 @@ private Property targetProperty(OneToOne oneToOne, PersistentClass targetEntity)
235185
+ "' which does not exist in the target entity type '" + oneToOne.getReferencedEntityName() + "'" );
236186
}
237187

238-
private void bindOwned(Map<String, PersistentClass> persistentClasses, OneToOne oneToOne, String propertyName, Property property) {
188+
private void bindOwned(Map<String, PersistentClass> persistentClasses, OneToOne oneToOne, String propertyName) {
239189
final ToOneFkSecondPass secondPass = new ToOneFkSecondPass(
240190
oneToOne,
241191
joinColumns,
@@ -246,7 +196,6 @@ private void bindOwned(Map<String, PersistentClass> persistentClasses, OneToOne
246196
);
247197
secondPass.doSecondPass(persistentClasses);
248198
//no column associated since it's a one to one
249-
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
250199
}
251200

252201
/**

hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyHolder.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ public interface PropertyHolder {
3737

3838
void addProperty(Property prop, AnnotatedColumns columns, XClass declaringClass);
3939

40+
void movePropertyToJoin(Property prop, Join join, XClass declaringClass);
41+
4042
KeyValue getIdentifier();
4143

4244
/**

0 commit comments

Comments
 (0)