Skip to content

Commit 08956a4

Browse files
gavinkingmbellade
authored andcommitted
HHH-17739 fix NPE for field with unsupported collection type
cleanups and warning fixes in TypeConfiguration and BasicValue
1 parent bc64bed commit 08956a4

File tree

3 files changed

+227
-210
lines changed

3 files changed

+227
-210
lines changed

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

Lines changed: 124 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@
4040
import org.hibernate.engine.jdbc.Size;
4141
import org.hibernate.internal.CoreLogging;
4242
import org.hibernate.internal.CoreMessageLogger;
43-
import org.hibernate.internal.util.ReflectHelper;
4443
import org.hibernate.internal.util.StringHelper;
4544
import org.hibernate.internal.util.collections.CollectionHelper;
4645
import org.hibernate.metamodel.mapping.JdbcMapping;
@@ -82,6 +81,7 @@
8281
import jakarta.persistence.TemporalType;
8382

8483
import static java.lang.Boolean.parseBoolean;
84+
import static org.hibernate.internal.util.ReflectHelper.reflectedPropertyType;
8585
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
8686
import static org.hibernate.mapping.MappingHelper.injectParameters;
8787

@@ -103,6 +103,7 @@ public class BasicValue extends SimpleValue implements JdbcTypeIndicators, Resol
103103
private Function<TypeConfiguration, java.lang.reflect.Type> implicitJavaTypeAccess;
104104

