From 2d066d1ae500cdfa391a778495ae444b9d01641b Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 19 Mar 2024 20:54:47 -0500 Subject: [PATCH] HHH-17460 - Ongoing JPA 32 work --- .../boot/model/internal/MapBinder.java | 2 +- .../internal/GlobalRegistrationsImpl.java | 59 ++- .../internal/AnnotationUsageHelper.java | 34 ++ .../boot/models/xml/XmlResourceException.java | 2 +- .../xml/internal/ManagedTypeProcessor.java | 20 +- .../models/xml/internal/QueryProcessing.java | 358 ++++++++++++++++++ .../xml/internal/XmlAnnotationHelper.java | 15 +- .../models/xml/spi/XmlDocumentContext.java | 12 + .../idclass/xml/IdClassXmlTest.java | 5 +- .../dynamicparameterized/MyStringType.java | 5 +- settings.gradle | 2 +- 11 files changed, 467 insertions(+), 47 deletions(-) create mode 100644 hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/QueryProcessing.java diff --git a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/MapBinder.java b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/MapBinder.java index 827fc2759d..1be3707566 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/model/internal/MapBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/model/internal/MapBinder.java @@ -416,7 +416,7 @@ public class MapBinder extends CollectionBinder { private AnnotationUsage getMapKeyForeignKey(MemberDetails property) { final AnnotationUsage mapKeyJoinColumns = property.getAnnotationUsage( MapKeyJoinColumns.class ); - final AnnotationUsage mapKeyJoinColumn = property.getAnnotationUsage( MapKeyJoinColumn.class ); + final AnnotationUsage mapKeyJoinColumn = property.getSingleAnnotationUsage( MapKeyJoinColumn.class ); if ( mapKeyJoinColumns != null ) { return mapKeyJoinColumns.getNestedUsage( "foreignKey" ); } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/categorize/internal/GlobalRegistrationsImpl.java b/hibernate-core/src/main/java/org/hibernate/boot/models/categorize/internal/GlobalRegistrationsImpl.java index 16dd5f3d29..a136bcece0 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/categorize/internal/GlobalRegistrationsImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/categorize/internal/GlobalRegistrationsImpl.java @@ -74,14 +74,11 @@ import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.jpa.AvailableHints; import org.hibernate.metamodel.CollectionClassification; import org.hibernate.models.spi.AnnotationDescriptor; -import org.hibernate.models.spi.AnnotationDescriptorRegistry; import org.hibernate.models.spi.AnnotationTarget; import org.hibernate.models.spi.AnnotationUsage; import org.hibernate.models.spi.ClassDetails; -import org.hibernate.models.spi.ClassDetailsRegistry; import org.hibernate.models.spi.MutableAnnotationUsage; import org.hibernate.models.spi.SourceModelBuildingContext; -import org.hibernate.models.spi.SourceModelContext; import jakarta.persistence.ColumnResult; import jakarta.persistence.ConstructorResult; @@ -116,8 +113,7 @@ import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty; * @author Steve Ebersole */ public class GlobalRegistrationsImpl implements GlobalRegistrations { - private final ClassDetailsRegistry classDetailsRegistry; - private final AnnotationDescriptorRegistry descriptorRegistry; + private final SourceModelBuildingContext sourceModelContext; private List jpaEventListeners; private List converterRegistrations; @@ -141,13 +137,8 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { private Map namedNativeQueryRegistrations; private Map namedStoredProcedureQueryRegistrations; - public GlobalRegistrationsImpl(SourceModelContext sourceModelContext) { - this( sourceModelContext.getClassDetailsRegistry(), sourceModelContext.getAnnotationDescriptorRegistry() ); - } - - public GlobalRegistrationsImpl(ClassDetailsRegistry classDetailsRegistry, AnnotationDescriptorRegistry descriptorRegistry) { - this.classDetailsRegistry = classDetailsRegistry; - this.descriptorRegistry = descriptorRegistry; + public GlobalRegistrationsImpl(SourceModelBuildingContext sourceModelContext) { + this.sourceModelContext = sourceModelContext; } @Override @@ -257,8 +248,8 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { } registrations.forEach( (reg) -> collectJavaTypeRegistration( - classDetailsRegistry.resolveClassDetails( reg.getClazz() ), - classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ) + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getClazz() ), + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getDescriptor() ) ) ); } @@ -291,7 +282,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { registrations.forEach( (reg) -> collectJdbcTypeRegistration( reg.getCode(), - classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ) + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getDescriptor() ) ) ); } @@ -324,12 +315,12 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { final ClassDetails explicitDomainType; final String explicitDomainTypeName = registration.getClazz(); if ( StringHelper.isNotEmpty( explicitDomainTypeName ) ) { - explicitDomainType = classDetailsRegistry.resolveClassDetails( explicitDomainTypeName ); + explicitDomainType = sourceModelContext.getClassDetailsRegistry().resolveClassDetails( explicitDomainTypeName ); } else { explicitDomainType = null; } - final ClassDetails converterType = classDetailsRegistry.resolveClassDetails( registration.getConverter() ); + final ClassDetails converterType = sourceModelContext.getClassDetailsRegistry().resolveClassDetails( registration.getConverter() ); final boolean autoApply = registration.isAutoApply(); collectConverterRegistration( new ConversionRegistration( explicitDomainType, converterType, autoApply, CONVERTER_REG ) ); } ); @@ -359,8 +350,8 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { } registrations.forEach( (reg) -> { - final ClassDetails domainTypeDetails = classDetailsRegistry.resolveClassDetails( reg.getClazz() ); - final ClassDetails descriptorDetails = classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ); + final ClassDetails domainTypeDetails = sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getClazz() ); + final ClassDetails descriptorDetails = sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getDescriptor() ); collectUserTypeRegistration( domainTypeDetails, descriptorDetails ); } ); } @@ -389,8 +380,8 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { } registrations.forEach( (reg) -> collectCompositeUserTypeRegistration( - classDetailsRegistry.resolveClassDetails( reg.getClazz() ), - classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ) + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getClazz() ), + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getDescriptor() ) ) ); } @@ -433,7 +424,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { registrations.forEach( (reg) -> collectCollectionTypeRegistration( reg.getClassification(), - classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ), + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getDescriptor() ), extractParameterMap( reg.getParameters() ) ) ); } @@ -475,8 +466,8 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { } registrations.forEach( (reg) -> collectEmbeddableInstantiatorRegistration( - classDetailsRegistry.resolveClassDetails( reg.getEmbeddableClass() ), - classDetailsRegistry.resolveClassDetails( reg.getInstantiator() ) + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getEmbeddableClass() ), + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( reg.getInstantiator() ) ) ); } @@ -536,7 +527,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { // should resolve to Object - let's see how that reacts final ClassDetails targetClassDetails = XmlAnnotationHelper.resolveJavaType( parameter.getType(), - classDetailsRegistry + sourceModelContext.getClassDetailsRegistry() ); result.put( parameter.getName(), targetClassDetails ); } @@ -592,7 +583,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { } listeners.forEach( (jaxbEntityListener) -> { - final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails( jaxbEntityListener.getClazz() ); + final ClassDetails classDetails = sourceModelContext.getClassDetailsRegistry().resolveClassDetails( jaxbEntityListener.getClazz() ); final JpaEventListener listener = JpaEventListener.from( JpaEventListenerStyle.LISTENER, classDetails, @@ -653,7 +644,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { } private MutableAnnotationUsage makeAnnotation(AnnotationDescriptor annotationDescriptor) { - return annotationDescriptor.createUsage( null, null ); + return annotationDescriptor.createUsage( null, sourceModelContext ); } public void collectSequenceGenerator(AnnotationUsage usage) { @@ -766,7 +757,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { converters.forEach( (jaxbConverter) -> { final String converterClassName = jaxbConverter.getClazz(); assert converterClassName != null; - final ClassDetails converterType = classDetailsRegistry.resolveClassDetails( converterClassName ); + final ClassDetails converterType = sourceModelContext.getClassDetailsRegistry().resolveClassDetails( converterClassName ); final boolean autoApply = jaxbConverter.isAutoApply(); jpaConverters.add( new ConverterRegistration( converterType, autoApply ) ); @@ -828,7 +819,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { final MutableAnnotationUsage entityResultAnnotation = makeAnnotation( JpaAnnotations.ENTITY_RESULT ); entityResults.add( entityResultAnnotation ); - applyAttributeIfSpecified( "entityClass", classDetailsRegistry.resolveClassDetails( jaxbEntityResult.getEntityClass() ), entityResultAnnotation ); + applyAttributeIfSpecified( "entityClass", sourceModelContext.getClassDetailsRegistry().resolveClassDetails( jaxbEntityResult.getEntityClass() ), entityResultAnnotation ); applyAttributeIfSpecified( "lockMode", jaxbEntityResult.getLockMode(), entityResultAnnotation ); applyStringAttributeIfSpecified( "discriminatorColumn", jaxbEntityResult.getDiscriminatorColumn(), entityResultAnnotation ); @@ -860,7 +851,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { result.setAttributeValue( "entityClass", - classDetailsRegistry.resolveClassDetails( jaxbConstructorResult.getTargetClass() ) + sourceModelContext.getClassDetailsRegistry().resolveClassDetails( jaxbConstructorResult.getTargetClass() ) ); if ( !jaxbConstructorResult.getColumns().isEmpty() ) { @@ -885,7 +876,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { columnResults.add( columnResultAnnotation ); columnResultAnnotation.setAttributeValue( "name", jaxbColumn.getName() ); - applyAttributeIfSpecified( "type", classDetailsRegistry.resolveClassDetails( jaxbColumn.getClazz() ), columnResultAnnotation ); + applyAttributeIfSpecified( "type", sourceModelContext.getClassDetailsRegistry().resolveClassDetails( jaxbColumn.getClazz() ), columnResultAnnotation ); } annotationListConsumer.accept( columnResults ); } @@ -977,7 +968,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { queryAnnotation.setAttributeValue( "query", jaxbNamedQuery.getQuery() ); if ( StringHelper.isNotEmpty( jaxbNamedQuery.getResultClass() ) ) { - queryAnnotation.setAttributeValue( "resultClass", classDetailsRegistry.resolveClassDetails( jaxbNamedQuery.getResultClass() ) ); + queryAnnotation.setAttributeValue( "resultClass", sourceModelContext.getClassDetailsRegistry().resolveClassDetails( jaxbNamedQuery.getResultClass() ) ); } applyStringAttributeIfSpecified( "resultSetMapping", jaxbNamedQuery.getResultSetMapping(), queryAnnotation ); @@ -1054,7 +1045,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { final ArrayList resultClasses = arrayList( jaxbQuery.getResultClasses().size() ); applyAttributeIfSpecified( "resultClasses", resultClasses, queryAnnotation ); for ( String resultClassName : jaxbQuery.getResultClasses() ) { - resultClasses.add( classDetailsRegistry.resolveClassDetails( resultClassName ) ); + resultClasses.add( sourceModelContext.getClassDetailsRegistry().resolveClassDetails( resultClassName ) ); } applyAttributeIfSpecified( "resultSetMappings", jaxbQuery.getResultSetMappings(), queryAnnotation ); @@ -1069,7 +1060,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations { applyStringAttributeIfSpecified( "name", jaxbProcedureParameter.getName(), parameterAnnotation ); applyAttributeIfSpecified( "mode", jaxbProcedureParameter.getMode(), parameterAnnotation ); - applyAttributeIfSpecified( "type", classDetailsRegistry.resolveClassDetails( jaxbProcedureParameter.getClazz() ), parameterAnnotation ); + applyAttributeIfSpecified( "type", sourceModelContext.getClassDetailsRegistry().resolveClassDetails( jaxbProcedureParameter.getClazz() ), parameterAnnotation ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/internal/AnnotationUsageHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/models/internal/AnnotationUsageHelper.java index ed11a0125f..ddd2c3d76e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/internal/AnnotationUsageHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/internal/AnnotationUsageHelper.java @@ -3,7 +3,14 @@ package org.hibernate.boot.models.internal; import java.lang.annotation.Annotation; import org.hibernate.internal.util.StringHelper; +import org.hibernate.models.spi.AnnotationDescriptor; +import org.hibernate.models.spi.AnnotationTarget; +import org.hibernate.models.spi.AnnotationUsage; import org.hibernate.models.spi.MutableAnnotationUsage; +import org.hibernate.models.spi.MutableClassDetails; +import org.hibernate.models.spi.SourceModelBuildingContext; + +import jakarta.persistence.NamedQueries; public class AnnotationUsageHelper { @@ -24,4 +31,31 @@ public class AnnotationUsageHelper { annotationUsage.setAttributeValue( attributeName, value ); } } + + public static MutableAnnotationUsage getOrCreateUsage( + Class annotationType, + AnnotationTarget target, + SourceModelBuildingContext modelBuildingContext) { + final MutableAnnotationUsage existing = (MutableAnnotationUsage) target.getAnnotationUsage( annotationType ); + if ( existing != null ) { + return existing; + } + + final AnnotationDescriptor descriptor = modelBuildingContext + .getAnnotationDescriptorRegistry() + .getDescriptor( annotationType ); + return descriptor.createUsage( target, modelBuildingContext ); + } + + public static MutableAnnotationUsage getOrCreateUsage( + AnnotationDescriptor annotationDescriptor, + AnnotationTarget target, + SourceModelBuildingContext modelBuildingContext) { + final MutableAnnotationUsage existing = (MutableAnnotationUsage) target.getAnnotationUsage( annotationDescriptor ); + if ( existing != null ) { + return existing; + } + + return annotationDescriptor.createUsage( target, modelBuildingContext ); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/XmlResourceException.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/XmlResourceException.java index 3e07e41959..2ccffe098e 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/XmlResourceException.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/XmlResourceException.java @@ -7,7 +7,7 @@ package org.hibernate.boot.models.xml; /** - * Generally indicates a problem locating or table an XML resource + * Generally indicates a problem locating an XML resource * * @author Steve Ebersole */ diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java index 8d43f772ba..61fa394cb8 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/ManagedTypeProcessor.java @@ -69,6 +69,7 @@ import jakarta.persistence.MappedSuperclass; import static org.hibernate.internal.util.NullnessHelper.coalesce; import static org.hibernate.internal.util.NullnessHelper.coalesceSuppliedValues; +import static org.hibernate.internal.util.StringHelper.isNotEmpty; /** * Helper for handling managed types defined in mapping XML, in either @@ -461,14 +462,14 @@ public class ManagedTypeProcessor { XmlDocumentContext xmlDocumentContext) { XmlAnnotationHelper.applyEntity( jaxbEntity, classDetails, xmlDocumentContext ); XmlAnnotationHelper.applyInheritance( jaxbEntity, classDetails, xmlDocumentContext ); - classDetails.addAnnotationUsage( XmlAnnotationHelper.createAccessAnnotation( classAccessType, classDetails, xmlDocumentContext ) ); + applyAccessAnnotation( classAccessType, classDetails, xmlDocumentContext ); applyCaching( jaxbEntity, classDetails, xmlDocumentContext ); if ( jaxbEntity.isAbstract() != null ) { XmlProcessingHelper.makeAnnotation( Abstract.class, classDetails, xmlDocumentContext ); } - if ( StringHelper.isNotEmpty( jaxbEntity.getExtends() ) ) { + if ( isNotEmpty( jaxbEntity.getExtends() ) ) { final MutableAnnotationUsage extendsAnn = HibernateAnnotations.EXTENDS.createUsage( classDetails, xmlDocumentContext.getModelBuildingContext() @@ -505,6 +506,10 @@ public class ManagedTypeProcessor { ); } + QueryProcessing.applyNamedQueries( jaxbEntity, classDetails, xmlDocumentContext ); + QueryProcessing.applyNamedNativeQueries( jaxbEntity, classDetails, jaxbRoot, xmlDocumentContext ); + QueryProcessing.applyNamedProcedureQueries( jaxbEntity, classDetails, xmlDocumentContext ); + jaxbEntity.getFilters().forEach( jaxbFilter -> XmlAnnotationHelper.applyFilter( jaxbFilter, classDetails, @@ -548,6 +553,15 @@ public class ManagedTypeProcessor { // todo : secondary-tables } + private static void applyAccessAnnotation( + AccessType accessType, + MutableClassDetails target, + XmlDocumentContext xmlDocumentContext) { + final MutableAnnotationUsage annotationUsage = XmlProcessingHelper.makeAnnotation( Access.class, target, xmlDocumentContext ); + annotationUsage.setAttributeValue( "value", accessType ); + target.addAnnotationUsage( annotationUsage ); + } + private static void applyCaching( JaxbEntityImpl jaxbEntity, MutableClassDetails classDetails, @@ -569,7 +583,7 @@ public class ManagedTypeProcessor { classDetails.addAnnotationUsage( cacheableUsage ); XmlProcessingHelper.applyAttributeIfSpecified( "region", jaxbCaching.getRegion(), cacheableUsage ); XmlProcessingHelper.applyAttributeIfSpecified( - "access", + "usage", convertCacheAccessType( jaxbCaching.getAccess() ), cacheableUsage ); diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/QueryProcessing.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/QueryProcessing.java new file mode 100644 index 0000000000..4847fd6791 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/QueryProcessing.java @@ -0,0 +1,358 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later. + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html. + */ +package org.hibernate.boot.models.xml.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.CacheMode; +import org.hibernate.annotations.NamedNativeQueries; +import org.hibernate.annotations.NamedNativeQuery; +import org.hibernate.annotations.NamedQueries; +import org.hibernate.annotations.NamedQuery; +import org.hibernate.boot.jaxb.mapping.spi.JaxbColumnResultImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedNativeQueryImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedQueryBase; +import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedQueryImpl; +import org.hibernate.boot.jaxb.mapping.spi.JaxbQueryHint; +import org.hibernate.boot.jaxb.mapping.spi.JaxbSynchronizedTableImpl; +import org.hibernate.boot.models.HibernateAnnotations; +import org.hibernate.boot.models.JpaAnnotations; +import org.hibernate.boot.models.internal.AnnotationUsageHelper; +import org.hibernate.boot.models.xml.spi.XmlDocumentContext; +import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.models.spi.AnnotationTarget; +import org.hibernate.models.spi.AnnotationUsage; +import org.hibernate.models.spi.ClassDetails; +import org.hibernate.models.spi.MutableAnnotationUsage; +import org.hibernate.models.spi.MutableClassDetails; +import org.hibernate.models.spi.SourceModelBuildingContext; + +import jakarta.persistence.ColumnResult; +import jakarta.persistence.QueryHint; + +import static org.hibernate.internal.util.StringHelper.isEmpty; +import static org.hibernate.internal.util.StringHelper.isNotEmpty; + +/** + * @author Steve Ebersole + */ +public class QueryProcessing { + public static void applyNamedQueries( + JaxbEntityImpl jaxbEntity, + MutableClassDetails classDetails, + XmlDocumentContext xmlDocumentContext) { + if ( CollectionHelper.isEmpty( jaxbEntity.getNamedQueries() ) ) { + return; + } + + final SourceModelBuildingContext modelBuildingContext = xmlDocumentContext.getModelBuildingContext(); + List> namedHqlQueryList = null; + List> namedJpqlQueryList = null; + + for ( int i = 0; i < jaxbEntity.getNamedQueries().size(); i++ ) { + final JaxbNamedQueryImpl jaxbNamedQuery = jaxbEntity.getNamedQueries().get( i ); + + if ( CollectionHelper.isNotEmpty( jaxbNamedQuery.getHints() ) ) { + // treat this as a Jakarta Persistence named-query + if ( namedJpqlQueryList == null ) { + final MutableAnnotationUsage namedJpqlQueriesUsage = AnnotationUsageHelper.getOrCreateUsage( + JpaAnnotations.NAMED_QUERIES, + classDetails, + modelBuildingContext + ); + classDetails.addAnnotationUsage( namedJpqlQueriesUsage ); + + namedJpqlQueryList = new ArrayList<>(); + namedJpqlQueriesUsage.setAttributeValue( "value", namedJpqlQueryList ); + } + applyNamedJpqlQuery( jaxbNamedQuery, classDetails, namedJpqlQueryList, modelBuildingContext ); + } + else { + // treat this as a named HQL query + if ( namedHqlQueryList == null ) { + final MutableAnnotationUsage namedHqlQueriesUsage = AnnotationUsageHelper.getOrCreateUsage( + HibernateAnnotations.NAMED_QUERIES, + classDetails, + modelBuildingContext + ); + classDetails.addAnnotationUsage( namedHqlQueriesUsage ); + + namedHqlQueryList = new ArrayList<>(); + namedHqlQueriesUsage.setAttributeValue( "value", namedHqlQueryList ); + } + applyNamedHqlQuery( jaxbNamedQuery, classDetails, namedHqlQueryList, modelBuildingContext ); + } + } + } + + public static void applyNamedHqlQuery( + JaxbNamedQueryImpl jaxbNamedQuery, + MutableClassDetails classDetails, + List> namedQueryList, + SourceModelBuildingContext modelBuildingContext) { + final MutableAnnotationUsage namedQueryUsage = HibernateAnnotations.NAMED_QUERY.createUsage( + classDetails, + modelBuildingContext + ); + namedQueryList.add( namedQueryUsage ); + + namedQueryUsage.setAttributeValue( "name", jaxbNamedQuery.getName() ); + namedQueryUsage.setAttributeValue( "query", jaxbNamedQuery.getQuery() ); + + AnnotationUsageHelper.applyAttributeIfSpecified( "comment", jaxbNamedQuery.getComment(), namedQueryUsage ); + + AnnotationUsageHelper.applyAttributeIfSpecified( "readOnly", jaxbNamedQuery.isReadOnly(), namedQueryUsage ); + AnnotationUsageHelper.applyAttributeIfSpecified( "flushMode", jaxbNamedQuery.getFlushMode(), namedQueryUsage ); + if ( jaxbNamedQuery.isCacheable() == Boolean.TRUE ) { + namedQueryUsage.setAttributeValue( "cacheable", true ); + AnnotationUsageHelper.applyAttributeIfSpecified( "cacheRegion", jaxbNamedQuery.getCacheRegion(), namedQueryUsage ); + AnnotationUsageHelper.applyAttributeIfSpecified( "cacheMode", jaxbNamedQuery.getCacheMode(), namedQueryUsage ); + + final CacheMode cacheMode = jaxbNamedQuery.getCacheMode(); + if ( cacheMode != null && cacheMode != CacheMode.IGNORE ) { + switch ( cacheMode ) { + case GET -> { + AnnotationUsageHelper.applyAttributeIfSpecified( "cacheRegion", cacheMode, namedQueryUsage ); + AnnotationUsageHelper.applyAttributeIfSpecified( "cacheRegion", cacheMode, namedQueryUsage ); + } + } + AnnotationUsageHelper.applyAttributeIfSpecified( "cacheRegion", cacheMode, namedQueryUsage ); + AnnotationUsageHelper.applyAttributeIfSpecified( "cacheRegion", cacheMode, namedQueryUsage ); + } + } + AnnotationUsageHelper.applyAttributeIfSpecified( "fetchSize", jaxbNamedQuery.getFetchSize(), namedQueryUsage ); + AnnotationUsageHelper.applyAttributeIfSpecified( "timeout", jaxbNamedQuery.getTimeout(), namedQueryUsage ); + + AnnotationUsageHelper.applyAttributeIfSpecified( "timeout", jaxbNamedQuery.getTimeout(), namedQueryUsage ); + } + + private static void applyNamedJpqlQuery( + JaxbNamedQueryImpl jaxbNamedQuery, + ClassDetails classDetails, + List> namedQueryList, + SourceModelBuildingContext modelBuildingContext) { + final MutableAnnotationUsage namedQueryUsage = JpaAnnotations.NAMED_QUERY.createUsage( + classDetails, + modelBuildingContext + ); + namedQueryList.add( namedQueryUsage ); + + namedQueryUsage.setAttributeValue( "name", jaxbNamedQuery.getName() ); + namedQueryUsage.setAttributeValue( "query", jaxbNamedQuery.getQuery() ); + + applyQueryHints( jaxbNamedQuery, classDetails, namedQueryUsage, modelBuildingContext ); + } + + private static void applyQueryHints( + JaxbNamedQueryBase jaxbNamedQuery, + ClassDetails classDetails, + MutableAnnotationUsage namedQueryUsage, + SourceModelBuildingContext modelBuildingContext) { + if ( CollectionHelper.isNotEmpty( jaxbNamedQuery.getHints() ) ) { + final ArrayList> hints = CollectionHelper.arrayList( jaxbNamedQuery.getHints().size() ); + namedQueryUsage.setAttributeValue( "hints", hints ); + + for ( JaxbQueryHint jaxbHint : jaxbNamedQuery.getHints() ) { + final MutableAnnotationUsage queryHintUsage = JpaAnnotations.QUERY_HINT.createUsage( + classDetails, + modelBuildingContext + ); + queryHintUsage.setAttributeValue( "name", jaxbHint.getName() ); + queryHintUsage.setAttributeValue( "value", jaxbHint.getValue() ); + } + } + } + + public static void applyNamedNativeQueries( + JaxbEntityImpl jaxbEntity, + MutableClassDetails classDetails, + JaxbEntityMappingsImpl jaxbRoot, + XmlDocumentContext xmlDocumentContext) { + if ( CollectionHelper.isEmpty( jaxbEntity.getNamedNativeQueries() ) ) { + return; + } + + + final SourceModelBuildingContext modelBuildingContext = xmlDocumentContext.getModelBuildingContext(); + List> namedHibernateQueryList = null; + List> namedJpaQueryList = null; + + for ( int i = 0; i < jaxbEntity.getNamedNativeQueries().size(); i++ ) { + final JaxbNamedNativeQueryImpl jaxbNamedQuery = jaxbEntity.getNamedNativeQueries().get( i ); + + if ( needsJpaNativeQuery( jaxbNamedQuery ) ) { + // @jakarta.persistence.NamedNativeQuery + if ( namedJpaQueryList == null ) { + final MutableAnnotationUsage namedQueriesUsage = AnnotationUsageHelper.getOrCreateUsage( + JpaAnnotations.NAMED_NATIVE_QUERIES, + classDetails, + modelBuildingContext + ); + classDetails.addAnnotationUsage( namedQueriesUsage ); + + namedJpaQueryList = new ArrayList<>(); + namedQueriesUsage.setAttributeValue( "value", namedQueriesUsage ); + } + + applyJpaNativeQuery( jaxbNamedQuery, classDetails, namedJpaQueryList, modelBuildingContext, xmlDocumentContext ); + } + else { + // @org.hibernate.annotations.NamedNativeQuery + if ( namedHibernateQueryList == null ) { + final MutableAnnotationUsage namedQueriesUsage = AnnotationUsageHelper.getOrCreateUsage( + HibernateAnnotations.NAMED_NATIVE_QUERIES, + classDetails, + modelBuildingContext + ); + classDetails.addAnnotationUsage( namedQueriesUsage ); + + namedHibernateQueryList = new ArrayList<>(); + namedQueriesUsage.setAttributeValue( "value", namedHibernateQueryList ); + } + + applyHibernateNativeQuery( jaxbNamedQuery, classDetails, namedHibernateQueryList, modelBuildingContext, xmlDocumentContext ); + } + } + } + + private static boolean needsJpaNativeQuery(JaxbNamedNativeQueryImpl jaxbNamedQuery) { + return CollectionHelper.isNotEmpty( jaxbNamedQuery.getHints() ) + || CollectionHelper.isNotEmpty( jaxbNamedQuery.getColumnResult() ) + || CollectionHelper.isNotEmpty( jaxbNamedQuery.getConstructorResult() ) + || CollectionHelper.isNotEmpty( jaxbNamedQuery.getEntityResult() ); + } + + private static void applyJpaNativeQuery( + JaxbNamedNativeQueryImpl jaxbNamedQuery, + MutableClassDetails classDetails, + List> namedQueryUsageList, + SourceModelBuildingContext modelBuildingContext, + XmlDocumentContext xmlDocumentContext) { + final MutableAnnotationUsage namedQueryUsage = JpaAnnotations.NAMED_NATIVE_QUERY.createUsage( + classDetails, + modelBuildingContext + ); + namedQueryUsageList.add( namedQueryUsage ); + + namedQueryUsage.setAttributeValue( "name", jaxbNamedQuery.getName() ); + namedQueryUsage.setAttributeValue( "query", jaxbNamedQuery.getQuery() ); + applyQueryHints( jaxbNamedQuery, classDetails, namedQueryUsage, modelBuildingContext ); + + applyResultClassAndSynchronizations( jaxbNamedQuery, namedQueryUsage, modelBuildingContext, xmlDocumentContext ); + applyResultSetMappings( jaxbNamedQuery, namedQueryUsage, modelBuildingContext, xmlDocumentContext ); + applyResults( jaxbNamedQuery, namedQueryUsage, classDetails, modelBuildingContext, xmlDocumentContext ); + } + + private static void applyResultClassAndSynchronizations( + JaxbNamedNativeQueryImpl jaxbNamedQuery, + MutableAnnotationUsage namedQueryUsage, + SourceModelBuildingContext modelBuildingContext, + XmlDocumentContext xmlDocumentContext) { + final List syncSpaces = new ArrayList<>(); + + if ( jaxbNamedQuery.getResultClass() != null ) { + final String resultClassName = xmlDocumentContext.resolveClassName( jaxbNamedQuery.getResultClass() ); + syncSpaces.add( resultClassName ); + namedQueryUsage.setAttributeValue( + "resultClass", + modelBuildingContext.getClassDetailsRegistry().getClassDetails( resultClassName ) + ); + } + + for ( JaxbSynchronizedTableImpl synchronization : jaxbNamedQuery.getSynchronizations() ) { + syncSpaces.add( synchronization.getTable() ); + } + + if ( CollectionHelper.isNotEmpty( syncSpaces ) ) { + namedQueryUsage.setAttributeValue( "querySpaces", syncSpaces ); + } + } + + private static void applyResultSetMappings( + JaxbNamedNativeQueryImpl jaxbNamedQuery, + MutableAnnotationUsage namedQueryUsage, + SourceModelBuildingContext modelBuildingContext, + XmlDocumentContext xmlDocumentContext) { + if ( isEmpty( jaxbNamedQuery.getResultSetMapping() ) ) { + return; + } + + namedQueryUsage.setAttributeValue( "resultSetMapping", jaxbNamedQuery.getResultSetMapping() ); + } + + private static void applyResults( + JaxbNamedNativeQueryImpl jaxbNamedQuery, + MutableAnnotationUsage namedQueryUsage, + AnnotationTarget annotationTarget, + SourceModelBuildingContext modelBuildingContext, + XmlDocumentContext xmlDocumentContext) { + if ( CollectionHelper.isNotEmpty( jaxbNamedQuery.getColumnResult() ) ) { + for ( JaxbColumnResultImpl jaxbColumnResult : jaxbNamedQuery.getColumnResult() ) { + final MutableAnnotationUsage columnResultUsage = JpaAnnotations.COLUMN_RESULT.createUsage( + annotationTarget, + modelBuildingContext + ); + columnResultUsage.setAttributeValue( "name", jaxbColumnResult.getName() ); + if ( isNotEmpty( jaxbColumnResult.getClazz() ) ) { + final String className = xmlDocumentContext.resolveClassName( jaxbColumnResult.getClazz() ); + columnResultUsage.setAttributeValue( "type", modelBuildingContext.getClassDetailsRegistry().getClassDetails( className ) ); + } + } + } + + // todo (7.0) : finish the rest + } + + private static void applyHibernateNativeQuery( + JaxbNamedNativeQueryImpl jaxbNamedQuery, + MutableClassDetails classDetails, + List> namedQueryUsageList, + SourceModelBuildingContext modelBuildingContext, + XmlDocumentContext xmlDocumentContext) { + final MutableAnnotationUsage namedQueryUsage = HibernateAnnotations.NAMED_NATIVE_QUERY.createUsage( + classDetails, + modelBuildingContext + ); + namedQueryUsageList.add( namedQueryUsage ); + + namedQueryUsage.setAttributeValue( "name", jaxbNamedQuery.getName() ); + namedQueryUsage.setAttributeValue( "query", jaxbNamedQuery.getQuery() ); + + applyResultClassAndSynchronizations( jaxbNamedQuery, namedQueryUsage, modelBuildingContext, xmlDocumentContext ); + applyResultSetMappings( jaxbNamedQuery, namedQueryUsage, modelBuildingContext, xmlDocumentContext ); + } + + public static void applyNamedProcedureQueries( + JaxbEntityImpl jaxbEntity, + MutableClassDetails classDetails, + XmlDocumentContext xmlDocumentContext) { + // todo (7.0) : implement + } + + private static void applyNamedQueryBaseDetails( + JaxbNamedQueryBase baseDetails, + MutableAnnotationUsage namedQueryUsage, + MutableClassDetails classDetails, + SourceModelBuildingContext modelBuildingContext) { + assert isNotEmpty( baseDetails.getName() ); + namedQueryUsage.setAttributeValue( "name", baseDetails.getName() ); + + if ( CollectionHelper.isNotEmpty( baseDetails.getHints() ) ) { + final ArrayList> hints = CollectionHelper.arrayList( baseDetails.getHints().size() ); + namedQueryUsage.setAttributeValue( "hints", hints ); + + for ( JaxbQueryHint jaxbHint : baseDetails.getHints() ) { + final MutableAnnotationUsage queryHintUsage = JpaAnnotations.QUERY_HINT.createUsage( classDetails, modelBuildingContext ); + queryHintUsage.setAttributeValue( "name", jaxbHint.getName() ); + queryHintUsage.setAttributeValue( "value", jaxbHint.getValue() ); + } + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java index 65e13d6bbe..36c4e157df 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/internal/XmlAnnotationHelper.java @@ -87,6 +87,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbUuidGeneratorImpl; import org.hibernate.boot.jaxb.mapping.spi.db.JaxbCheckable; import org.hibernate.boot.jaxb.mapping.spi.db.JaxbColumnJoined; import org.hibernate.boot.jaxb.mapping.spi.db.JaxbTableMapping; +import org.hibernate.boot.models.JpaAnnotations; import org.hibernate.boot.models.categorize.spi.JpaEventListener; import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle; import org.hibernate.boot.models.internal.AnnotationUsageHelper; @@ -155,6 +156,7 @@ import jakarta.persistence.UniqueConstraint; import static java.lang.Boolean.FALSE; import static java.util.Collections.emptyList; +import static org.hibernate.boot.models.JpaAnnotations.NAMED_ATTRIBUTE_NODE; import static org.hibernate.boot.models.xml.internal.XmlProcessingHelper.makeNestedAnnotation; /** @@ -308,7 +310,13 @@ public class XmlAnnotationHelper { } final List> joinColumns = new ArrayList<>( jaxbJoinColumns.size() ); jaxbJoinColumns.forEach( jaxbJoinColumn -> { - joinColumns.add( applyJoinColumn( jaxbJoinColumn, memberDetails, xmlDocumentContext ) ); + final MutableAnnotationUsage annotationUsage = createJoinColumnAnnotation( + jaxbJoinColumn, + memberDetails, + JoinColumn.class, + xmlDocumentContext + ); + joinColumns.add( annotationUsage ); } ); return joinColumns; } @@ -1554,10 +1562,9 @@ public class XmlAnnotationHelper { final List> namedAttributeNodeAnnotations = new ArrayList<>( namedAttributeNodes.size() ); for ( JaxbNamedAttributeNodeImpl namedAttributeNode : namedAttributeNodes ) { - final MutableAnnotationUsage namedAttributeNodeAnn = makeNestedAnnotation( - NamedAttributeNode.class, + final MutableAnnotationUsage namedAttributeNodeAnn = NAMED_ATTRIBUTE_NODE.createUsage( target, - xmlDocumentContext + xmlDocumentContext.getModelBuildingContext() ); XmlProcessingHelper.applyAttributeIfSpecified( "value", namedAttributeNode.getName(), namedAttributeNodeAnn ); final AnnotationDescriptor namedAttributeNodeDescriptor = xmlDocumentContext diff --git a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/spi/XmlDocumentContext.java b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/spi/XmlDocumentContext.java index bc172bc709..58590bb6d9 100644 --- a/hibernate-core/src/main/java/org/hibernate/boot/models/xml/spi/XmlDocumentContext.java +++ b/hibernate-core/src/main/java/org/hibernate/boot/models/xml/spi/XmlDocumentContext.java @@ -22,6 +22,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbPluralAttribute; import org.hibernate.boot.jaxb.mapping.spi.JaxbUserTypeImpl; import org.hibernate.boot.models.xml.internal.XmlAnnotationHelper; import org.hibernate.boot.spi.BootstrapContext; +import org.hibernate.internal.util.StringHelper; import org.hibernate.models.internal.dynamic.DynamicClassDetails; import org.hibernate.models.spi.ClassDetails; import org.hibernate.models.spi.MutableClassDetails; @@ -196,4 +197,15 @@ public interface XmlDocumentContext { throw new HibernateException( "Unable to create instance from incoming ClassDetails - " + classDetails ); } } + + default String resolveClassName(String specifiedName) { + if ( specifiedName.contains( "." ) ) { + return specifiedName; + } + + return StringHelper.qualifyConditionallyIfNot( + getXmlDocument().getDefaults().getPackage(), + specifiedName + ); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/idclass/xml/IdClassXmlTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/idclass/xml/IdClassXmlTest.java index 2183b637f3..4840fb7345 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/idclass/xml/IdClassXmlTest.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/idclass/xml/IdClassXmlTest.java @@ -7,6 +7,8 @@ package org.hibernate.orm.test.annotations.idclass.xml; +import java.util.List; + import org.hibernate.community.dialect.AltibaseDialect; import org.hibernate.query.Query; @@ -41,7 +43,8 @@ public class IdClassXmlTest { session.persist( link ); Query q = session.getNamedQuery( "testQuery" ); - assertEquals( 1, q.list().size() ); + final List list = q.list(); + assertEquals( 1, list.size() ); } ); } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/MyStringType.java b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/MyStringType.java index 19b51915d0..4f73065015 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/MyStringType.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/annotations/type/dynamicparameterized/MyStringType.java @@ -32,6 +32,7 @@ import java.util.Properties; import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.models.spi.FieldDetails; import org.hibernate.usertype.DynamicParameterizedType; import org.hibernate.usertype.UserType; @@ -75,9 +76,9 @@ public class MyStringType implements UserType, DynamicParameterizedType String entity = params.getProperty( DynamicParameterizedType.ENTITY ); String propertyName = params.getProperty( DynamicParameterizedType.PROPERTY ); - XProperty xproperty = (XProperty) params.get( DynamicParameterizedType.XPROPERTY ); + FieldDetails xproperty = (FieldDetails) params.get( DynamicParameterizedType.XPROPERTY ); Assert.assertEquals( propertyName, xproperty.getName() ); - Assert.assertEquals( entity, xproperty.getDeclaringClass().getName() ); + Assert.assertEquals( entity, xproperty.getDeclaringType().getName() ); Assert.assertEquals( String.class.getName(), xproperty.getType().getName() ); String tableName = propertyName.toUpperCase().split( "_" )[0]; diff --git a/settings.gradle b/settings.gradle index f02b4e4bc1..0ae7339c8a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -70,7 +70,7 @@ dependencyResolutionManagement { def byteBuddyVersion = version "byteBuddy", "1.14.18" def classmateVersion = version "classmate", "1.5.1" def geolatteVersion = version "geolatte", "1.9.1" - def hibernateModelsVersion = version "hibernateModels", "0.7.2" + def hibernateModelsVersion = version "hibernateModels", "0.7.3" def jandexVersion = version "jandex", "3.2.0" def hcannVersion = version "hcann", "7.0.1.Final" def jacksonVersion = version "jackson", "2.17.0"