Skip to content

Commit e227699

Browse files
committed
HHH-19978 Support type variable members also in abstract entities
1 parent ee19e15 commit e227699

File tree

16 files changed

+366
-36
lines changed

16 files changed

+366
-36
lines changed

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.hibernate.PropertyNotFoundException;
1414
import org.hibernate.boot.spi.MetadataBuildingContext;
1515
import org.hibernate.boot.spi.SecondPass;
16+
import org.hibernate.mapping.BasicValue;
1617
import org.hibernate.mapping.Collection;
1718
import org.hibernate.mapping.Component;
1819
import org.hibernate.mapping.IndexedCollection;
@@ -29,6 +30,8 @@
2930

3031
import jakarta.persistence.Convert;
3132
import jakarta.persistence.JoinTable;
33+
import org.hibernate.models.spi.TypeDetails;
34+
import org.hibernate.type.internal.ParameterizedTypeImpl;
3235

3336
import static org.hibernate.internal.util.StringHelper.isEmpty;
3437
import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize;
@@ -419,6 +422,34 @@ else if ( value instanceof SimpleValue simpleValue ) {
419422
}
420423
}
421424

425+
static void setType(Value value, TypeDetails type) {
426+
final var typeName = type.getName();
427+
if ( value instanceof ToOne toOne ) {
428+
toOne.setReferencedEntityName( typeName );
429+
toOne.setTypeName( typeName );
430+
}
431+
else if ( value instanceof Component component ) {
432+
// Avoid setting type name for generic components
433+
if ( !component.isGeneric() ) {
434+
component.setComponentClassName( typeName );
435+
}
436+
if ( component.getTypeName() != null ) {
437+
component.setTypeName( typeName );
438+
}
439+
}
440+
else if ( value instanceof SimpleValue simpleValue ) {
441+
if ( value instanceof BasicValue basicValue ) {
442+
basicValue.setImplicitJavaTypeAccess( typeConfiguration ->
443+
type.getTypeKind() == TypeDetails.Kind.PARAMETERIZED_TYPE
444+
? ParameterizedTypeImpl.from( type.asParameterizedType() )
445+
: type.determineRawClass().toJavaClass() );
446+
}
447+
else {
448+
simpleValue.setTypeName( typeName );
449+
}
450+
}
451+
}
452+
422453
private void addPropertyToJoin(Property property, MemberDetails memberDetails, ClassDetails declaringClass, Join join) {
423454
if ( declaringClass != null ) {
424455
final var inheritanceState = inheritanceStatePerClass.get( declaringClass );

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

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.hibernate.boot.spi.InFlightMetadataCollector;
4949
import org.hibernate.boot.spi.MetadataBuildingContext;
5050
import org.hibernate.boot.spi.PropertyData;
51+
import org.hibernate.boot.spi.SecondPass;
5152
import org.hibernate.cfg.AvailableSettings;
5253
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
5354
import org.hibernate.internal.util.StringHelper;
@@ -59,6 +60,7 @@
5960
import org.hibernate.mapping.Join;
6061
import org.hibernate.mapping.JoinedSubclass;
6162
import org.hibernate.mapping.PersistentClass;
63+
import org.hibernate.mapping.Property;
6264
import org.hibernate.mapping.RootClass;
6365
import org.hibernate.mapping.SimpleValue;
6466
import org.hibernate.mapping.SingleTableSubclass;
@@ -71,6 +73,7 @@
7173
import org.hibernate.models.internal.ClassTypeDetailsImpl;
7274
import org.hibernate.models.spi.AnnotationTarget;
7375
import org.hibernate.models.spi.ClassDetails;
76+
import org.hibernate.models.spi.MemberDetails;
7477
import org.hibernate.models.spi.ModelsContext;
7578
import org.hibernate.models.spi.TypeDetails;
7679
import org.hibernate.spi.NavigablePath;
@@ -97,6 +100,7 @@
97100
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
98101
import static org.hibernate.boot.model.internal.BinderHelper.toAliasEntityMap;
99102
import static org.hibernate.boot.model.internal.BinderHelper.toAliasTableMap;
103+
import static org.hibernate.boot.model.internal.ClassPropertyHolder.setType;
100104
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation;
101105
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverrideAnnotation;
102106
import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable;
@@ -122,6 +126,7 @@
122126
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
123127
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
124128
import static org.hibernate.jpa.event.internal.CallbackDefinitionResolver.resolveLifecycleCallbacks;
129+
import static org.hibernate.models.spi.TypeDetailsHelper.resolveRelativeType;
125130
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
126131

127132

@@ -1088,6 +1093,7 @@ private void processIdPropertiesIfNotAlready(
10881093
missingIdProperties.remove( propertyName );
10891094
}
10901095
}
1096+
addGenericProperties( persistentClass, inheritanceState, inheritanceStates );
10911097

10921098
if ( !missingIdProperties.isEmpty() ) {
10931099
throw new AnnotationException( "Entity '" + persistentClass.getEntityName()
@@ -1101,6 +1107,77 @@ else if ( !missingEntityProperties.isEmpty() ) {
11011107
}
11021108
}
11031109

1110+
private void addGenericProperties(
1111+
PersistentClass persistentClass,
1112+
InheritanceState inheritanceState,
1113+
Map<ClassDetails, InheritanceState> inheritanceStates) {
1114+
if ( persistentClass.isAbstract() == null || !persistentClass.isAbstract() ) {
1115+
var superclass = persistentClass.getSuperPersistentClass();
1116+
while ( superclass != null ) {
1117+
for ( Property declaredProperty : superclass.getDeclaredProperties() ) {
1118+
if ( declaredProperty.isGeneric() ) {
1119+
final var memberDetails = getMemberDetails( inheritanceState, inheritanceStates, declaredProperty, superclass );
1120+
final var typeDetails = resolveRelativeType( memberDetails.getType(), inheritanceState.getClassDetails() );
1121+
final var returnedClassName = typeDetails.getName();
1122+
final var actualProperty = declaredProperty.copy();
1123+
actualProperty.setGeneric( false );
1124+
actualProperty.setGenericSpecialization( true );
1125+
actualProperty.setReturnedClassName( returnedClassName );
1126+
final var value = actualProperty.getValue().copy();
1127+
setType( value, typeDetails );
1128+
actualProperty.setValue( value );
1129+
persistentClass.addProperty( actualProperty );
1130+
if ( value instanceof BasicValue basicValue ) {
1131+
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
1132+
final BasicValue originalBasicValue = (BasicValue) declaredProperty.getValue();
1133+
metadataCollector.addSecondPass( new SecondPass() {
1134+
@Override
1135+
public void doSecondPass(Map<String, PersistentClass> persistentClasses)
1136+
throws MappingException {
1137+
basicValue.setExplicitTypeParams( originalBasicValue.getExplicitTypeParams() );
1138+
basicValue.setTypeParameters( originalBasicValue.getTypeParameters() );
1139+
basicValue.setJpaAttributeConverterDescriptor( originalBasicValue.getJpaAttributeConverterDescriptor() );
1140+
// Don't copy over the implicit java type access, since we figure that out in ClassPropertyHolder#setType
1141+
// basicValue.setImplicitJavaTypeAccess( originalBasicValue.getImplicitJavaTypeAccess() );
1142+
basicValue.setExplicitJavaTypeAccess( originalBasicValue.getExplicitJavaTypeAccess() );
1143+
basicValue.setExplicitJdbcTypeAccess( originalBasicValue.getExplicitJdbcTypeAccess() );
1144+
basicValue.setExplicitMutabilityPlanAccess( originalBasicValue.getExplicitMutabilityPlanAccess() );
1145+
basicValue.setEnumerationStyle( originalBasicValue.getEnumeratedType() );
1146+
basicValue.setTimeZoneStorageType( originalBasicValue.getTimeZoneStorageType() );
1147+
basicValue.setTemporalPrecision( originalBasicValue.getTemporalPrecision() );
1148+
if ( originalBasicValue.isLob() ) {
1149+
basicValue.makeLob();
1150+
}
1151+
if ( originalBasicValue.isNationalized() ) {
1152+
basicValue.makeNationalized();
1153+
}
1154+
}
1155+
} );
1156+
metadataCollector.registerValueMappingResolver( basicValue::resolve );
1157+
}
1158+
}
1159+
}
1160+
1161+
superclass = superclass.getSuperPersistentClass();
1162+
}
1163+
}
1164+
}
1165+
1166+
private static MemberDetails getMemberDetails(InheritanceState inheritanceState, Map<ClassDetails, InheritanceState> inheritanceStates, Property declaredProperty, PersistentClass superclass) {
1167+
var superclassDetails = inheritanceState.getClassDetails().getSuperClass();
1168+
while ( !superclass.getClassName().equals( superclassDetails.getClassName()) ) {
1169+
superclassDetails = superclassDetails.getSuperClass();
1170+
}
1171+
final var superclassInheritanceState = inheritanceStates.get( superclassDetails );
1172+
final var elementsToProcess = superclassInheritanceState.getElementsToProcess();
1173+
for ( PropertyData element : elementsToProcess.getElements() ) {
1174+
if ( declaredProperty.getName().equals( element.getPropertyName() ) ) {
1175+
return element.getAttributeMember();
1176+
}
1177+
}
1178+
throw new IllegalArgumentException("Couldn't find PropertyData for [" + declaredProperty.getName() + "] in class: " + declaredProperty.getPersistentClass().getClassName() );
1179+
}
1180+
11041181
private static String getMissingPropertiesString(Set<String> propertyNames) {
11051182
final var missingProperties = new StringBuilder();
11061183
for ( String propertyName : propertyNames ) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ public Boolean hasIdClassOrEmbeddedId() {
223223
* guessing from @Id or @EmbeddedId presence if not specified.
224224
* Change EntityBinder by side effect
225225
*/
226-
private ElementsToProcess getElementsToProcess() {
226+
ElementsToProcess getElementsToProcess() {
227227
if ( elementsToProcess == null ) {
228228
final var inheritanceState = inheritanceStatePerClass.get( classDetails );
229229
assert !inheritanceState.isEmbeddableSuperclass();

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ public class PropertyBinder {
109109

110110
private String name;
111111
private String returnedClassName;
112+
private boolean generic;
113+
private boolean genericSpecialization;
112114
private boolean lazy;
113115
private String lazyGroup;
114116
private AccessType accessType;
@@ -166,6 +168,14 @@ private void setReturnedClassName(String returnedClassName) {
166168
this.returnedClassName = returnedClassName;
167169
}
168170

171+
public void setGeneric(boolean generic) {
172+
this.generic = generic;
173+
}
174+
175+
public void setGenericSpecialization(boolean genericSpecialization) {
176+
this.genericSpecialization = genericSpecialization;
177+
}
178+
169179
public void setLazy(boolean lazy) {
170180
this.lazy = lazy;
171181
}
@@ -441,6 +451,8 @@ public Property makeProperty() {
441451
property.setCascade( cascadeTypes );
442452
property.setPropertyAccessorName( accessType.getType() );
443453
property.setReturnedClassName( returnedClassName );
454+
property.setGeneric( generic );
455+
property.setGenericSpecialization( genericSpecialization );
444456
// property.setPropertyAccessStrategy( propertyAccessStrategy );
445457
handleValueGeneration( property );
446458
handleNaturalId( property );
@@ -1002,6 +1014,11 @@ private AnnotatedColumns bindBasicOrComposite(
10021014
ClassDetails returnedClass) {
10031015
final var memberDetails = inferredData.getAttributeMember();
10041016

1017+
if ( propertyHolder.isEntity() && propertyHolder.getPersistentClass().isAbstract() ) {
1018+
// When the type of the member is a type variable, we mark it as generic for abstract classes
1019+
setGeneric( inferredData.getClassOrElementType().getTypeKind() == TypeDetails.Kind.TYPE_VARIABLE );
1020+
}
1021+
10051022
// overrides from @MapsId or @IdClass if needed
10061023
final PropertyData overridingProperty =
10071024
overridingProperty( propertyHolder, isIdentifierMapper, memberDetails );

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ public BasicValue(BasicValue original) {
145145
this.isSoftDelete = original.isSoftDelete;
146146
this.softDeleteStrategy = original.softDeleteStrategy;
147147
this.aggregateColumn = original.aggregateColumn;
148+
this.jdbcTypeCode = original.jdbcTypeCode;
148149
}
149150

150151
@Override
@@ -215,6 +216,22 @@ public void setImplicitJavaTypeAccess(Function<TypeConfiguration, java.lang.refl
215216
this.implicitJavaTypeAccess = implicitJavaTypeAccess;
216217
}
217218

219+
public Function<TypeConfiguration, BasicJavaType<?>> getExplicitJavaTypeAccess() {
220+
return explicitJavaTypeAccess;
221+
}
222+
223+
public Function<TypeConfiguration, JdbcType> getExplicitJdbcTypeAccess() {
224+
return explicitJdbcTypeAccess;
225+
}
226+
227+
public Function<TypeConfiguration, MutabilityPlan<?>> getExplicitMutabilityPlanAccess() {
228+
return explicitMutabilityPlanAccess;
229+
}
230+
231+
public Function<TypeConfiguration, java.lang.reflect.Type> getImplicitJavaTypeAccess() {
232+
return implicitJavaTypeAccess;
233+
}
234+
218235
public Selectable getColumn() {
219236
return getColumnSpan() == 0 ? null : getColumn( 0 );
220237
}
@@ -1030,6 +1047,10 @@ public void setExplicitTypeParams(Map<String,String> explicitLocalTypeParams) {
10301047
this.explicitLocalTypeParams = explicitLocalTypeParams;
10311048
}
10321049

1050+
public Map<String, String> getExplicitTypeParams() {
1051+
return explicitLocalTypeParams;
1052+
}
1053+
10331054
public void setExplicitTypeName(String typeName) {
10341055
this.explicitTypeName = typeName;
10351056
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ public static void checkPropertyColumnDuplication(
106106
List<Property> properties,
107107
String owner) throws MappingException {
108108
for ( var property : properties ) {
109-
if ( property.isUpdatable() || property.isInsertable() ) {
109+
if ( ( property.isUpdatable() || property.isInsertable() ) && !property.isGenericSpecialization() ) {
110110
property.getValue().checkColumnDuplication( distinctColumns, owner );
111111
}
112112
}

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,8 @@ public boolean isExplicitPolymorphism() {
362362

363363
public abstract List<Property> getPropertyClosure();
364364

365+
public abstract List<Property> getAllPropertyClosure();
366+
365367
public abstract List<Table> getTableClosure();
366368

367369
public abstract List<KeyValue> getKeyClosure();
@@ -722,6 +724,23 @@ public int getJoinClosureSpan() {
722724
}
723725

724726
public int getPropertyClosureSpan() {
727+
int span = 0;
728+
for ( Property property : properties ) {
729+
if ( !property.isGeneric() ) {
730+
span += 1;
731+
}
732+
}
733+
for ( var join : joins ) {
734+
for ( Property property : join.getProperties() ) {
735+
if ( !property.isGeneric() ) {
736+
span += 1;
737+
}
738+
}
739+
}
740+
return span;
741+
}
742+
743+
public int getAllPropertyClosureSpan() {
725744
int span = properties.size();
726745
for ( var join : joins ) {
727746
span += join.getPropertySpan();
@@ -754,6 +773,23 @@ public int getJoinNumber(Property prop) {
754773
* @return A list over the "normal" properties.
755774
*/
756775
public List<Property> getProperties() {
776+
final ArrayList<Property> list = new ArrayList<>();
777+
for ( Property property : properties ) {
778+
if ( !property.isGeneric() ) {
779+
list.add( property );
780+
}
781+
}
782+
for ( var join : joins ) {
783+
for ( Property property : join.getProperties() ) {
784+
if ( !property.isGeneric() ) {
785+
list.add( property );
786+
}
787+
}
788+
}
789+
return list;
790+
}
791+
792+
public List<Property> getAllProperties() {
757793
final ArrayList<List<Property>> list = new ArrayList<>();
758794
list.add( properties );
759795
for ( var join : joins ) {

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ public class Property implements Serializable, MetaAttributable {
7171
private PersistentClass persistentClass;
7272
private boolean naturalIdentifier;
7373
private boolean isGeneric;
74+
private boolean isGenericSpecialization;
7475
private boolean lob;
7576
private java.util.List<CallbackDefinition> callbackDefinitions;
7677
private String returnedClassName;
@@ -490,6 +491,14 @@ public void setGeneric(boolean generic) {
490491
this.isGeneric = generic;
491492
}
492493

494+
public boolean isGenericSpecialization() {
495+
return isGenericSpecialization;
496+
}
497+
498+
public void setGenericSpecialization(boolean genericSpecialization) {
499+
isGenericSpecialization = genericSpecialization;
500+
}
501+
493502
public boolean isLob() {
494503
return lob;
495504
}
@@ -554,6 +563,7 @@ public Property copy() {
554563
property.setPersistentClass( getPersistentClass() );
555564
property.setNaturalIdentifier( isNaturalIdentifier() );
556565
property.setGeneric( isGeneric() );
566+
property.setGenericSpecialization( isGenericSpecialization() );
557567
property.setLob( isLob() );
558568
property.addCallbackDefinitions( getCallbackDefinitions() );
559569
property.setReturnedClassName( getReturnedClassName() );

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,11 @@ public List<Property> getPropertyClosure() {
134134
return getProperties();
135135
}
136136

137+
@Override
138+
public List<Property> getAllPropertyClosure() {
139+
return getAllProperties();
140+
}
141+
137142
@Override
138143
public List<Table> getTableClosure() {
139144
return List.of( getTable() );

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,13 @@ public SimpleValue(MetadataBuildingContext buildingContext, Table table) {
121121
protected SimpleValue(SimpleValue original) {
122122
this.buildingContext = original.buildingContext;
123123
this.metadata = original.metadata;
124-
this.columns.addAll( original.columns );
124+
for ( Selectable selectable : original.columns ) {
125+
if ( selectable instanceof Column column ) {
126+
final Column newColumn = column.clone();
127+
newColumn.setValue( this );
128+
this.columns.add( newColumn );
129+
}
130+
}
125131
this.insertability.addAll( original.insertability );
126132
this.updatability.addAll( original.updatability );
127133
this.partitionKey = original.partitionKey;

0 commit comments

Comments
 (0)