105105
private EnumType enumerationStyle;
106+
@SuppressWarnings("deprecation")
106107
private TemporalType temporalPrecision;
107108
private TimeZoneStorageType timeZoneStorageType;
108109
private boolean isSoftDelete;
@@ -234,8 +235,7 @@ public java.lang.reflect.Type getResolvedJavaType() {
234235
public long getColumnLength() {
235236
final Selectable selectable = getColumn();
236237
if ( selectable instanceof Column ) {
237-
final Column column = (Column) selectable;
238-
final Long length = column.getLength();
238+
final Long length = ( (Column) selectable ).getLength();
239239
return length == null ? NO_COLUMN_LENGTH : length;
240240
}
241241
else {
@@ -248,8 +248,9 @@ public int getColumnPrecision() {
248248
final Selectable selectable = getColumn();
249249
if ( selectable instanceof Column ) {
250250
final Column column = (Column) selectable;
251-
if ( column.getTemporalPrecision() != null ) {
252-
return column.getTemporalPrecision();
251+
final Integer temporalPrecision = column.getTemporalPrecision();
252+
if ( temporalPrecision != null ) {
253+
return temporalPrecision;
253254
}
254255
final Integer precision = column.getPrecision();
255256
return precision == null ? NO_COLUMN_PRECISION : precision;
@@ -263,8 +264,7 @@ public int getColumnPrecision() {
263264
public int getColumnScale() {
264265
final Selectable selectable = getColumn();
265266
if ( selectable instanceof Column ) {
266-
final Column column = (Column) selectable;
267-
final Integer scale = column.getScale();
267+
final Integer scale = ( (Column) selectable ).getScale();
268268
return scale == null ? NO_COLUMN_SCALE : scale;
269269
}
270270
else {
@@ -284,8 +284,9 @@ public void copyTypeFrom(SimpleValue sourceValue) {
284284
super.copyTypeFrom( sourceValue );
285285
if ( sourceValue instanceof BasicValue ) {
286286
final BasicValue basicValue = (BasicValue) sourceValue;
287-
this.resolution = basicValue.resolution;
288-
this.implicitJavaTypeAccess = (typeConfiguration) -> basicValue.implicitJavaTypeAccess.apply( typeConfiguration );
287+
resolution = basicValue.resolution;
288+
implicitJavaTypeAccess =
289+
typeConfiguration -> basicValue.implicitJavaTypeAccess.apply( typeConfiguration );
289290
}
290291
}
291292

@@ -447,55 +448,71 @@ && parseBoolean( typeParameters.getProperty(DynamicParameterizedType.IS_DYNAMIC)
447448
getBuildingContext()
448449
);
449450
}
450-
451-
if ( isVersion() ) {
451+
else if ( isVersion() ) {
452452
return VersionResolution.from( implicitJavaTypeAccess, timeZoneStorageType, getBuildingContext() );
453453
}
454+
else {
455+
// determine JavaType if we can
456+
final BasicJavaType<?> explicitJavaType = getExplicitJavaType();
457+
final JavaType<?> javaType = determineJavaType( explicitJavaType );
458+
final ConverterDescriptor converterDescriptor = getConverterDescriptor( javaType );
459+
return converterDescriptor != null
460+
? converterResolution( javaType, converterDescriptor )
461+
: resolution( explicitJavaType, javaType );
462+
}
463+
}
454464

455-
// determine JavaType if we can
456-
final BasicJavaType<?> explicitJavaType = explicitJavaTypeAccess == null
457-
? null
465+
private BasicJavaType<?> getExplicitJavaType() {
466+
return explicitJavaTypeAccess == null ? null
458467
: explicitJavaTypeAccess.apply( getTypeConfiguration() );
468+
}
459469

460-
JavaType<?> javaType = determineJavaType( explicitJavaType );
461-
ConverterDescriptor attributeConverterDescriptor = getAttributeConverterDescriptor();
462-
470+
private ConverterDescriptor getConverterDescriptor(JavaType<?> javaType) {
471+
final ConverterDescriptor converterDescriptor = getAttributeConverterDescriptor();
463472
if ( isSoftDelete() ) {
464-
assert attributeConverterDescriptor != null;
465-
final boolean conversionWasUnspecified = SoftDelete.UnspecifiedConversion.class.equals( attributeConverterDescriptor.getAttributeConverterClass() );
466-
if ( conversionWasUnspecified ) {
467-
final JdbcType jdbcType = BooleanJdbcType.INSTANCE.resolveIndicatedType( this, javaType );
468-
if ( jdbcType.isNumber() ) {
469-
attributeConverterDescriptor = new InstanceBasedConverterDescriptor(
470-
NumericBooleanConverter.INSTANCE,
471-
getBuildingContext().getBootstrapContext().getClassmateContext()
472-
);
473-
}
474-
else if ( jdbcType.isString() ) {
475-
// here we pick 'T' / 'F' storage, though 'Y' / 'N' is equally valid - its 50/50
476-
attributeConverterDescriptor = new InstanceBasedConverterDescriptor(
477-
TrueFalseConverter.INSTANCE,
478-
getBuildingContext().getBootstrapContext().getClassmateContext()
479-
);
480-
}
481-
else {
482-
// should indicate BIT or BOOLEAN == no conversion needed
483-
// - we still create the converter to properly set up JDBC type, etc
484-
attributeConverterDescriptor = new InstanceBasedConverterDescriptor(
485-
PassThruSoftDeleteConverter.INSTANCE,
486-
getBuildingContext().getBootstrapContext().getClassmateContext()
487-
);
488-
}
489-
}
473+
assert converterDescriptor != null;
474+
final ConverterDescriptor softDeleteConverterDescriptor =
475+
getSoftDeleteConverterDescriptor( converterDescriptor, javaType);
476+
return getSoftDeleteStrategy() == SoftDeleteType.ACTIVE
477+
? new ReversedConverterDescriptor<>( softDeleteConverterDescriptor )
478+
: softDeleteConverterDescriptor;
479+
}
480+
else {
481+
return converterDescriptor;
482+
}
483+
}
490484

491-
if ( getSoftDeleteStrategy() == SoftDeleteType.ACTIVE ) {
492-
attributeConverterDescriptor = new ReversedConverterDescriptor<>( attributeConverterDescriptor );
485+
private ConverterDescriptor getSoftDeleteConverterDescriptor(
486+
ConverterDescriptor attributeConverterDescriptor, JavaType<?> javaType) {
487+
final boolean conversionWasUnspecified =
488+
SoftDelete.UnspecifiedConversion.class.equals( attributeConverterDescriptor.getAttributeConverterClass() );
489+
if ( conversionWasUnspecified ) {
490+
final JdbcType jdbcType = BooleanJdbcType.INSTANCE.resolveIndicatedType( this, javaType);
491+
if ( jdbcType.isNumber() ) {
492+
return new InstanceBasedConverterDescriptor(
493+
NumericBooleanConverter.INSTANCE,
494+
getBuildingContext().getBootstrapContext().getClassmateContext()
495+
);
496+
}
497+
else if ( jdbcType.isString() ) {
498+
// here we pick 'T' / 'F' storage, though 'Y' / 'N' is equally valid - its 50/50
499+
return new InstanceBasedConverterDescriptor(
500+
TrueFalseConverter.INSTANCE,
501+
getBuildingContext().getBootstrapContext().getClassmateContext()
502+
);
503+
}
504+
else {
505+
// should indicate BIT or BOOLEAN == no conversion needed
506+
// - we still create the converter to properly set up JDBC type, etc
507+
return new InstanceBasedConverterDescriptor(
508+
PassThruSoftDeleteConverter.INSTANCE,
509+
getBuildingContext().getBootstrapContext().getClassmateContext()
510+
);
493511
}
494512
}
495-
496-
return attributeConverterDescriptor != null
497-
? converterResolution( javaType, attributeConverterDescriptor )
498-
: resolution( explicitJavaType, javaType );
513+
else {
514+
return attributeConverterDescriptor;
515+
}
499516
}
500517

501518
private static class ReversedConverterDescriptor<R> implements ConverterDescriptor {
@@ -680,11 +697,10 @@ private Resolution<?> converterResolution(JavaType<?> javaType, ConverterDescrip
680697
&& !attributeConverterDescriptor.getDomainValueResolvedType().getErasedType()
681698
.isAssignableFrom( javaType.getJavaTypeClass() ) ) {
682699
// In this case, the converter applies to the element of a BasicPluralJavaType
683-
final BasicPluralJavaType<?> containerJtd = (BasicPluralJavaType<?>) javaType;
684700
final BasicType registeredElementType = converterResolution.getLegacyResolvedBasicType();
685701
final Selectable column = getColumn();
686702
final BasicType<?> registeredType = registeredElementType == null ? null
687-
: containerJtd.resolveType(
703+
: ( (BasicPluralJavaType<?>) javaType ).resolveType(
688704
getTypeConfiguration(),
689705
getDialect(),
690706
registeredElementType,
@@ -730,66 +746,76 @@ private JavaType<?> determineJavaType(JavaType<?> explicitJavaType) {
730746
}
731747

732748
private JavaType<?> determineReflectedJavaType() {
733-
final java.lang.reflect.Type impliedJavaType;
734-
735749
final TypeConfiguration typeConfiguration = getTypeConfiguration();
750+
final java.lang.reflect.Type impliedJavaType = impliedJavaType( typeConfiguration );
751+
if ( impliedJavaType == null ) {
752+
return null;
753+
}
754+
else {
755+
resolvedJavaType = impliedJavaType;
756+
return javaType( typeConfiguration, impliedJavaType );
757+
}
758+
}
759+
760+
private java.lang.reflect.Type impliedJavaType(TypeConfiguration typeConfiguration) {
736761
if ( resolvedJavaType != null ) {
737-
impliedJavaType = resolvedJavaType;
762+
return resolvedJavaType;
738763
}
739764
else if ( implicitJavaTypeAccess != null ) {
740-
impliedJavaType = implicitJavaTypeAccess.apply( typeConfiguration );
765+
return implicitJavaTypeAccess.apply(typeConfiguration);
741766
}
742767
else if ( ownerName != null && propertyName != null ) {
743-
impliedJavaType = ReflectHelper.reflectedPropertyType(
744-
ownerName,
745-
propertyName,
746-
getServiceRegistry().requireService( ClassLoaderService.class )
747-
);
768+
return reflectedPropertyType( ownerName, propertyName,
769+
getServiceRegistry().requireService( ClassLoaderService.class ) );
748770
}
749771
else {
750772
return null;
751773
}
774+
}
752775

753-
resolvedJavaType = impliedJavaType;
776+
private JavaType<Object> javaType(TypeConfiguration typeConfiguration, java.lang.reflect.Type impliedJavaType) {
777+
final JavaType<Object> javaType = typeConfiguration.getJavaTypeRegistry().findDescriptor( impliedJavaType );
778+
return javaType == null ? specialJavaType( typeConfiguration, impliedJavaType ) : javaType;
779+
}
754780

755-
if ( impliedJavaType == null ) {
756-
return null;
781+
private JavaType<Object> specialJavaType(
782+
TypeConfiguration typeConfiguration,
783+
java.lang.reflect.Type impliedJavaType) {
784+
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
785+
if ( jdbcTypeCode != null ) {
786+
// Construct special JavaType instances for JSON/XML types which can report recommended JDBC types
787+
// and implement toString/fromString as well as copying based on FormatMapper operations
788+
switch ( jdbcTypeCode ) {
789+
case SqlTypes.JSON:
790+
final JavaType<Object> jsonJavaType =
791+
new JsonJavaType<>( impliedJavaType,
792+
mutabilityPlan( typeConfiguration, impliedJavaType ),
793+
typeConfiguration );
794+
javaTypeRegistry.addDescriptor( jsonJavaType );
795+
return jsonJavaType;
796+
case SqlTypes.SQLXML:
797+
final JavaType<Object> xmlJavaType =
798+
new XmlJavaType<>( impliedJavaType,
799+
mutabilityPlan( typeConfiguration, impliedJavaType ),
800+
typeConfiguration );
801+
javaTypeRegistry.addDescriptor( xmlJavaType );
802+
return xmlJavaType;
803+
}
757804
}
805+
return javaTypeRegistry.resolveDescriptor( impliedJavaType );
806+
}
758807

759-
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
760-
final JavaType<Object> javaType = javaTypeRegistry.findDescriptor( impliedJavaType );
761-
final MutabilityPlan<Object> explicitMutabilityPlan = explicitMutabilityPlanAccess != null
762-
? explicitMutabilityPlanAccess.apply( typeConfiguration )
763-
: null;
764-
final MutabilityPlan<Object> determinedMutabilityPlan = explicitMutabilityPlan != null
808+
private MutabilityPlan<Object> mutabilityPlan(
809+
TypeConfiguration typeConfiguration, java.lang.reflect.Type impliedJavaType) {
810+
final MutabilityPlan<Object> explicitMutabilityPlan = getExplicitMutabilityPlan();
811+
return explicitMutabilityPlan != null
765812
? explicitMutabilityPlan
766813
: RegistryHelper.INSTANCE.determineMutabilityPlan( impliedJavaType, typeConfiguration );
767-
if ( javaType == null ) {
768-
if ( jdbcTypeCode != null ) {
769-
// Construct special JavaType instances for JSON/XML types which can report recommended JDBC types
770-
// and implement toString/fromString as well as copying based on FormatMapper operations
771-
switch ( jdbcTypeCode ) {
772-
case SqlTypes.JSON:
773-
final JavaType<Object> jsonJavaType = new JsonJavaType<>(
774-
impliedJavaType,
775-
determinedMutabilityPlan,
776-
typeConfiguration
777-
);
778-
javaTypeRegistry.addDescriptor( jsonJavaType );
779-
return jsonJavaType;
780-
case SqlTypes.SQLXML:
781-
final JavaType<Object> xmlJavaType = new XmlJavaType<>(
782-
impliedJavaType,
783-
determinedMutabilityPlan,
784-
typeConfiguration
785-
);
786-
javaTypeRegistry.addDescriptor( xmlJavaType );
787-
return xmlJavaType;
788-
}
789-
}
790-
return javaTypeRegistry.resolveDescriptor( impliedJavaType );
791-
}
792-
return javaType;
814+
}
815+
816+
private MutabilityPlan<Object> getExplicitMutabilityPlan() {
817+
return explicitMutabilityPlanAccess == null ? null
818+
: explicitMutabilityPlanAccess.apply( getTypeConfiguration() );
793819
}
794820

795821
private static Resolution<?> interpretExplicitlyNamedType(
@@ -1066,8 +1092,7 @@ private UserType<?> getConfiguredUserTypeBean(Class<? extends UserType<?>> expli
10661092
: getUserTypeBean( explicitCustomType, properties ).getBeanInstance();
10671093

10681094
if ( typeInstance instanceof TypeConfigurationAware ) {
1069-
final TypeConfigurationAware configurationAware = (TypeConfigurationAware) typeInstance;
1070-
configurationAware.setTypeConfiguration( getTypeConfiguration() );
1095+
( (TypeConfigurationAware) typeInstance ).setTypeConfiguration( getTypeConfiguration() );
10711096
}
10721097

10731098
if ( typeInstance instanceof DynamicParameterizedType ) {
@@ -1097,11 +1122,12 @@ private <T> ManagedBean<T> getUserTypeBean(Class<T> explicitCustomType, Properti
10971122
}
10981123
}
10991124

1125+
@SuppressWarnings("deprecation")
11001126
public void setTemporalPrecision(TemporalType temporalPrecision) {
11011127
this.temporalPrecision = temporalPrecision;
11021128
}
11031129

1104-
@Override
1130+
@Override @SuppressWarnings("deprecation")
11051131
public TemporalType getTemporalPrecision() {
11061132
return temporalPrecision;
11071133
}

0 commit comments

Comments
 (0)