Skip to content

Commit d2ba448

Browse files
committed
Implement support for using generically typed associations to re-enable inheritance/discriminator tests
1 parent a290e85 commit d2ba448

40 files changed

+891
-121
lines changed

hibernate-core/src/main/java/org/hibernate/cfg/ClassPropertyHolder.java

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,19 +13,25 @@
1313
import jakarta.persistence.JoinTable;
1414

1515
import org.hibernate.AssertionFailure;
16+
import org.hibernate.MappingException;
1617
import org.hibernate.annotations.common.reflection.XClass;
1718
import org.hibernate.annotations.common.reflection.XProperty;
1819
import org.hibernate.boot.spi.MetadataBuildingContext;
1920
import org.hibernate.cfg.annotations.EntityBinder;
2021
import org.hibernate.internal.util.StringHelper;
2122
import org.hibernate.internal.util.collections.CollectionHelper;
23+
import org.hibernate.mapping.Collection;
2224
import org.hibernate.mapping.Component;
25+
import org.hibernate.mapping.IndexedCollection;
2326
import org.hibernate.mapping.Join;
2427
import org.hibernate.mapping.KeyValue;
2528
import org.hibernate.mapping.MappedSuperclass;
2629
import org.hibernate.mapping.PersistentClass;
2730
import org.hibernate.mapping.Property;
31+
import org.hibernate.mapping.SimpleValue;
2832
import org.hibernate.mapping.Table;
33+
import org.hibernate.mapping.ToOne;
34+
import org.hibernate.mapping.Value;
2935

