diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/Binders.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/Binders.java new file mode 100644 index 000000000000..8b17dbbed4db --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/Binders.java @@ -0,0 +1,112 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.boot.model.internal; + +import org.hibernate.AnnotationException; +import org.hibernate.annotations.AttributeBinderType; +import org.hibernate.annotations.TypeBinderType; +import org.hibernate.binder.AttributeBinder; +import org.hibernate.binder.TypeBinder; +import org.hibernate.boot.spi.MetadataBuildingContext; +import org.hibernate.mapping.Component; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Property; + +import java.lang.annotation.Annotation; + +import static org.hibernate.internal.util.GenericsHelper.typeArguments; + +/** + * @author Gavin King + * @since 7.3 + */ +public class Binders { + static void callTypeBinder( + Annotation annotation, Class annotationType, + Component embeddable, + MetadataBuildingContext context) { + try { + typeBinder( annotationType ) + .bind( annotationType.cast( annotation ), + context, embeddable ); + } + catch (Exception e) { + throw new AnnotationException( + "Error processing @TypeBinderType annotation '%s' for embeddable type '%s'" + .formatted( annotation, embeddable.getComponentClassName() ), e ); + } + } + + static void callTypeBinder( + Annotation annotation, Class annotationType, + PersistentClass entity, + MetadataBuildingContext context) { + try { + typeBinder( annotationType ) + .bind( annotationType.cast( annotation ), + context, entity ); + } + catch (Exception e) { + throw new AnnotationException( + "Error processing @TypeBinderType annotation '%s' for entity type '%s'" + .formatted( annotation, entity.getClassName() ), e ); + } + } + + static void callPropertyBinder( + Annotation annotation, Class annotationType, + PersistentClass entity, Property property, + MetadataBuildingContext context) { + try { + propertyBinder( annotationType ) + .bind( annotationType.cast( annotation ), + context, entity, property ); + } + catch (Exception e) { + throw new AnnotationException( + "error processing @AttributeBinderType annotation '%s' for attribute '%s' of entity type '%s'" + .formatted( annotation, property.getName(), entity.getClassName() ), e ); + } + } + + private static TypeBinder typeBinder(Class annotationType) + throws Exception { + final var binderType = + annotationType.getAnnotation( TypeBinderType.class ) + .binder(); + checkImplementedTypeArgument( annotationType, binderType, TypeBinder.class ); + @SuppressWarnings("unchecked") // Safe, we just checked + final var castBinderType = (Class>) binderType; + return castBinderType.getDeclaredConstructor().newInstance(); + } + + private static AttributeBinder propertyBinder(Class annotationType) + throws Exception { + final var binderType = + annotationType.getAnnotation( AttributeBinderType.class ) + .binder(); + checkImplementedTypeArgument( annotationType, binderType, PropertyBinder.class ); + @SuppressWarnings("unchecked") // Safe, we just checked + final var castBinderType = (Class>) binderType; + return castBinderType.getDeclaredConstructor().newInstance(); + } + + private static void checkImplementedTypeArgument( + Class annotationType, + Class binderType, Class implementedType) { + final var args = typeArguments( implementedType, binderType ); + if ( args.length == 1 ) { + final var requiredAnnotationType = args[0]; + if ( annotationType != requiredAnnotationType ) { + throw new AnnotationException( + "Wrong kind of binder for annotation type:" + + " '%s' does not accept an annotation of type '%s'" + .formatted( binderType.getTypeName(), + annotationType.getTypeName() ) + ); + } + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java index 030253640cf6..9ddc33ae4af0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EmbeddableBinder.java @@ -25,7 +25,6 @@ import org.hibernate.annotations.EmbeddedColumnNaming; import org.hibernate.annotations.Instantiator; import org.hibernate.annotations.TypeBinderType; -import org.hibernate.binder.TypeBinder; import org.hibernate.boot.spi.AccessType; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.PropertyData; @@ -65,6 +64,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.getPath; import static org.hibernate.boot.model.internal.BinderHelper.getRelativePath; import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation; +import static org.hibernate.boot.model.internal.Binders.callTypeBinder; import static org.hibernate.boot.model.internal.ComponentPropertyHolder.applyExplicitTableName; import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.GeneratorBinder.createIdGeneratorsFromGeneratorAnnotations; @@ -316,17 +316,7 @@ private static void callTypeBinders(Component embeddable, MetadataBuildingContex .getMetaAnnotated( TypeBinderType.class, context.getBootstrapContext().getModelsContext() ); for ( var metaAnnotated : metaAnnotatedAnnotations ) { - final var binderType = metaAnnotated.annotationType().getAnnotation( TypeBinderType.class ); - try { - //noinspection rawtypes - final TypeBinder binder = binderType.binder().getDeclaredConstructor().newInstance(); - //noinspection unchecked - binder.bind( metaAnnotated, context, embeddable ); - } - catch (Exception e) { - throw new AnnotationException( - "error processing @TypeBinderType annotation '" + metaAnnotated + "'", e ); - } + callTypeBinder( metaAnnotated, metaAnnotated.annotationType(), embeddable, context ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java index b0e9332eaeb8..034e931d80ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/EntityBinder.java @@ -63,7 +63,6 @@ import org.hibernate.annotations.Synchronize; import org.hibernate.annotations.TypeBinderType; import org.hibernate.annotations.View; -import org.hibernate.binder.TypeBinder; import org.hibernate.boot.model.NamedEntityGraphDefinition; import org.hibernate.boot.model.internal.InheritanceState.ElementsToProcess; import org.hibernate.boot.model.naming.EntityNaming; @@ -134,6 +133,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation; import static org.hibernate.boot.model.internal.BinderHelper.toAliasEntityMap; import static org.hibernate.boot.model.internal.BinderHelper.toAliasTableMap; +import static org.hibernate.boot.model.internal.Binders.callTypeBinder; import static org.hibernate.boot.model.internal.ClassPropertyHolder.setType; import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverridableAnnotation; import static org.hibernate.boot.model.internal.DialectOverridesAnnotationHelper.getOverrideAnnotation; @@ -383,23 +383,7 @@ private void addCheckToEntity(Check check) { private void callTypeBinders(PersistentClass persistentClass) { for ( var metaAnnotated : annotatedClass.getMetaAnnotated( TypeBinderType.class, modelsContext() ) ) { - applyTypeBinder( metaAnnotated, persistentClass ); - } - } - - private void applyTypeBinder(Annotation containingAnnotation, PersistentClass persistentClass) { - final var binderClass = - containingAnnotation.annotationType() - .getAnnotation( TypeBinderType.class ) - .binder(); - try { - //noinspection rawtypes - final TypeBinder binder = binderClass.getConstructor().newInstance(); - //noinspection unchecked - binder.bind( containingAnnotation, context, persistentClass ); - } - catch ( Exception e ) { - throw new AnnotationException( "error processing @TypeBinderType annotation '" + containingAnnotation + "'", e ); + callTypeBinder( metaAnnotated, metaAnnotated.annotationType(), persistentClass, context ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java index dc3084f43870..77d1f9f1e9e6 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/PropertyBinder.java @@ -41,7 +41,6 @@ import org.hibernate.annotations.NaturalId; import org.hibernate.annotations.OptimisticLock; import org.hibernate.annotations.Parent; -import org.hibernate.binder.AttributeBinder; import org.hibernate.boot.spi.AccessType; import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.PropertyData; @@ -81,6 +80,7 @@ import static org.hibernate.boot.model.internal.BinderHelper.getMappedSuperclassOrNull; import static org.hibernate.boot.model.internal.BinderHelper.getPath; import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation; +import static org.hibernate.boot.model.internal.Binders.callPropertyBinder; import static org.hibernate.boot.model.internal.ClassPropertyHolder.handleGenericComponentProperty; import static org.hibernate.boot.model.internal.ClassPropertyHolder.prepareActualProperty; import static org.hibernate.boot.model.internal.CollectionBinder.bindCollection; @@ -303,30 +303,16 @@ private boolean autoApplyConverters() { && !memberDetails.hasDirectAnnotationUsage( Temporal.class ); } - @SuppressWarnings({"rawtypes", "unchecked"}) private void callAttributeBinders(Property property, Map persistentClasses) { final var metaAnnotatedTargets = memberDetails.getMetaAnnotated( AttributeBinderType.class, getSourceModelContext() ); - final var descriptorRegistry = getSourceModelContext().getAnnotationDescriptorRegistry(); - for ( int i = 0; i < metaAnnotatedTargets.size(); i++ ) { - final Annotation metaAnnotatedTarget = metaAnnotatedTargets.get( i ); - final var metaAnnotatedDescriptor = - descriptorRegistry.getDescriptor( metaAnnotatedTarget.annotationType() ); - final var binderTypeAnn = - metaAnnotatedDescriptor.getDirectAnnotationUsage( AttributeBinderType.class ); - try { - final AttributeBinder binder = binderTypeAnn.binder().getConstructor().newInstance(); - final var persistentClass = - entityBinder != null - ? entityBinder.getPersistentClass() - : persistentClasses.get( holder.getEntityName() ); - binder.bind( metaAnnotatedTarget, buildingContext, persistentClass, property ); - } - catch ( Exception e ) { - throw new AnnotationException( "error processing @AttributeBinderType annotation '" - + metaAnnotatedDescriptor.getAnnotationType().getName() + "' for property '" - + qualify( holder.getPath(), name ) + "'", e ); - } + for ( final var metaAnnotatedTarget : metaAnnotatedTargets ) { + final var annotationType = metaAnnotatedTarget.annotationType(); + final var persistentClass = + entityBinder != null + ? entityBinder.getPersistentClass() + : persistentClasses.get( holder.getEntityName() ); + callPropertyBinder( metaAnnotatedTarget, annotationType, persistentClass, property, buildingContext ); } }