Skip to content

Commit 1076ffa

Browse files
committed
HHH-19978 Support type variable members also in abstract entities
1 parent 3a66432 commit 1076ffa

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
@@ -12,6 +12,7 @@
1212
import org.hibernate.AssertionFailure;
1313
import org.hibernate.PropertyNotFoundException;
1414
import org.hibernate.boot.spi.MetadataBuildingContext;
15+
import org.hibernate.mapping.BasicValue;
1516
import org.hibernate.mapping.Collection;
1617
import org.hibernate.mapping.Component;
1718
import org.hibernate.mapping.IndexedCollection;
@@ -28,6 +29,8 @@
2829

2930
import jakarta.persistence.Convert;
3031
import jakarta.persistence.JoinTable;
32+
import org.hibernate.models.spi.TypeDetails;
33+
import org.hibernate.type.internal.ParameterizedTypeImpl;
3134

3235
import static org.hibernate.internal.util.StringHelper.isEmpty;
3336
import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize;
@@ -414,6 +417,34 @@ else if ( value instanceof SimpleValue simpleValue ) {
414417
}
415418
}
416419

420+
static void setType(Value value, TypeDetails type) {
421+
final var typeName = type.getName();
422+
if ( value instanceof ToOne toOne ) {
423+
toOne.setReferencedEntityName( typeName );
424+
toOne.setTypeName( typeName );
425+
}
426+
else if ( value instanceof Component component ) {
427+
// Avoid setting type name for generic components
428+
if ( !component.isGeneric() ) {
429+
component.setComponentClassName( typeName );
430+
}
431+
if ( component.getTypeName() != null ) {
432+
component.setTypeName( typeName );
433+
}
434+
}
435+
else if ( value instanceof SimpleValue simpleValue ) {
436+
if ( value instanceof BasicValue basicValue ) {
437+
basicValue.setImplicitJavaTypeAccess( typeConfiguration ->
438+
type.getTypeKind() == TypeDetails.Kind.PARAMETERIZED_TYPE
439+
? ParameterizedTypeImpl.from( type.asParameterizedType() )
440+
: type.determineRawClass().toJavaClass() );
441+
}
442+
else {
443+
simpleValue.setTypeName( typeName );
444+
}
445+
}
446+
}
447+
417448
private void addPropertyToJoin(Property property, MemberDetails memberDetails, ClassDetails declaringClass, Join join) {
418449
if ( declaringClass != null ) {
419450
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
@@ -81,6 +81,7 @@
8181
import org.hibernate.boot.spi.InFlightMetadataCollector;
8282
import org.hibernate.boot.spi.MetadataBuildingContext;
8383
import org.hibernate.boot.spi.PropertyData;
84+
import org.hibernate.boot.spi.SecondPass;
8485
import org.hibernate.cfg.AvailableSettings;
8586
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
8687
import org.hibernate.internal.util.StringHelper;
@@ -92,6 +93,7 @@
9293
import org.hibernate.mapping.Join;
9394
import org.hibernate.mapping.JoinedSubclass;
9495
import org.hibernate.mapping.PersistentClass;
96+
import org.hibernate.mapping.Property;
9597
import org.hibernate.mapping.RootClass;
9698
import org.hibernate.mapping.SimpleValue;
9799
import org.hibernate.mapping.SingleTableSubclass;
@@ -104,6 +106,7 @@
104106
import org.hibernate.models.internal.ClassTypeDetailsImpl;
105107
import org.hibernate.models.spi.AnnotationTarget;
106108
import org.hibernate.models.spi.ClassDetails;
109+
import org.hibernate.models.spi.MemberDetails;
107110
import org.hibernate.models.spi.ModelsContext;
108111
import org.hibernate.models.spi.TypeDetails;
109112
import org.hibernate.spi.NavigablePath;
@@ -131,6 +134,7 @@
131134
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
132135
import static org.hibernate.boot.model.internal.BinderHelper.toAliasEntityMap;
133136
import static org.hibernate.boot.model.internal.BinderHelper.toAliasTableMap;
137+
import static org.hibernate.boot.model.internal.ClassPropertyHolder.setType;
134138
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation;
135139
import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverrideAnnotation;
136140
import static org.hibernate.boot.model.internal.EmbeddableBinder.fillEmbeddable;
@@ -155,6 +159,7 @@
155159
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
156160
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
157161
import static org.hibernate.jpa.event.internal.CallbackDefinitionResolver.resolveLifecycleCallbacks;
162+
import static org.hibernate.models.spi.TypeDetailsHelper.resolveRelativeType;
158163
import static org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies.EMBEDDED;
159164

160165

@@ -1122,6 +1127,7 @@ private void processIdPropertiesIfNotAlready(
11221127
missingIdProperties.remove( propertyName );
11231128
}
11241129
}
1130+
addGenericProperties( persistentClass, inheritanceState, inheritanceStates );
11251131

11261132
if ( !missingIdProperties.isEmpty() ) {
11271133
throw new AnnotationException( "Entity '" + persistentClass.getEntityName()
@@ -1135,6 +1141,77 @@ else if ( !missingEntityProperties.isEmpty() ) {
11351141
}
11361142
}
11371143

1144+
private void addGenericProperties(
1145+
PersistentClass persistentClass,
1146+
InheritanceState inheritanceState,
1147+
Map<ClassDetails, InheritanceState> inheritanceStates) {
1148+
if ( persistentClass.isAbstract() == null || !persistentClass.isAbstract() ) {
1149+
var superclass = persistentClass.getSuperPersistentClass();
1150+
while ( superclass != null ) {
1151+
for ( Property declaredProperty : superclass.getDeclaredProperties() ) {
1152+
if ( declaredProperty.isGeneric() ) {
1153+
final var memberDetails = getMemberDetails( inheritanceState, inheritanceStates, declaredProperty, superclass );
1154+
final var typeDetails = resolveRelativeType( memberDetails.getType(), inheritanceState.getClassDetails() );
1155+
final var returnedClassName = typeDetails.getName();
1156+
final var actualProperty = declaredProperty.copy();
1157+
actualProperty.setGeneric( false );
1158+
actualProperty.setGenericSpecialization( true );
1159+
actualProperty.setReturnedClassName( returnedClassName );
1160+
final var value = actualProperty.getValue().copy();
1161+
setType( value, typeDetails );
1162+
actualProperty.setValue( value );
1163+
persistentClass.addProperty( actualProperty );
1164+
if ( value instanceof BasicValue basicValue ) {
1165+
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
1166+
final BasicValue originalBasicValue = (BasicValue) declaredProperty.getValue();
1167+
metadataCollector.addSecondPass( new SecondPass() {
1168+
@Override
1169+
public void doSecondPass(Map<String, PersistentClass> persistentClasses)
1170+
throws MappingException {
1171+
basicValue.setExplicitTypeParams( originalBasicValue.getExplicitTypeParams() );
1172+
basicValue.setTypeParameters( originalBasicValue.getTypeParameters() );
1173+
basicValue.setJpaAttributeConverterDescriptor( originalBasicValue.getJpaAttributeConverterDescriptor() );
1174+
// Don't copy over the implicit java type access, since we figure that out in ClassPropertyHolder#setType
1175+
// basicValue.setImplicitJavaTypeAccess( originalBasicValue.getImplicitJavaTypeAccess() );
1176+
basicValue.setExplicitJavaTypeAccess( originalBasicValue.getExplicitJavaTypeAccess() );
1177+
basicValue.setExplicitJdbcTypeAccess( originalBasicValue.getExplicitJdbcTypeAccess() );
1178+
basicValue.setExplicitMutabilityPlanAccess( originalBasicValue.getExplicitMutabilityPlanAccess() );
1179+
basicValue.setEnumerationStyle( originalBasicValue.getEnumeratedType() );
1180+
basicValue.setTimeZoneStorageType( originalBasicValue.getTimeZoneStorageType() );
1181+
basicValue.setTemporalPrecision( originalBasicValue.getTemporalPrecision() );
1182+
if ( originalBasicValue.isLob() ) {
1183+
basicValue.makeLob();
1184+
}
1185+
if ( originalBasicValue.isNationalized() ) {
1186+
basicValue.makeNationalized();
1187+
}
1188+
}
1189+
} );
1190+
metadataCollector.registerValueMappingResolver( basicValue::resolve );
1191+
}
1192+
}
1193+
}
1194+
1195+
superclass = superclass.getSuperPersistentClass();
1196+
}
1197+
}
1198+
}
1199+
1200+
private static MemberDetails getMemberDetails(InheritanceState inheritanceState, Map<ClassDetails, InheritanceState> inheritanceStates, Property declaredProperty, PersistentClass superclass) {
1201+
var superclassDetails = inheritanceState.getClassDetails().getSuperClass();
1202+
while ( !superclass.getClassName().equals( superclassDetails.getClassName()) ) {
1203+
superclassDetails = superclassDetails.getSuperClass();
1204+
}
1205+
final var superclassInheritanceState = inheritanceStates.get( superclassDetails );
1206+
final var elementsToProcess = superclassInheritanceState.getElementsToProcess();
1207+
for ( PropertyData element : elementsToProcess.getElements() ) {
1208+
if ( declaredProperty.getName().equals( element.getPropertyName() ) ) {
1209+
return element.getAttributeMember();
1210+
}
1211+
}
1212+
throw new IllegalArgumentException("Couldn't find PropertyData for [" + declaredProperty.getName() + "] in class: " + declaredProperty.getPersistentClass().getClassName() );
1213+
}
1214+
11381215
private static String getMissingPropertiesString(Set<String> propertyNames) {
11391216
final var missingProperties = new StringBuilder();
11401217
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
@@ -147,6 +147,7 @@ public BasicValue(BasicValue original) {
147147
this.isSoftDelete = original.isSoftDelete;
148148
this.softDeleteStrategy = original.softDeleteStrategy;
149149
this.aggregateColumn = original.aggregateColumn;
150+
this.jdbcTypeCode = original.jdbcTypeCode;
150151
}
151152

152153
@Override
@@ -217,6 +218,22 @@ public void setImplicitJavaTypeAccess(Function<TypeConfiguration, java.lang.refl
217218
this.implicitJavaTypeAccess = implicitJavaTypeAccess;
218219
}
219220

221+
public Function<TypeConfiguration, BasicJavaType<?>> getExplicitJavaTypeAccess() {
222+
return explicitJavaTypeAccess;
223+
}
224+
225+
public Function<TypeConfiguration, JdbcType> getExplicitJdbcTypeAccess() {
226+
return explicitJdbcTypeAccess;
227+
}
228+
229+
public Function<TypeConfiguration, MutabilityPlan<?>> getExplicitMutabilityPlanAccess() {
230+
return explicitMutabilityPlanAccess;
231+
}
232+
233+
public Function<TypeConfiguration, java.lang.reflect.Type> getImplicitJavaTypeAccess() {
234+
return implicitJavaTypeAccess;
235+
}
236+
220237
public Selectable getColumn() {
221238
return getColumnSpan() == 0 ? null : getColumn( 0 );
222239
}
@@ -1032,6 +1049,10 @@ public void setExplicitTypeParams(Map<String,String> explicitLocalTypeParams) {
10321049
this.explicitLocalTypeParams = explicitLocalTypeParams;
10331050
}
10341051

1052+
public Map<String, String> getExplicitTypeParams() {
1053+
return explicitLocalTypeParams;
1054+
}
1055+
10351056
public void setExplicitTypeName(String typeName) {
10361057
this.explicitTypeName = typeName;
10371058
}

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
@@ -136,6 +136,11 @@ public List<Property> getPropertyClosure() {
136136
return getProperties();
137137
}
138138

139+
@Override
140+
public List<Property> getAllPropertyClosure() {
141+
return getAllProperties();
142+
}
143+
139144
@Override
140145
public List<Table> getTableClosure() {
141146
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
@@ -122,7 +122,13 @@ public SimpleValue(MetadataBuildingContext buildingContext, Table table) {
122122
protected SimpleValue(SimpleValue original) {
123123
this.buildingContext = original.buildingContext;
124124
this.metadata = original.metadata;
125-
this.columns.addAll( original.columns );
125+
for ( Selectable selectable : original.columns ) {
126+
if ( selectable instanceof Column column ) {
127+
final Column newColumn = column.clone();
128+
newColumn.setValue( this );
129+
this.columns.add( newColumn );
130+
}
131+
}
126132
this.insertability.addAll( original.insertability );
127133
this.updatability.addAll( original.updatability );
128134
this.partitionKey = original.partitionKey;

0 commit comments

Comments
 (0)