3036
/**
3137
* @author Emmanuel Bernard
@@ -234,7 +240,82 @@ private void addPropertyToPersistentClass(Property prop, XClass declaringClass)
234240
private void addPropertyToMappedSuperclass(Property prop, XClass declaringClass) {
235241
final Class<?> type = getContext().getBootstrapContext().getReflectionManager().toClass( declaringClass );
236242
MappedSuperclass superclass = getContext().getMetadataCollector().getMappedSuperclass( type );
237-
superclass.addDeclaredProperty( prop );
243+
if ( type.getTypeParameters().length == 0 ) {
244+
superclass.addDeclaredProperty( prop );
245+
}
246+
else {
247+
// If the type has type parameters, we have to look up the XClass and actual property again
248+
// because the given XClass has a TypeEnvironment based on the type variable assignments of a subclass
249+
// and that might result in a wrong property type being used for a property which uses a type variable
250+
final XClass actualDeclaringClass = getContext().getBootstrapContext().getReflectionManager().toXClass( type );
251+
for ( XProperty declaredProperty : actualDeclaringClass.getDeclaredProperties( prop.getPropertyAccessorName() ) ) {
252+
if ( prop.getName().equals( declaredProperty.getName() ) ) {
253+
final PropertyData inferredData = new PropertyInferredData(
254+
actualDeclaringClass,
255+
declaredProperty,
256+
null,
257+
getContext().getBootstrapContext().getReflectionManager()
258+
);
259+
final Value originalValue = prop.getValue();
260+
if ( originalValue instanceof SimpleValue ) {
261+
// Avoid copying when the property doesn't depend on a type variable
262+
if ( inferredData.getTypeName().equals( ( (SimpleValue) originalValue ).getTypeName() ) ) {
263+
superclass.addDeclaredProperty( prop );
264+
return;
265+
}
266+
}
267+
// If the property depends on a type variable, we have to copy it and the Value
268+
final Property actualProperty = prop.copy();
269+
actualProperty.setReturnedClassName( inferredData.getTypeName() );
270+
final Value value = actualProperty.getValue().copy();
271+
if ( value instanceof Collection ) {
272+
final Collection collection = (Collection) value;
273+
// The owner is a MappedSuperclass which is not a PersistentClass, so set it to null
274+
// collection.setOwner( null );
275+
collection.setRole( type.getName() + "." + prop.getName() );
276+
// To copy the element and key values, we need to defer setting the type name until the CollectionBinder ran
277+
getContext().getMetadataCollector().addSecondPass(
278+
new SecondPass() {
279+
@Override
280+
public void doSecondPass(Map persistentClasses) throws MappingException {
281+
final Collection initializedCollection = (Collection) originalValue;
282+
final Value element = initializedCollection.getElement().copy();
283+
setTypeName( element, inferredData.getProperty().getElementClass().getName() );
284+
if ( initializedCollection instanceof IndexedCollection ) {
285+
final Value index = ( (IndexedCollection) initializedCollection ).getIndex().copy();
286+
setTypeName( index, inferredData.getProperty().getMapKey().getName() );
287+
( (IndexedCollection) collection ).setIndex( index );
288+
}
289+
collection.setElement( element );
290+
}
291+
}
292+
);
293+
}
294+
else {
295+
setTypeName( value, inferredData.getTypeName() );
296+
}
297+
actualProperty.setValue( value );
298+
superclass.addDeclaredProperty( actualProperty );
299+
break;
300+
}
301+
}
302+
}
303+
}
304+
305+
private void setTypeName(Value value, String typeName) {
306+
if ( value instanceof ToOne ) {
307+
final ToOne toOne = (ToOne) value;
308+
toOne.setReferencedEntityName( typeName );
309+
toOne.setTypeName( typeName );
310+
}
311+
else if ( value instanceof Component ) {
312+
final Component component = (Component) value;
313+
component.setComponentClassName( typeName );
314+
component.setTypeName( typeName );
315+
}
316+
else if ( value instanceof SimpleValue ) {
317+
( (SimpleValue) value ).setTypeName( typeName );
318+
}
238319
}
239320

240321
private void addPropertyToJoin(Property prop, XClass declaringClass, Join join) {

hibernate-core/src/main/java/org/hibernate/id/ExportableColumn.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public ValueImpl(ExportableColumn column, Table table, BasicType type, Database
6363
this.database = database;
6464
}
6565

66+
@Override
67+
public Value copy() {
68+
return new ValueImpl( column, table, type, database );
69+
}
70+
6671
@Override
6772
public int getColumnSpan() {
6873
return 1;

hibernate-core/src/main/java/org/hibernate/mapping/Any.java

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,30 @@ public Any(MetadataBuildingContext buildingContext, Table table, boolean annotat
5757

5858
}
5959

60+
public Any(Any original) {
61+
super( original );
62+
63+
this.metaMapping = original.metaMapping == null ? null : original.metaMapping.copy();
64+
this.keyMapping = original.keyMapping == null ? null : (SimpleValue) original.keyMapping.copy();
65+
66+
// annotations
67+
this.discriminatorDescriptor = original.discriminatorDescriptor == null
68+
? null
69+
: original.discriminatorDescriptor.copy();
70+
this.keyDescriptor = original.keyDescriptor == null ? null : original.keyDescriptor.copy();
71+
72+
// common
73+
this.metaValueToEntityNameMap = original.metaValueToEntityNameMap == null
74+
? null
75+
: new HashMap<>(original.metaValueToEntityNameMap);
76+
this.lazy = original.lazy;
77+
}
78+
79+
@Override
80+
public Any copy() {
81+
return new Any( this );
82+
}
83+
6084
public void addSelectable(Selectable selectable) {
6185
if ( selectable == null ) {
6286
return;
@@ -281,6 +305,18 @@ public MetaValue(
281305
this.selectableConsumer = selectableConsumer;
282306
}
283307

308+
private MetaValue(MetaValue original) {
309+
super( original );
310+
this.typeName = original.typeName;
311+
this.columnName = original.columnName;
312+
this.selectableConsumer = original.selectableConsumer;
313+
}
314+
315+
@Override
316+
public MetaValue copy() {
317+
return new MetaValue( this );
318+
}
319+
284320
@Override
285321
public Type getType() throws MappingException {
286322
return getMetadata().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( typeName );
@@ -365,6 +401,17 @@ public KeyValue(
365401
this.selectableConsumer = selectableConsumer;
366402
}
367403

404+
private KeyValue(KeyValue original) {
405+
super( original );
406+
this.typeName = original.typeName;
407+
this.selectableConsumer = original.selectableConsumer;
408+
}
409+
410+
@Override
411+
public KeyValue copy() {
412+
return new KeyValue( this );
413+
}
414+
368415
@Override
369416
public Type getType() throws MappingException {
370417
return getMetadata().getTypeConfiguration().getBasicTypeRegistry().getRegisteredType( typeName );

hibernate-core/src/main/java/org/hibernate/mapping/Array.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@ public Array(Supplier<ManagedBean<? extends UserCollectionType>> customTypeBeanR
3535
super( customTypeBeanResolver, owner, buildingContext );
3636
}
3737

38+
protected Array(Array original) {
39+
super( original );
40+
this.elementClassName = original.elementClassName;
41+
}
42+
43+
@Override
44+
public Array copy() {
45+
return new Array( this );
46+
}
47+
3848
public Class<?> getElementClass() throws MappingException {
3949
if ( elementClassName == null ) {
4050
final org.hibernate.type.Type elementType = getElement().getType();

hibernate-core/src/main/java/org/hibernate/mapping/Bag.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,15 @@ public Bag(Supplier<ManagedBean<? extends UserCollectionType>> customTypeBeanRes
3434
super( customTypeBeanResolver, owner, buildingContext );
3535
}
3636

37+
private Bag(Collection original) {
38+
super( original );
39+
}
40+
41+
@Override
42+
public Bag copy() {
43+
return new Bag( this );
44+
}
45+
3746
public CollectionType getDefaultCollectionType() {
3847
return new BagType( getRole(), getReferencedPropertyName() );
3948
}

hibernate-core/src/main/java/org/hibernate/mapping/BasicValue.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.hibernate.mapping;
88

9+
import java.util.HashMap;
910
import java.util.Map;
1011
import java.util.Properties;
1112
import java.util.function.Consumer;
@@ -109,6 +110,29 @@ public BasicValue(MetadataBuildingContext buildingContext, Table table) {
109110
buildingContext.getMetadataCollector().registerValueMappingResolver( this::resolve );
110111
}
111112

113+
public BasicValue(BasicValue original) {
114+
super( original );
115+
this.typeConfiguration = original.typeConfiguration;
116+
this.explicitTypeName = original.explicitTypeName;
117+
this.explicitLocalTypeParams = original.explicitLocalTypeParams == null
118+
? null
119+
: new HashMap(original.explicitLocalTypeParams);
120+
this.explicitJavaTypeAccess = original.explicitJavaTypeAccess;
121+
this.explicitJdbcTypeAccess = original.explicitJdbcTypeAccess;
122+
this.explicitMutabilityPlanAccess = original.explicitMutabilityPlanAccess;
123+
this.implicitJavaTypeAccess = original.implicitJavaTypeAccess;
124+
this.enumerationStyle = original.enumerationStyle;
125+
this.temporalPrecision = original.temporalPrecision;
126+
this.timeZoneStorageType = original.timeZoneStorageType;
127+
this.resolvedJavaType = original.resolvedJavaType;
128+
this.ownerName = original.ownerName;
129+
this.propertyName = original.propertyName;
130+
}
131+
132+
@Override
133+
public BasicValue copy() {
134+
return new BasicValue( this );
135+
}
112136

113137
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114138
// Setters - in preparation of resolution

hibernate-core/src/main/java/org/hibernate/mapping/Collection.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,55 @@ protected Collection(
121121
this.buildingContext = buildingContext;
122122
}
123123

124+
protected Collection(Collection original) {
125+
this.buildingContext = original.buildingContext;
126+
this.owner = original.owner;
127+
this.key = original.key == null ? null : (KeyValue) original.key.copy();
128+
this.element = original.element == null ? null : original.element.copy();
129+
this.collectionTable = original.collectionTable;
130+
this.role = original.role;
131+
this.lazy = original.lazy;
132+
this.extraLazy = original.extraLazy;
133+
this.inverse = original.inverse;
134+
this.mutable = original.mutable;
135+
this.subselectLoadable = original.subselectLoadable;
136+
this.cacheConcurrencyStrategy = original.cacheConcurrencyStrategy;
137+
this.cacheRegionName = original.cacheRegionName;
138+
this.orderBy = original.orderBy;
139+
this.where = original.where;
140+
this.manyToManyWhere = original.manyToManyWhere;
141+
this.manyToManyOrderBy = original.manyToManyOrderBy;
142+
this.referencedPropertyName = original.referencedPropertyName;
143+
this.mappedByProperty = original.mappedByProperty;
144+
this.sorted = original.sorted;
145+
this.comparator = original.comparator;
146+
this.comparatorClassName = original.comparatorClassName;
147+
this.orphanDelete = original.orphanDelete;
148+
this.batchSize = original.batchSize;
149+
this.fetchMode = original.fetchMode;
150+
this.optimisticLocked = original.optimisticLocked;
151+
this.typeName = original.typeName;
152+
this.typeParameters = original.typeParameters == null ? null : new Properties(original.typeParameters);
153+
this.customTypeBeanResolver = original.customTypeBeanResolver;
154+
this.collectionPersisterClass = original.collectionPersisterClass;
155+
this.filters.addAll( original.filters );
156+
this.manyToManyFilters.addAll( original.manyToManyFilters );
157+
this.synchronizedTables.addAll( original.synchronizedTables );
158+
this.customSQLInsert = original.customSQLInsert;
159+
this.customInsertCallable = original.customInsertCallable;
160+
this.insertCheckStyle = original.insertCheckStyle;
161+
this.customSQLUpdate = original.customSQLUpdate;
162+
this.customUpdateCallable = original.customUpdateCallable;
163+
this.updateCheckStyle = original.updateCheckStyle;
164+
this.customSQLDelete = original.customSQLDelete;
165+
this.customDeleteCallable = original.customDeleteCallable;
166+
this.deleteCheckStyle = original.deleteCheckStyle;
167+
this.customSQLDeleteAll = original.customSQLDeleteAll;
168+
this.customDeleteAllCallable = original.customDeleteAllCallable;
169+
this.deleteAllCheckStyle = original.deleteAllCheckStyle;
170+
this.loaderName = original.loaderName;
171+
}
172+
124173
public MetadataBuildingContext getBuildingContext() {
125174
return buildingContext;
126175
}

hibernate-core/src/main/java/org/hibernate/mapping/Component.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import java.util.ArrayList;
1010
import java.util.Comparator;
11+
import java.util.HashMap;
1112
import java.util.Iterator;
1213
import java.util.List;
1314
import java.util.Map;
@@ -81,6 +82,27 @@ public Component(MetadataBuildingContext metadata, Table table, PersistentClass
8182
metadata.getMetadataCollector().registerComponent( this );
8283
}
8384

85+
private Component(Component original) {
86+
super( original );
87+
this.properties.addAll( original.properties );
88+
this.originalPropertyOrder = original.originalPropertyOrder.clone();
89+
this.componentClassName = original.componentClassName;
90+
this.embedded = original.embedded;
91+
this.parentProperty = original.parentProperty;
92+
this.owner = original.owner;
93+
this.dynamic = original.dynamic;
94+
this.metaAttributes = original.metaAttributes == null ? null : new HashMap(original.metaAttributes);
95+
this.isKey = original.isKey;
96+
this.roleName = original.roleName;
97+
this.customInstantiator = original.customInstantiator;
98+
this.type = original.type;
99+
}
100+
101+
@Override
102+
public Component copy() {
103+
return new Component( this );
104+
}
105+
84106
public int getPropertySpan() {
85107
return properties.size();
86108
}

hibernate-core/src/main/java/org/hibernate/mapping/DependantBasicValue.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,18 @@ public DependantBasicValue(
2929
this.updateable = updateable;
3030
}
3131

32+
private DependantBasicValue(DependantBasicValue original) {
33+
super( original );
34+
this.referencedValue = original.referencedValue.copy();
35+
this.nullable = original.nullable;
36+
this.updateable = original.updateable;
37+
}
38+
39+
@Override
40+
public DependantBasicValue copy() {
41+
return new DependantBasicValue( this );
42+
}
43+
3244
@Override
3345
protected Resolution<?> buildResolution() {
3446
return referencedValue.resolve();

hibernate-core/src/main/java/org/hibernate/mapping/DependantValue.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ public DependantValue(MetadataBuildingContext buildingContext, Table table, KeyV
2828
this.wrappedValue = prototype;
2929
}
3030

31+
private DependantValue(DependantValue original) {
32+
super( original );
33+
this.wrappedValue = (KeyValue) original.wrappedValue.copy();
34+
this.nullable = original.nullable;
35+
this.updateable = original.updateable;
36+
this.sorted = original.sorted;
37+
}
38+
39+
@Override
40+
public DependantValue copy() {
41+
return new DependantValue( this );
42+
}
43+
3144
public KeyValue getWrappedValue() {
3245
return wrappedValue;
3346
}

0 commit comments

Comments
 (0)