Skip to content

Commit 322b67f

Browse files
committed
HHH-18998 pass annotation to constructor of UserType
1 parent d0f3b67 commit 322b67f

File tree

10 files changed

+429
-44
lines changed

10 files changed

+429
-44
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/TypeDefinition.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
package org.hibernate.boot.model;
66

77
import java.io.Serializable;
8+
import java.lang.annotation.Annotation;
9+
import java.lang.reflect.Constructor;
10+
import java.lang.reflect.InvocationTargetException;
811
import java.sql.Types;
912
import java.util.Arrays;
1013
import java.util.Collections;
@@ -98,32 +101,35 @@ public Map<String,String> getParameters() {
98101

99102
public BasicValue.Resolution<?> resolve(
100103
Map<?,?> localConfigParameters,
104+
Annotation typeAnnotation,
101105
MutabilityPlan<?> explicitMutabilityPlan,
102106
MetadataBuildingContext context,
103107
JdbcTypeIndicators indicators) {
104108
if ( CollectionHelper.isEmpty( localConfigParameters ) ) {
105109
// we can use the re-usable resolution...
106110
if ( reusableResolution == null ) {
107-
reusableResolution = createResolution( name, Collections.emptyMap(), indicators, context );
111+
reusableResolution = createResolution( name, Collections.emptyMap(), typeAnnotation, indicators, context );
108112
}
109113
return reusableResolution;
110114
}
111115
else {
112116
final String name = this.name + ":" + NAME_COUNTER.getAndIncrement();
113-
return createResolution( name, localConfigParameters, indicators, context );
117+
return createResolution( name, localConfigParameters, typeAnnotation, indicators, context );
114118
}
115119
}
116120

117121
private BasicValue.Resolution<?> createResolution(
118122
String name,
119123
Map<?,?> usageSiteProperties,
124+
Annotation typeAnnotation,
120125
JdbcTypeIndicators indicators,
121126
MetadataBuildingContext context) {
122127
return createResolution(
123128
name,
124129
typeImplementorClass,
125130
parameters,
126131
usageSiteProperties,
132+
typeAnnotation,
127133
indicators,
128134
context
129135
);
@@ -134,20 +140,21 @@ private static <T> BasicValue.Resolution<T> createResolution(
134140
Class<T> typeImplementorClass,
135141
Map<?,?> parameters,
136142
Map<?,?> usageSiteProperties,
143+
Annotation typeAnnotation,
137144
JdbcTypeIndicators indicators,
138145
MetadataBuildingContext context) {
139146
final BootstrapContext bootstrapContext = context.getBootstrapContext();
140147
final TypeConfiguration typeConfiguration = bootstrapContext.getTypeConfiguration();
141-
final BeanInstanceProducer instanceProducer = bootstrapContext.getCustomTypeProducer();
148+
142149
final boolean isKnownType =
143150
Type.class.isAssignableFrom( typeImplementorClass )
144151
|| UserType.class.isAssignableFrom( typeImplementorClass );
145152

146153
// support for AttributeConverter would be nice too
147154
if ( isKnownType ) {
155+
148156
final T typeInstance =
149-
instantiateType( bootstrapContext.getServiceRegistry(), context.getBuildingOptions(),
150-
name, typeImplementorClass, instanceProducer );
157+
instantiateType( name, typeImplementorClass, typeAnnotation, context, bootstrapContext );
151158

152159
if ( typeInstance instanceof TypeConfigurationAware configurationAware ) {
153160
configurationAware.setTypeConfiguration( typeConfiguration );
@@ -167,7 +174,7 @@ private static <T> BasicValue.Resolution<T> createResolution(
167174
@SuppressWarnings("unchecked")
168175
final UserType<T> userType = (UserType<T>) typeInstance;
169176
final CustomType<T> customType = new CustomType<>( userType, typeConfiguration );
170-
return new UserTypeResolution<>( customType, null, combinedTypeParameters );
177+
return new UserTypeResolution<>( customType, null, combinedTypeParameters, typeAnnotation );
171178
}
172179

173180
if ( typeInstance instanceof BasicType ) {
@@ -224,6 +231,28 @@ public MutabilityPlan<T> getMutabilityPlan() {
224231
return resolveLegacyCases( typeImplementorClass, indicators, typeConfiguration );
225232
}
226233

234+
private static <T> T instantiateType(
235+
String name, Class<T> typeImplementorClass, Annotation typeAnnotation,
236+
MetadataBuildingContext context, BootstrapContext bootstrapContext) {
237+
if ( typeAnnotation != null ) {
238+
// attempt to instantiate it with the annotation as a constructor argument
239+
try {
240+
final Constructor<T> constructor = typeImplementorClass.getConstructor( typeAnnotation.annotationType() );
241+
constructor.setAccessible( true );
242+
return constructor.newInstance( typeAnnotation );
243+
}
244+
catch ( NoSuchMethodException ignored ) {
245+
// no such constructor, instantiate it the old way
246+
}
247+
catch (InvocationTargetException|InstantiationException|IllegalAccessException e) {
248+
throw new org.hibernate.InstantiationException( "Could not instantiate custom type", typeImplementorClass, e );
249+
}
250+
}
251+
252+
return instantiateType( bootstrapContext.getServiceRegistry(), context.getBuildingOptions(),
253+
name, typeImplementorClass, bootstrapContext.getCustomTypeProducer() );
254+
}
255+
227256
private static <T> BasicValue.Resolution<T> resolveLegacyCases(
228257
Class<T> typeImplementorClass, JdbcTypeIndicators indicators, TypeConfiguration typeConfiguration) {
229258
final BasicType<T> legacyType;
@@ -316,12 +345,14 @@ public static BasicValue.Resolution<?> createLocalResolution(
316345
String name,
317346
Class<?> typeImplementorClass,
318347
Map<?,?> localTypeParams,
348+
Annotation typeAnnotation,
319349
MetadataBuildingContext buildingContext) {
320350
return createResolution(
321351
name + ':' + NAME_COUNTER.getAndIncrement(),
322352
typeImplementorClass,
323353
localTypeParams,
324354
null,
355+
typeAnnotation,
325356
buildingContext.getBootstrapContext().getTypeConfiguration().getCurrentBaseSqlTypeIndicators(),
326357
buildingContext
327358
);

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

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ public enum Kind {
109109
// in-flight info
110110

111111
private Class<? extends UserType<?>> explicitCustomType;
112-
private Map<String,String> explicitLocalTypeParams;
112+
private Map<String,String> explicitLocalCustomTypeParams;
113+
private Annotation explicitCustomTypeAnnotation;
113114

114115
private Function<TypeConfiguration, JdbcType> explicitJdbcTypeAccess;
115116
private Function<TypeConfiguration, BasicJavaType> explicitJavaTypeAccess;
@@ -324,30 +325,33 @@ public void setType(
324325
isLob = value.hasDirectAnnotationUsage( Lob.class );
325326
}
326327

328+
final SourceModelBuildingContext context = getSourceModelContext();
329+
327330
if ( getDialect().getNationalizationSupport() == NationalizationSupport.EXPLICIT ) {
328331
isNationalized = buildingContext.getBuildingOptions().useNationalizedCharacterData()
329-
|| value.locateAnnotationUsage( Nationalized.class, getSourceModelContext() ) != null;
332+
|| value.locateAnnotationUsage( Nationalized.class, context ) != null;
330333
}
331334

332335
if ( converterDescriptor != null ) {
333336
applyJpaConverter( value, converterDescriptor );
334337
}
335338

336339
final Class<? extends UserType<?>> userTypeImpl =
337-
kind.mappingAccess.customType( value, getSourceModelContext() );
340+
kind.mappingAccess.customType( value, context );
338341
if ( userTypeImpl != null ) {
339-
applyExplicitType( userTypeImpl,
340-
kind.mappingAccess.customTypeParameters( value, getSourceModelContext() ) );
342+
this.explicitCustomType = userTypeImpl;
343+
this.explicitLocalCustomTypeParams = kind.mappingAccess.customTypeParameters( value, context );
344+
this.explicitCustomTypeAnnotation = kind.mappingAccess.customTypeAnnotation( value, context );
341345
// An explicit custom UserType has top precedence when we get to BasicValue resolution.
342346
return;
343347
}
344348
else if ( modelClassDetails != null ) {
345-
final ClassDetails rawClassDetails = modelClassDetails.determineRawClass();
346-
final Class<?> basicClass = rawClassDetails.toJavaClass();
347349
final Class<? extends UserType<?>> registeredUserTypeImpl =
348-
getMetadataCollector().findRegisteredUserType( basicClass );
350+
getMetadataCollector()
351+
.findRegisteredUserType( modelClassDetails.determineRawClass().toJavaClass() );
349352
if ( registeredUserTypeImpl != null ) {
350-
applyExplicitType( registeredUserTypeImpl, emptyMap() );
353+
this.explicitCustomType = registeredUserTypeImpl;
354+
this.explicitLocalCustomTypeParams = emptyMap();
351355
return;
352356
}
353357
}
@@ -380,11 +384,6 @@ else if ( modelClassDetails != null ) {
380384

381385
}
382386

383-
private void applyExplicitType(Class<? extends UserType<?>> impl, Map<String,String> params) {
384-
this.explicitCustomType = impl;
385-
this.explicitLocalTypeParams = params;
386-
}
387-
388387
private void prepareCollectionId(MemberDetails attribute) {
389388
final CollectionId collectionIdAnn = attribute.getDirectAnnotationUsage( CollectionId.class );
390389
if ( collectionIdAnn == null ) {
@@ -1277,7 +1276,7 @@ else if ( aggregateComponent != null ) {
12771276
}
12781277

12791278
public void fillSimpleValue() {
1280-
basicValue.setExplicitTypeParams( explicitLocalTypeParams );
1279+
basicValue.setExplicitTypeParams( explicitLocalCustomTypeParams );
12811280

12821281
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12831282
// todo (6.0) : we are dropping support for @Type and @TypeDef from annotations
@@ -1293,6 +1292,10 @@ public void fillSimpleValue() {
12931292
basicValue.setTypeParameters( createDynamicParameterizedTypeParameters() );
12941293
}
12951294

1295+
if ( explicitCustomType != null ) {
1296+
basicValue.setTypeAnnotation( explicitCustomTypeAnnotation );
1297+
}
1298+
12961299
if ( converterDescriptor != null ) {
12971300
basicValue.setJpaAttributeConverterDescriptor( converterDescriptor );
12981301
}
@@ -1364,8 +1367,8 @@ private Map<String, Object> createDynamicParameterizedTypeParameters() {
13641367
parameters.put( DynamicParameterizedType.ACCESS_TYPE, accessType.getType() );
13651368
}
13661369

1367-
if ( explicitLocalTypeParams != null ) {
1368-
parameters.putAll( explicitLocalTypeParams );
1370+
if ( explicitLocalCustomTypeParams != null ) {
1371+
parameters.putAll( explicitLocalCustomTypeParams );
13691372
}
13701373

13711374
return parameters;
@@ -1377,6 +1380,7 @@ private Map<String, Object> createDynamicParameterizedTypeParameters() {
13771380
private interface BasicMappingAccess {
13781381
Class<? extends UserType<?>> customType(MemberDetails attribute, SourceModelBuildingContext context);
13791382
Map<String,String> customTypeParameters(MemberDetails attribute, SourceModelBuildingContext context);
1383+
Annotation customTypeAnnotation(MemberDetails attribute, SourceModelBuildingContext context);
13801384
}
13811385

13821386
private static class ValueMappingAccess implements BasicMappingAccess {
@@ -1393,6 +1397,12 @@ public Map<String,String> customTypeParameters(MemberDetails attribute, SourceMo
13931397
final Type customType = attribute.locateAnnotationUsage( Type.class, context );
13941398
return customType == null ? null : extractParameterMap( customType.parameters() );
13951399
}
1400+
1401+
@Override
1402+
public Annotation customTypeAnnotation(MemberDetails attribute, SourceModelBuildingContext context) {
1403+
final List<? extends Annotation> metaAnnotated = attribute.getMetaAnnotated( Type.class, context );
1404+
return metaAnnotated.size() == 1 ? metaAnnotated.get( 0 ) : null;
1405+
}
13961406
}
13971407

13981408
private static class AnyDiscriminatorMappingAccess implements BasicMappingAccess {
@@ -1407,6 +1417,11 @@ public Class<? extends UserType<?>> customType(MemberDetails attribute, SourceMo
14071417
public Map<String,String> customTypeParameters(MemberDetails attribute, SourceModelBuildingContext context) {
14081418
return emptyMap();
14091419
}
1420+
1421+
@Override
1422+
public Annotation customTypeAnnotation(MemberDetails attribute, SourceModelBuildingContext context) {
1423+
return null;
1424+
}
14101425
}
14111426

14121427
private static class AnyKeyMappingAccess implements BasicMappingAccess {
@@ -1421,6 +1436,11 @@ public Class<? extends UserType<?>> customType(MemberDetails attribute, SourceMo
14211436
public Map<String,String> customTypeParameters(MemberDetails attribute, SourceModelBuildingContext context) {
14221437
return emptyMap();
14231438
}
1439+
1440+
@Override
1441+
public Annotation customTypeAnnotation(MemberDetails attribute, SourceModelBuildingContext context) {
1442+
return null;
1443+
}
14241444
}
14251445

14261446
private static class MapKeyMappingAccess implements BasicMappingAccess {
@@ -1439,6 +1459,12 @@ public Map<String,String> customTypeParameters(MemberDetails attribute, SourceMo
14391459
return customType == null ? null : extractParameterMap( customType.parameters() );
14401460

14411461
}
1462+
1463+
@Override
1464+
public Annotation customTypeAnnotation(MemberDetails attribute, SourceModelBuildingContext context) {
1465+
final List<? extends Annotation> metaAnnotated = attribute.getMetaAnnotated( MapKeyType.class, context );
1466+
return metaAnnotated.size() == 1 ? metaAnnotated.get( 0 ) : null;
1467+
}
14421468
}
14431469

14441470
private static class CollectionIdMappingAccess implements BasicMappingAccess {
@@ -1455,7 +1481,12 @@ public Class<? extends UserType<?>> customType(MemberDetails attribute, SourceMo
14551481
public Map<String,String> customTypeParameters(MemberDetails attribute, SourceModelBuildingContext context) {
14561482
final CollectionIdType customType = attribute.locateAnnotationUsage( CollectionIdType.class, context );
14571483
return customType == null ? null : extractParameterMap( customType.parameters() );
1484+
}
14581485

1486+
@Override
1487+
public Annotation customTypeAnnotation(MemberDetails attribute, SourceModelBuildingContext context) {
1488+
final List<? extends Annotation> metaAnnotated = attribute.getMetaAnnotated( CollectionIdType.class, context );
1489+
return metaAnnotated.size() == 1 ? metaAnnotated.get( 0 ) : null;
14591490
}
14601491
}
14611492

@@ -1471,6 +1502,11 @@ public Class<? extends UserType<?>> customType(MemberDetails attribute, SourceMo
14711502
public Map<String,String> customTypeParameters(MemberDetails attribute, SourceModelBuildingContext context) {
14721503
return emptyMap();
14731504
}
1505+
1506+
@Override
1507+
public Annotation customTypeAnnotation(MemberDetails attribute, SourceModelBuildingContext context) {
1508+
return null;
1509+
}
14741510
}
14751511

14761512
private static AnnotatedJoinColumns convertToJoinColumns(AnnotatedColumns columns, MetadataBuildingContext context) {

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
*/
55
package org.hibernate.boot.model.process.internal;
66

7+
import java.lang.annotation.Annotation;
78
import java.util.Properties;
89

910
import org.hibernate.mapping.BasicValue;
@@ -27,13 +28,16 @@ public class UserTypeResolution<T> implements BasicValue.Resolution<T> {
2728
* and builds its own :(
2829
*/
2930
private final Properties combinedTypeParameters;
31+
private final Annotation typeAnnotation;
3032

3133
public UserTypeResolution(
3234
CustomType<T> userTypeAdapter,
3335
MutabilityPlan<T> explicitMutabilityPlan,
34-
Properties combinedTypeParameters) {
36+
Properties combinedTypeParameters,
37+
Annotation typeAnnotation) {
3538
this.userTypeAdapter = userTypeAdapter;
3639
this.combinedTypeParameters = combinedTypeParameters;
40+
this.typeAnnotation = typeAnnotation;
3741
this.mutabilityPlan = explicitMutabilityPlan != null
3842
? explicitMutabilityPlan
3943
: new UserTypeMutabilityPlanAdapter<>( userTypeAdapter.getUserType() );
@@ -77,6 +81,10 @@ public Properties getCombinedTypeParameters() {
7781
return combinedTypeParameters;
7882
}
7983

84+
public Annotation getTypeAnnotation() {
85+
return typeAnnotation;
86+
}
87+
8088
@Override
8189
public JdbcMapping getJdbcMapping() {
8290
return userTypeAdapter;

hibernate-core/src/main/java/org/hibernate/boot/model/source/internal/hbm/ModelBinder.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2185,8 +2185,9 @@ private void bindAny(
21852185
}
21862186
else {
21872187
discriminatorTypeName = StandardBasicTypes.STRING.getName();
2188-
discriminatorType = metadataBuildingContext.getBootstrapContext().getTypeConfiguration().getBasicTypeRegistry()
2189-
.resolve( StandardBasicTypes.STRING );
2188+
discriminatorType =
2189+
metadataBuildingContext.getBootstrapContext().getTypeConfiguration().getBasicTypeRegistry()
2190+
.resolve( StandardBasicTypes.STRING );
21902191
}
21912192

21922193
anyBinding.setMetaType( discriminatorTypeName );
@@ -2196,7 +2197,9 @@ private void bindAny(
21962197
anyMapping.getDiscriminatorSource().getValueMappings().forEach(
21972198
(discriminatorValueString, entityName) -> {
21982199
try {
2199-
final Object discriminatorValue = discriminatorType.getJavaTypeDescriptor().fromString( discriminatorValueString );
2200+
final Object discriminatorValue =
2201+
discriminatorType.getJavaTypeDescriptor()
2202+
.fromString( discriminatorValueString );
22002203
discriminatorValueToEntityNameMap.put( discriminatorValue, entityName );
22012204
}
22022205
catch (Exception e) {
@@ -2258,6 +2261,7 @@ private BasicType<?> resolveExplicitlyNamedAnyDiscriminatorType(
22582261
final BasicValue.Resolution<?> resolution = typeDefinition.resolve(
22592262
parameters,
22602263
null,
2264+
null,
22612265
metadataBuildingContext,
22622266
typeConfiguration.getCurrentBaseSqlTypeIndicators()
22632267
);

0 commit comments

Comments
 (0)