From b89d9b96affe97797644db139dfa1cc01f5c472a Mon Sep 17 00:00:00 2001 From: Steve Ebersole Date: Tue, 17 Oct 2023 17:51:41 -0500 Subject: [PATCH] Start work Processor and global XML handling --- .../models/internal/SimpleClassLoading.java | 5 + .../hibernate/models/spi/ClassLoading.java | 2 + .../hibernate-models-orm.gradle | 1 + .../AbstractIdentifiableTypeMetadata.java | 262 ++++++++++ .../internal/AbstractManagedTypeMetadata.java | 292 ++++++++++++ ...ractPersistentAttributeMemberResolver.java | 102 ++++ .../orm/internal/AttributeMetadataImpl.java | 45 ++ .../internal/CollectionTypeRegistration.java | 42 ++ .../CompositeUserTypeRegistration.java | 30 ++ .../orm/internal/ConversionRegistration.java | 168 +++++++ .../EmbeddableInstantiatorRegistration.java | 32 ++ .../orm/internal/EntityHierarchyBuilder.java | 211 +++++++++ .../orm/internal/EntityHierarchyImpl.java | 116 +++++ .../internal/EntityListenerRegistration.java | 148 ++++++ .../orm/internal/EntityTypeMetadataImpl.java | 295 ++++++++++++ .../orm/internal/IdGeneratorRegistration.java | 57 +++ .../orm/internal/JavaTypeRegistration.java | 32 ++ .../orm/internal/JdbcTypeRegistration.java | 32 ++ .../orm/internal/ManagedResourcesImpl.java | 103 ++++ .../MappedSuperclassTypeMetadataImpl.java | 56 +++ .../orm/internal/NamedQueryRegistration.java | 56 +++ .../internal/OrmModelBuildingContextImpl.java | 77 +++ .../orm/internal/ProcessResultCollector.java | 407 ++++++++++++++++ .../orm/internal/ProcessResultImpl.java | 129 +++++ ...dardPersistentAttributeMemberResolver.java | 188 ++++++++ .../orm/internal/UserTypeRegistration.java | 30 ++ .../models/orm/spi/AttributeMetadata.java | 11 + .../models/orm/spi/EntityTypeMetadata.java | 2 +- .../orm/spi/IdentifiableTypeMetadata.java | 24 +- .../models/orm/spi/ManagedResources.java | 22 + .../models/orm/spi/ManagedTypeMetadata.java | 5 +- .../orm/spi/MappedSuperclassTypeMetadata.java | 2 +- .../orm/spi/OrmModelBuildingContext.java | 2 +- .../PersistentAttributeMemberResolver.java | 44 ++ .../models/orm/spi/ProcessResult.java | 55 +++ .../hibernate/models/orm/spi/Processor.java | 448 ++++++++++++++++++ .../models/orm/xml/XmlResourceException.java | 24 + .../internal/PersistenceUnitMetadataImpl.java | 158 ++++++ .../internal/ResourceStreamLocatorImpl.java | 36 ++ .../orm/xml/internal/XmlDocumentImpl.java | 309 ++++++++++++ .../orm/xml/internal/XmlProcessingState.java | 29 ++ .../orm/xml/spi/PersistenceUnitMetadata.java | 69 +++ .../models/orm/xml/spi/XmlDocument.java | 80 ++++ .../orm/xml/spi/XmlProcessingContext.java | 13 + .../models/orm/xml/spi/XmlResources.java | 37 ++ .../org/hibernate/models/orm/XmlHelper.java | 60 +++ .../models/orm/process/MyStringConverter.java | 26 + .../models/orm/process/MyUuidConverter.java | 28 ++ .../hibernate/models/orm/process/Person.java | 45 ++ .../orm/process/ProcessorSmokeTests.java | 76 +++ .../hibernate/models/orm/process/Root.java | 25 + .../orm/process/SimpleProcessorTests.java | 148 ++++++ .../org/hibernate/models/orm/process/Sub.java | 32 ++ .../hibernate/models/orm/xml/MyUserType.java | 74 +++ .../models/orm/xml/SimpleEntity.java | 43 ++ .../orm/xml/XmlProcessingSmokeTests.java | 144 ++++++ .../src/test/resources/mappings/globals.xml | 16 + .../src/test/resources/mappings/simple1.xml | 28 ++ .../src/test/resources/mappings/simple2.xml | 27 ++ .../hibernate-models-source.gradle | 1 + .../internal/ClassDetailsRegistryImpl.java | 7 +- .../internal/jandex/JandexClassDetails.java | 2 +- .../internal/jandex/JandexPackageDetails.java | 64 +++ .../source/internal/jdk/JdkBuilders.java | 20 + .../source/internal/jdk/JdkClassDetails.java | 6 +- .../internal/jdk/JdkPackageDetailsImpl.java | 51 ++ .../org/hibernate/models/source/Counter.java | 46 -- .../models/source/MixedSourcesTests.java | 8 +- .../hibernate-models-testing.gradle | 12 + .../models/source/SourceModelTestHelper.java | 2 +- .../SourceModelTestHelperSmokeTests.java | 10 +- settings.gradle | 1 + 72 files changed, 5218 insertions(+), 72 deletions(-) create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractIdentifiableTypeMetadata.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractManagedTypeMetadata.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractPersistentAttributeMemberResolver.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AttributeMetadataImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CollectionTypeRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CompositeUserTypeRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ConversionRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EmbeddableInstantiatorRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyBuilder.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityListenerRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityTypeMetadataImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/IdGeneratorRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JavaTypeRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JdbcTypeRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ManagedResourcesImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/MappedSuperclassTypeMetadataImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/NamedQueryRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/OrmModelBuildingContextImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/StandardPersistentAttributeMemberResolver.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/UserTypeRegistration.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedResources.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/PersistentAttributeMemberResolver.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/Processor.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/XmlResourceException.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/PersistenceUnitMetadataImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/ResourceStreamLocatorImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlDocumentImpl.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlProcessingState.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/PersistenceUnitMetadata.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlDocument.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlProcessingContext.java create mode 100644 hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlResources.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/XmlHelper.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyStringConverter.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyUuidConverter.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/ProcessorSmokeTests.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Root.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Sub.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/MyUserType.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/SimpleEntity.java create mode 100644 hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java create mode 100644 hibernate-models-orm/src/test/resources/mappings/globals.xml create mode 100644 hibernate-models-orm/src/test/resources/mappings/simple1.xml create mode 100644 hibernate-models-orm/src/test/resources/mappings/simple2.xml create mode 100644 hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexPackageDetails.java create mode 100644 hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkPackageDetailsImpl.java delete mode 100644 hibernate-models-source/src/test/java/org/hibernate/models/source/Counter.java create mode 100644 hibernate-models-testing/hibernate-models-testing.gradle rename hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelper.java => hibernate-models-testing/src/main/java/org/hibernate/models/source/SourceModelTestHelper.java (99%) rename hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelperSmokeTest.java => hibernate-models-testing/src/test/java/org/hibernate/models/source/SourceModelTestHelperSmokeTests.java (90%) diff --git a/hibernate-models-common/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java b/hibernate-models-common/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java index d4f4a2f..d0fb4de 100644 --- a/hibernate-models-common/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java +++ b/hibernate-models-common/src/main/java/org/hibernate/models/internal/SimpleClassLoading.java @@ -28,6 +28,11 @@ public Class classForName(String name) { } } + @Override + public Package packageForName(String name) { + return getClass().getClassLoader().getDefinedPackage( name ); + } + @Override public URL locateResource(String resourceName) { return getClass().getClassLoader().getResource( resourceName ); diff --git a/hibernate-models-common/src/main/java/org/hibernate/models/spi/ClassLoading.java b/hibernate-models-common/src/main/java/org/hibernate/models/spi/ClassLoading.java index 652fa91..46555f1 100644 --- a/hibernate-models-common/src/main/java/org/hibernate/models/spi/ClassLoading.java +++ b/hibernate-models-common/src/main/java/org/hibernate/models/spi/ClassLoading.java @@ -26,6 +26,8 @@ public interface ClassLoading { */ Class classForName(String name); + Package packageForName(String name); + /** * Locate a resource by name * diff --git a/hibernate-models-orm/hibernate-models-orm.gradle b/hibernate-models-orm/hibernate-models-orm.gradle index f629fd0..ce635be 100644 --- a/hibernate-models-orm/hibernate-models-orm.gradle +++ b/hibernate-models-orm/hibernate-models-orm.gradle @@ -18,6 +18,7 @@ dependencies { annotationProcessor libs.logging annotationProcessor libs.loggingAnnotations + testImplementation project( ":hibernate-models-testing" ) testImplementation testLibs.jpa testImplementation libs.hibernateCore testRuntimeOnly testLibs.log4j diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractIdentifiableTypeMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractIdentifiableTypeMetadata.java new file mode 100644 index 0000000..5b453d9 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractIdentifiableTypeMetadata.java @@ -0,0 +1,262 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import java.util.function.Consumer; + +import org.hibernate.MappingException; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.IdentifiableTypeMetadata; +import org.hibernate.models.orm.spi.JpaAnnotations; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.ClassDetailsRegistry; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; + + +/** + * @author Steve Ebersole + */ +public abstract class AbstractIdentifiableTypeMetadata + extends AbstractManagedTypeMetadata + implements IdentifiableTypeMetadata { + private final EntityHierarchy hierarchy; + private final AbstractIdentifiableTypeMetadata superType; + private final Set subTypes = new HashSet<>(); + + private final AccessType accessType; + + + /** + * This form is intended for construction of root Entity, and any of + * its MappedSuperclasses + * + * @param classDetails The Entity/MappedSuperclass class descriptor + * @param hierarchy Details about the hierarchy + * @param isRootEntity Whether this descriptor is for the root entity itself, or + * one of its mapped-superclasses. + * @param accessType The default AccessType for the hierarchy + * @param processingContext The context + */ + public AbstractIdentifiableTypeMetadata( + ClassDetails classDetails, + EntityHierarchy hierarchy, + boolean isRootEntity, + AccessType accessType, + Consumer typeConsumer, + OrmModelBuildingContext processingContext) { + super( classDetails, processingContext ); + + typeConsumer.accept( this ); + + this.hierarchy = hierarchy; + this.accessType = determineAccessType( accessType ); + + // walk up + this.superType = walkRootSuperclasses( classDetails, accessType, typeConsumer ); + + if ( isRootEntity ) { + // walk down + walkSubclasses( classDetails, this, this.accessType, typeConsumer ); + } + + // the idea here is to collect up class-level annotations and to apply + // the maps from supers + collectConversionInfo(); + collectAttributeOverrides(); + collectAssociationOverrides(); + } + + private AccessType determineAccessType(AccessType defaultAccessType) { + final AnnotationUsage annotation = getClassDetails().getUsage( JpaAnnotations.ACCESS ); + if ( annotation != null ) { + return annotation.getAttributeValue( "value" ); + } + + return defaultAccessType; + } + + /** + * This form is intended for cases where the entity/mapped-superclass + * is part of the root subclass tree. + * + * @param classDetails The entity/mapped-superclass class descriptor + * @param hierarchy The hierarchy + * @param superType The metadata for the super type. + * @param processingContext The binding context + */ + public AbstractIdentifiableTypeMetadata( + ClassDetails classDetails, + EntityHierarchy hierarchy, + AbstractIdentifiableTypeMetadata superType, + Consumer typeConsumer, + OrmModelBuildingContext processingContext) { + super( classDetails, processingContext ); + + typeConsumer.accept( this ); + + this.hierarchy = hierarchy; + this.superType = superType; + this.accessType = determineAccessType( superType.getAccessType() ); + + // the idea here is to collect up class-level annotations and to apply + // the maps from supers + collectConversionInfo(); + collectAttributeOverrides(); + collectAssociationOverrides(); + } + + private AbstractIdentifiableTypeMetadata walkRootSuperclasses( + ClassDetails classDetails, + AccessType hierarchyAccessType, + Consumer typeConsumer) { + final ClassDetails superTypeClassDetails = classDetails.getSuperType(); + if ( superTypeClassDetails == null ) { + return null; + } + + // make triple sure there is no @Entity annotation + if ( isEntity( superTypeClassDetails ) ) { + throw new MappingException( + String.format( + Locale.ENGLISH, + "Unexpected @Entity [%s] as MappedSuperclass of entity hierarchy", + superTypeClassDetails.getName() + ) + ); + } + else if ( isMappedSuperclass( superTypeClassDetails ) ) { + final MappedSuperclassTypeMetadataImpl superType = new MappedSuperclassTypeMetadataImpl( + superTypeClassDetails, + getHierarchy(), + hierarchyAccessType, + typeConsumer, + getModelContext() + ); + superType.addSubclass( this ); + return superType; + } + else { + // otherwise, we might have an "intermediate" subclass + if ( superTypeClassDetails.getSuperType() != null ) { + return walkRootSuperclasses( superTypeClassDetails, hierarchyAccessType, typeConsumer ); + } + else { + return null; + } + } + } + + protected void addSubclass(IdentifiableTypeMetadata subclass) { + subTypes.add( subclass ); + } + + protected boolean isMappedSuperclass(ClassDetails classDetails) { + return classDetails.getUsage( JpaAnnotations.MAPPED_SUPERCLASS ) != null; + } + + protected boolean isEntity(ClassDetails classDetails) { + return classDetails.getUsage( JpaAnnotations.ENTITY ) != null; + } + + private void walkSubclasses( + ClassDetails classDetails, + AbstractIdentifiableTypeMetadata superType, + AccessType defaultAccessType, + Consumer typeConsumer) { + final ClassDetailsRegistry classDetailsRegistry = getModelContext().getClassDetailsRegistry(); + classDetailsRegistry.forEachDirectSubType( classDetails.getName(), (subTypeManagedClass) -> { + final AbstractIdentifiableTypeMetadata subTypeMetadata; + if ( isEntity( subTypeManagedClass ) ) { + subTypeMetadata = new EntityTypeMetadataImpl( + subTypeManagedClass, + getHierarchy(), + superType, + typeConsumer, + getModelContext() + ); + superType.addSubclass( subTypeMetadata ); + } + else if ( isMappedSuperclass( subTypeManagedClass ) ) { + subTypeMetadata = new MappedSuperclassTypeMetadataImpl( + subTypeManagedClass, + getHierarchy(), + superType, + typeConsumer, + getModelContext() + ); + superType.addSubclass( subTypeMetadata ); + } + else { + subTypeMetadata = superType; + } + + walkSubclasses( subTypeManagedClass, subTypeMetadata, defaultAccessType, typeConsumer ); + } ); + } + + @Override + public EntityHierarchy getHierarchy() { + return hierarchy; + } + + @Override + public boolean isAbstract() { + return getClassDetails().isAbstract(); + } + + @Override + public IdentifiableTypeMetadata getSuperType() { + return superType; + } + + @Override + public boolean hasSubTypes() { + // assume this is called only after its constructor is complete + return !subTypes.isEmpty(); + } + + @Override + public int getNumberOfSubTypes() { + return subTypes.size(); + } + + @Override + public void forEachSubType(Consumer consumer) { + // assume this is called only after its constructor is complete + subTypes.forEach( consumer ); + } + + @Override + public Iterable getSubTypes() { + // assume this is called only after its constructor is complete + return subTypes; + } + + @Override + public AccessType getAccessType() { + return accessType; + } + + protected void collectConversionInfo() { + // we only need to do this on root + } + + protected void collectAttributeOverrides() { + // we only need to do this on root + } + + protected void collectAssociationOverrides() { + // we only need to do this on root + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractManagedTypeMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractManagedTypeMetadata.java new file mode 100644 index 0000000..ae1e0a8 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractManagedTypeMetadata.java @@ -0,0 +1,292 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.Collection; +import java.util.EnumSet; +import java.util.List; +import java.util.Objects; + +import org.hibernate.annotations.Any; +import org.hibernate.annotations.ManyToAny; +import org.hibernate.boot.model.source.spi.AttributePath; +import org.hibernate.boot.model.source.spi.AttributeRole; +import org.hibernate.boot.model.source.spi.NaturalIdMutability; +import org.hibernate.models.internal.IndexedConsumer; +import org.hibernate.models.orm.MultipleAttributeNaturesException; +import org.hibernate.models.orm.spi.AttributeMetadata; +import org.hibernate.models.orm.spi.HibernateAnnotations; +import org.hibernate.models.orm.spi.JpaAnnotations; +import org.hibernate.models.orm.spi.ManagedTypeMetadata; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.MemberDetails; + +import jakarta.persistence.Basic; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.ManyToMany; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; + +import static org.hibernate.models.internal.CollectionHelper.arrayList; +import static org.hibernate.models.orm.internal.OrmModelLogging.ORM_MODEL_LOGGER; + +/** + * Models metadata about a JPA {@linkplain jakarta.persistence.metamodel.ManagedType managed-type}. + * + * @author Hardy Ferentschik + * @author Steve Ebersole + * @author Brett Meyer + */ +public abstract class AbstractManagedTypeMetadata implements ManagedTypeMetadata { + private final ClassDetails classDetails; + private final OrmModelBuildingContext modelContext; + + private final AttributePath attributePathBase; + private final AttributeRole attributeRoleBase; + + /** + * This form is intended for construction of the root of an entity hierarchy + * and its mapped-superclasses + */ + public AbstractManagedTypeMetadata(ClassDetails classDetails, OrmModelBuildingContext modelContext) { + this.classDetails = classDetails; + this.modelContext = modelContext; + this.attributeRoleBase = new AttributeRole( classDetails.getName() ); + this.attributePathBase = new AttributePath(); + } + + /** + * This form is used to create Embedded references + * + * @param classDetails The Embeddable descriptor + * @param attributeRoleBase The base for the roles of attributes created *from* here + * @param attributePathBase The base for the paths of attributes created *from* here + */ + public AbstractManagedTypeMetadata( + ClassDetails classDetails, + AttributeRole attributeRoleBase, + AttributePath attributePathBase, + OrmModelBuildingContext modelContext) { + this.classDetails = classDetails; + this.modelContext = modelContext; + this.attributeRoleBase = attributeRoleBase; + this.attributePathBase = attributePathBase; + } + + public ClassDetails getClassDetails() { + return classDetails; + } + + public OrmModelBuildingContext getModelContext() { + return modelContext; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + AbstractManagedTypeMetadata that = (AbstractManagedTypeMetadata) o; + return Objects.equals( classDetails.getName(), that.classDetails.getName() ); + } + + @Override + public int hashCode() { + return Objects.hash( classDetails ); + } + + @Override + public String toString() { + return "ManagedTypeMetadata(" + classDetails.getName() + ")"; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // attribute handling + + protected abstract List attributeList(); + + @Override + public int getNumberOfAttributes() { + return attributeList().size(); + } + + @Override + public Collection getAttributes() { + return attributeList(); + } + + @Override + public void forEachAttribute(IndexedConsumer consumer) { + for ( int i = 0; i < attributeList().size(); i++ ) { + consumer.accept( i, attributeList().get( i ) ); + } + } + + protected List resolveAttributes() { + final List backingMembers = StandardPersistentAttributeMemberResolver.INSTANCE.resolveAttributesMembers( + classDetails, + getAccessType(), + modelContext + ); + + final List attributeList = arrayList( backingMembers.size() ); + + for ( MemberDetails backingMember : backingMembers ) { + final AttributeMetadata attribute = new AttributeMetadataImpl( + backingMember.resolveAttributeName(), + determineAttributeNature( backingMember ), + backingMember + ); + attributeList.add( attribute ); + } + + return attributeList; + } + + /** + * Determine the attribute's nature - is it a basic mapping, an embeddable, ...? + * + * Also performs some simple validation around multiple natures being indicated + */ + private AttributeMetadata.AttributeNature determineAttributeNature(MemberDetails backingMember) { + final EnumSet natures = EnumSet.noneOf( AttributeMetadata.AttributeNature.class ); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // first, look for explicit nature annotations + + final AnnotationUsage any = backingMember.getUsage( HibernateAnnotations.ANY ); + final AnnotationUsage basic = backingMember.getUsage( JpaAnnotations.BASIC ); + final AnnotationUsage elementCollection = backingMember.getUsage( JpaAnnotations.ELEMENT_COLLECTION ); + final AnnotationUsage embedded = backingMember.getUsage( JpaAnnotations.EMBEDDED ); + final AnnotationUsage embeddedId = backingMember.getUsage( JpaAnnotations.EMBEDDED_ID ); + final AnnotationUsage manyToAny = backingMember.getUsage( HibernateAnnotations.MANY_TO_ANY ); + final AnnotationUsage manyToMany = backingMember.getUsage( JpaAnnotations.MANY_TO_MANY ); + final AnnotationUsage manyToOne = backingMember.getUsage( JpaAnnotations.MANY_TO_ONE ); + final AnnotationUsage oneToMany = backingMember.getUsage( JpaAnnotations.ONE_TO_MANY ); + final AnnotationUsage oneToOne = backingMember.getUsage( JpaAnnotations.ONE_TO_ONE ); + + if ( basic != null ) { + natures.add( AttributeMetadata.AttributeNature.BASIC ); + } + + if ( embedded != null + || embeddedId != null + || ( backingMember.getType() != null && backingMember.getType().getUsage( JpaAnnotations.EMBEDDABLE ) != null ) ) { + natures.add( AttributeMetadata.AttributeNature.EMBEDDED ); + } + + if ( any != null ) { + natures.add( AttributeMetadata.AttributeNature.ANY ); + } + + if ( oneToOne != null + || manyToOne != null ) { + natures.add( AttributeMetadata.AttributeNature.TO_ONE ); + } + + final boolean plural = oneToMany != null + || manyToMany != null + || elementCollection != null + || manyToAny != null; + if ( plural ) { + natures.add( AttributeMetadata.AttributeNature.PLURAL ); + } + + // look at annotations that imply a nature + // NOTE : these could apply to the element or index of collection, so + // only do these if it is not a collection + + if ( !plural ) { + // first implicit basic nature + if ( backingMember.getUsage( JpaAnnotations.TEMPORAL ) != null + || backingMember.getUsage( JpaAnnotations.LOB ) != null + || backingMember.getUsage( JpaAnnotations.ENUMERATED ) != null + || backingMember.getUsage( JpaAnnotations.CONVERT ) != null + || backingMember.getUsage( JpaAnnotations.VERSION ) != null + || backingMember.getUsage( HibernateAnnotations.GENERATED ) != null + || backingMember.getUsage( HibernateAnnotations.NATIONALIZED ) != null + || backingMember.getUsage( HibernateAnnotations.TZ_COLUMN ) != null + || backingMember.getUsage( HibernateAnnotations.TZ_STORAGE ) != null + || backingMember.getUsage( HibernateAnnotations.TYPE ) != null + || backingMember.getUsage( HibernateAnnotations.TENANT_ID ) != null + || backingMember.getUsage( HibernateAnnotations.JAVA_TYPE ) != null + || backingMember.getUsage( HibernateAnnotations.JDBC_TYPE_CODE ) != null + || backingMember.getUsage( HibernateAnnotations.JDBC_TYPE ) != null ) { + natures.add( AttributeMetadata.AttributeNature.BASIC ); + } + + // then embedded + if ( backingMember.getUsage( HibernateAnnotations.EMBEDDABLE_INSTANTIATOR ) != null + || backingMember.getUsage( HibernateAnnotations.COMPOSITE_TYPE ) != null ) { + natures.add( AttributeMetadata.AttributeNature.EMBEDDED ); + } + + // and any + if ( backingMember.getUsage( HibernateAnnotations.ANY_DISCRIMINATOR ) != null + || backingMember.getUsage( HibernateAnnotations.ANY_DISCRIMINATOR_VALUE ) != null + || backingMember.getUsage( HibernateAnnotations.ANY_DISCRIMINATOR_VALUES ) != null + || backingMember.getUsage( HibernateAnnotations.ANY_KEY_JAVA_TYPE ) != null + || backingMember.getUsage( HibernateAnnotations.ANY_KEY_JAVA_CLASS ) != null + || backingMember.getUsage( HibernateAnnotations.ANY_KEY_JDBC_TYPE ) != null + || backingMember.getUsage( HibernateAnnotations.ANY_KEY_JDBC_TYPE_CODE ) != null ) { + natures.add( AttributeMetadata.AttributeNature.ANY ); + } + } + + int size = natures.size(); + switch ( size ) { + case 0: { + ORM_MODEL_LOGGER.debugf( + "Implicitly interpreting attribute `%s` as BASIC", + backingMember.resolveAttributeName() + ); + return AttributeMetadata.AttributeNature.BASIC; + } + case 1: { + return natures.iterator().next(); + } + default: { + throw new MultipleAttributeNaturesException( backingMember.resolveAttributeName(), natures ); + } + } + } + +// @Override +// public List> findAnnotations(AnnotationDescriptor type) { +// return classDetails.getAnnotations( type ); +// } +// +// @Override +// public void forEachAnnotation(AnnotationDescriptor type, Consumer> consumer) { +// classDetails.forEachAnnotation( type, consumer ); +// } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Stuff affecting attributes built from this managed type. + + public boolean canAttributesBeInsertable() { + return true; + } + + public boolean canAttributesBeUpdatable() { + return true; + } + + public NaturalIdMutability getContainerNaturalIdMutability() { + return NaturalIdMutability.NOT_NATURAL_ID; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractPersistentAttributeMemberResolver.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractPersistentAttributeMemberResolver.java new file mode 100644 index 0000000..4f35bf3 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AbstractPersistentAttributeMemberResolver.java @@ -0,0 +1,102 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.hibernate.models.orm.spi.JpaAnnotations; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.orm.spi.PersistentAttributeMemberResolver; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.FieldDetails; +import org.hibernate.models.source.spi.MemberDetails; +import org.hibernate.models.source.spi.MethodDetails; + +import jakarta.persistence.AccessType; + +/** + * "Template" support for writing PersistentAttributeMemberResolver + * implementations. + * + * @author Steve Ebersole + */ +public abstract class AbstractPersistentAttributeMemberResolver implements PersistentAttributeMemberResolver { + + /** + * This is the call that represents the bulk of the work needed to resolve + * the persistent attribute members. It is the strategy specific portion + * for sure. + *

+ * The expectation is to + * Here is the call that most likely changes per strategy. This occurs + * immediately after we have determined all the fields and methods marked as + * transient. The expectation is to + * + * @param transientFieldChecker Check whether a field is annotated as @Transient + * @param transientMethodChecker Check whether a method is annotated as @Transient + * @param classDetails The Jandex ClassInfo describing the type for which to resolve members + * @param classLevelAccessType The AccessType determined for the class default + * @param processingContext The local context + */ + protected abstract List resolveAttributesMembers( + Function transientFieldChecker, + Function transientMethodChecker, + ClassDetails classDetails, + AccessType classLevelAccessType, + OrmModelBuildingContext processingContext); + + @Override + public List resolveAttributesMembers( + ClassDetails classDetails, + AccessType classLevelAccessType, + OrmModelBuildingContext processingContext) { + + final Set transientFields = new HashSet<>(); + final Set transientMethods = new HashSet<>(); + collectMembersMarkedTransient( + transientFields::add, + transientMethods::add, + classDetails, + processingContext + ); + + return resolveAttributesMembers( + transientFields::contains, + transientMethods::contains, + classDetails, + classLevelAccessType, + processingContext + ); + } + + protected void collectMembersMarkedTransient( + final Consumer transientFieldConsumer, + final Consumer transientMethodConsumer, + ClassDetails classDetails, + @SuppressWarnings("unused") OrmModelBuildingContext processingContext) { + final List fields = classDetails.getFields(); + for ( int i = 0; i < fields.size(); i++ ) { + final FieldDetails fieldDetails = fields.get( i ); + if ( fieldDetails.getUsage( JpaAnnotations.TRANSIENT ) != null ) { + transientFieldConsumer.accept( fieldDetails ); + } + } + + final List methods = classDetails.getMethods(); + for ( int i = 0; i < methods.size(); i++ ) { + final MethodDetails methodDetails = methods.get( i ); + if ( methodDetails.getUsage( JpaAnnotations.TRANSIENT ) != null ) { + transientMethodConsumer.accept( methodDetails ); + } + } + } + +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AttributeMetadataImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AttributeMetadataImpl.java new file mode 100644 index 0000000..2ebc5d5 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/AttributeMetadataImpl.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.models.orm.spi.AttributeMetadata; +import org.hibernate.models.source.spi.MemberDetails; + +/** + * @author Steve Ebersole + */ +public class AttributeMetadataImpl implements AttributeMetadata { + private final String name; + private final AttributeNature nature; + private final MemberDetails member; + + public AttributeMetadataImpl(String name, AttributeNature nature, MemberDetails member) { + this.name = name; + this.nature = nature; + this.member = member; + } + + @Override + public String getName() { + return name; + } + + @Override + public AttributeNature getNature() { + return nature; + } + + @Override + public MemberDetails getMember() { + return member; + } + + @Override + public String toString() { + return "AttributeMetadata(`" + name + "`)"; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CollectionTypeRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CollectionTypeRegistration.java new file mode 100644 index 0000000..9bed8a2 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CollectionTypeRegistration.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.Map; + +import org.hibernate.metamodel.CollectionClassification; +import org.hibernate.models.source.spi.ClassDetails; + +/** + * @author Steve Ebersole + */ +public class CollectionTypeRegistration { + private final CollectionClassification classification; + private final ClassDetails userTypeClass; + private final Map parameterMap; + + public CollectionTypeRegistration( + CollectionClassification classification, + ClassDetails userTypeClass, + Map parameterMap) { + this.classification = classification; + this.userTypeClass = userTypeClass; + this.parameterMap = parameterMap; + } + + public CollectionClassification getClassification() { + return classification; + } + + public ClassDetails getUserTypeClass() { + return userTypeClass; + } + + public Map getParameterMap() { + return parameterMap; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CompositeUserTypeRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CompositeUserTypeRegistration.java new file mode 100644 index 0000000..16dfcc9 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/CompositeUserTypeRegistration.java @@ -0,0 +1,30 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.models.source.spi.ClassDetails; + +/** + * @author Steve Ebersole + */ +public class CompositeUserTypeRegistration { + private final ClassDetails embeddableClass; + private final ClassDetails userTypeClass; + + public CompositeUserTypeRegistration(ClassDetails embeddableClass, ClassDetails userTypeClass) { + this.embeddableClass = embeddableClass; + this.userTypeClass = userTypeClass; + } + + public ClassDetails getEmbeddableClass() { + return embeddableClass; + } + + public ClassDetails getUserTypeClass() { + return userTypeClass; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ConversionRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ConversionRegistration.java new file mode 100644 index 0000000..bbbefd0 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ConversionRegistration.java @@ -0,0 +1,168 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ + +package org.hibernate.models.orm.internal; + +import java.util.List; +import java.util.Objects; + +import org.hibernate.boot.internal.ClassmateContext; +import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorBypassedImpl; +import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorStandardImpl; +import org.hibernate.boot.model.convert.internal.ConverterHelper; +import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor; +import org.hibernate.boot.model.convert.spi.ConverterDescriptor; +import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext; +import org.hibernate.boot.model.convert.spi.RegisteredConversion; +import org.hibernate.models.Copied; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.resource.beans.spi.ManagedBean; +import org.hibernate.type.descriptor.converter.internal.JpaAttributeConverterImpl; +import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter; +import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry; +import org.hibernate.type.spi.TypeConfiguration; + +import com.fasterxml.classmate.ResolvedType; +import jakarta.persistence.AttributeConverter; + +/** + * A registered conversion. + * + * @see org.hibernate.annotations.ConverterRegistration + * + * @author Steve Ebersole + */ +@Copied(RegisteredConversion.class) +public class ConversionRegistration { + private final ClassDetails explicitDomainType; + private final ClassDetails converterType; + private final boolean autoApply; + + public ConversionRegistration(ClassDetails explicitDomainType, ClassDetails converterType, boolean autoApply) { + assert converterType != null; + + this.explicitDomainType = explicitDomainType; + this.converterType = converterType; + this.autoApply = autoApply; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null || getClass() != o.getClass() ) { + return false; + } + ConversionRegistration that = (ConversionRegistration) o; + return autoApply == that.autoApply + && Objects.equals( explicitDomainType, that.explicitDomainType ) + && converterType.equals( that.converterType ); + } + + @Override + public int hashCode() { + return Objects.hash( explicitDomainType, converterType ); + } + + public ClassDetails getExplicitDomainType() { + return explicitDomainType; + } + + public ClassDetails getConverterType() { + return converterType; + } + + public boolean isAutoApply() { + return autoApply; + } + + public ConverterDescriptor makeConverterDescriptor(ClassmateContext classmateContext) { + final Class explicitDomainType = this.explicitDomainType.toJavaClass(); + final Class> converterType = this.converterType.toJavaClass(); + + final List resolvedParamTypes = ConverterHelper.resolveConverterClassParamTypes( + converterType, + classmateContext + ); + final ResolvedType relationalType = resolvedParamTypes.get( 1 ); + final ResolvedType domainTypeToMatch; + if ( !void.class.equals( explicitDomainType ) ) { + domainTypeToMatch = classmateContext.getTypeResolver().resolve( explicitDomainType ); + } + else { + domainTypeToMatch = resolvedParamTypes.get( 0 ); + } + + return new ConverterDescriptorImpl( converterType, domainTypeToMatch, relationalType, autoApply ); + } + + private static class ConverterDescriptorImpl implements ConverterDescriptor { + private final Class> converterType; + private final ResolvedType domainTypeToMatch; + private final ResolvedType relationalType; + private final boolean autoApply; + + private final AutoApplicableConverterDescriptor autoApplyDescriptor; + + public ConverterDescriptorImpl( + Class> converterType, + ResolvedType domainTypeToMatch, + ResolvedType relationalType, + boolean autoApply) { + this.converterType = converterType; + this.domainTypeToMatch = domainTypeToMatch; + this.relationalType = relationalType; + this.autoApply = autoApply; + + this.autoApplyDescriptor = autoApply + ? new AutoApplicableConverterDescriptorStandardImpl( this ) + : AutoApplicableConverterDescriptorBypassedImpl.INSTANCE; + } + + @Override + public Class> getAttributeConverterClass() { + return converterType; + } + + @Override + public ResolvedType getDomainValueResolvedType() { + return domainTypeToMatch; + } + + @Override + public ResolvedType getRelationalValueResolvedType() { + return relationalType; + } + + @Override + public AutoApplicableConverterDescriptor getAutoApplyDescriptor() { + return autoApplyDescriptor; + } + + @SuppressWarnings("unchecked") + @Override + public JpaAttributeConverter createJpaAttributeConverter(JpaAttributeConverterCreationContext context) { + final ManagedBean> converterBean = context + .getManagedBeanRegistry() + .getBean( converterType ); + + final TypeConfiguration typeConfiguration = context.getTypeConfiguration(); + final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry(); + javaTypeRegistry.resolveDescriptor( domainTypeToMatch.getErasedType() ); + + //noinspection rawtypes + return new JpaAttributeConverterImpl( + converterBean, + javaTypeRegistry.getDescriptor( converterBean.getBeanClass() ), + javaTypeRegistry.resolveDescriptor( domainTypeToMatch.getErasedType() ), + javaTypeRegistry.resolveDescriptor( relationalType.getErasedType() ) + ); + } + } + +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EmbeddableInstantiatorRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EmbeddableInstantiatorRegistration.java new file mode 100644 index 0000000..b7386de --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EmbeddableInstantiatorRegistration.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.models.source.spi.ClassDetails; + +/** + * @see org.hibernate.annotations.EmbeddableInstantiatorRegistration + * + * @author Steve Ebersole + */ +public class EmbeddableInstantiatorRegistration { + private final ClassDetails embeddableClass; + private final ClassDetails instantiator; + + public EmbeddableInstantiatorRegistration(ClassDetails embeddableClass, ClassDetails instantiator) { + this.embeddableClass = embeddableClass; + this.instantiator = instantiator; + } + + public ClassDetails getEmbeddableClass() { + return embeddableClass; + } + + public ClassDetails getInstantiator() { + return instantiator; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyBuilder.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyBuilder.java new file mode 100644 index 0000000..9e53c00 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyBuilder.java @@ -0,0 +1,211 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; + +import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.models.orm.AccessTypeDeterminationException; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.IdentifiableTypeMetadata; +import org.hibernate.models.orm.spi.JpaAnnotations; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.AnnotationTarget; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.ClassDetailsRegistry; +import org.hibernate.models.source.spi.FieldDetails; +import org.hibernate.models.source.spi.MethodDetails; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; + +import static org.hibernate.models.source.internal.SourceModelLogging.SOURCE_MODEL_LOGGER; + +/** + * Builds {@link EntityHierarchy} references from + * {@linkplain ClassDetailsRegistry#forEachClassDetails managed classes}. + * + * @author Steve Ebersole + */ +public class EntityHierarchyBuilder { + + /** + * Pre-processes the annotated entities from the index and create a set of entity hierarchies which can be bound + * to the metamodel. + * + * @param typeConsumer Callback for any identifiable-type metadata references + * @param buildingContext The binding context, giving access to needed services and information + * + * @return a set of {@code EntityHierarchySource} instances. + */ + public static Set createEntityHierarchies( + Set rootEntities, + Consumer typeConsumer, + OrmModelBuildingContext buildingContext) { + return new EntityHierarchyBuilder( buildingContext ).process( rootEntities, typeConsumer ); + } + + /** + * Pre-processes the annotated entities from the index and create a set of entity hierarchies which can be bound + * to the metamodel. + * + * @param typeConsumer Callback for any identifiable-type metadata references + * @param buildingContext The binding context, giving access to needed services and information + * + * @return a set of {@code EntityHierarchySource} instances. + */ + public static Set createEntityHierarchies( + Consumer typeConsumer, + OrmModelBuildingContext buildingContext) { + return createEntityHierarchies( + collectRootEntityTypes( buildingContext.getClassDetailsRegistry() ), + typeConsumer, + buildingContext + ); + } + + private final OrmModelBuildingContext modelContext; + + public EntityHierarchyBuilder(OrmModelBuildingContext modelContext) { + this.modelContext = modelContext; + } + + private Set process( + Set rootEntities, + Consumer typeConsumer) { + final Set hierarchies = CollectionHelper.setOfSize( rootEntities.size() ); + + rootEntities.forEach( (rootEntity) -> { + final AccessType defaultAccessType = determineDefaultAccessTypeForHierarchy( rootEntity ); + hierarchies.add( new EntityHierarchyImpl( + rootEntity, + defaultAccessType, + typeConsumer, + modelContext + ) ); + } ); + + return hierarchies; + } + + private AccessType determineDefaultAccessTypeForHierarchy(ClassDetails rootEntityType) { + assert rootEntityType != null; + + ClassDetails current = rootEntityType; + while ( current != null ) { + // look for `@Access` on the class + final AnnotationUsage accessAnnotation = current.getUsage( JpaAnnotations.ACCESS ); + if ( accessAnnotation != null ) { + return accessAnnotation.getAttributeValue( "value" ); + } + + // look for `@Id` or `@EmbeddedId` + final AnnotationTarget idMember = determineIdMember( current ); + if ( idMember != null ) { + switch ( idMember.getKind() ) { + case FIELD: { + return AccessType.FIELD; + } + case METHOD: { + return AccessType.PROPERTY; + } + default: { + throw new IllegalStateException( "@Id / @EmbeddedId found on target other than field or method : " + idMember ); + } + } + } + + current = current.getSuperType(); + } + + // 2.3.1 Default Access Type + // It is an error if a default access type cannot be determined and an access type is not explicitly specified + // by means of annotations or the XML descriptor. + + throw new AccessTypeDeterminationException( rootEntityType ); + } + + private AnnotationTarget determineIdMember(ClassDetails current) { + final List methods = current.getMethods(); + for ( int i = 0; i < methods.size(); i++ ) { + final MethodDetails methodDetails = methods.get( i ); + if ( methodDetails.getUsage( JpaAnnotations.ID ) != null + || methodDetails.getUsage( JpaAnnotations.EMBEDDED_ID ) != null ) { + return methodDetails; + } + } + + final List fields = current.getFields(); + for ( int i = 0; i < fields.size(); i++ ) { + final FieldDetails fieldDetails = fields.get( i ); + if ( fieldDetails.getUsage( JpaAnnotations.ID ) != null + || fieldDetails.getUsage( JpaAnnotations.EMBEDDED_ID ) != null ) { + return fieldDetails; + } + } + + return null; + } + + private Set collectRootEntityTypes() { + return collectRootEntityTypes( modelContext.getClassDetailsRegistry() ); + } + + private static Set collectRootEntityTypes(ClassDetailsRegistry classDetailsRegistry) { + final Set collectedTypes = new HashSet<>(); + + classDetailsRegistry.forEachClassDetails( (managedType) -> { + if ( managedType.getUsage( JpaAnnotations.ENTITY ) != null + && isRoot( managedType ) ) { + collectedTypes.add( managedType ); + } + } ); + + return collectedTypes; + } + + public static boolean isRoot(ClassDetails classInfo) { + // perform a series of opt-out checks against the super-type hierarchy + + // an entity is considered a root of the hierarchy if: + // 1) it has no super-types + // 2) its super types contain no entities (MappedSuperclasses are allowed) + + if ( classInfo.getSuperType() == null ) { + return true; + } + + ClassDetails current = classInfo.getSuperType(); + while ( current != null ) { + if ( current.getUsage( JpaAnnotations.ENTITY ) != null ) { + // a super type has `@Entity`, cannot be root + return false; + } + current = current.getSuperType(); + } + + // if we hit no opt-outs we have a root + return true; + } + + + /** + * Used in tests + */ + public static Set createEntityHierarchies(OrmModelBuildingContext processingContext) { + return new EntityHierarchyBuilder( processingContext ).process( + collectRootEntityTypes( processingContext.getClassDetailsRegistry() ), + EntityHierarchyBuilder::ignore + ); + } + + private static void ignore(IdentifiableTypeMetadata it) {} +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyImpl.java new file mode 100644 index 0000000..c98e95d --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityHierarchyImpl.java @@ -0,0 +1,116 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.Locale; +import java.util.function.Consumer; + +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.EntityTypeMetadata; +import org.hibernate.models.orm.spi.IdentifiableTypeMetadata; +import org.hibernate.models.orm.spi.JpaAnnotations; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.ClassDetails; + +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; + +import static org.hibernate.models.orm.internal.OrmModelLogging.ORM_MODEL_LOGGER; + +/** + * @author Steve Ebersole + */ +public class EntityHierarchyImpl implements EntityHierarchy { + private final EntityTypeMetadata rootEntityTypeMetadata; + + private final InheritanceType inheritanceType; + + // todo (models) : version? row-id? tenant-id? others? + + public EntityHierarchyImpl( + ClassDetails rootEntityClassDetails, + jakarta.persistence.AccessType defaultAccessType, + Consumer typeConsumer, + OrmModelBuildingContext processingContext) { + this.rootEntityTypeMetadata = new EntityTypeMetadataImpl( + rootEntityClassDetails, + this, + defaultAccessType, + typeConsumer, + processingContext + ); + + this.inheritanceType = determineInheritanceType( rootEntityTypeMetadata ); + } + + private InheritanceType determineInheritanceType(EntityTypeMetadata root) { + if ( ORM_MODEL_LOGGER.isDebugEnabled() ) { + // Validate that there is no @Inheritance annotation further down the hierarchy + ensureNoInheritanceAnnotationsOnSubclasses( root ); + } + + IdentifiableTypeMetadata current = root; + while ( current != null ) { + final InheritanceType inheritanceType = getLocallyDefinedInheritanceType( current.getClassDetails() ); + if ( inheritanceType != null ) { + return inheritanceType; + } + + current = current.getSuperType(); + } + + return InheritanceType.SINGLE_TABLE; + } + + /** + * Find the InheritanceType from the locally defined {@link Inheritance} annotation, + * if one. Returns {@code null} if {@link Inheritance} is not locally defined. + * + * @apiNote Used when building the {@link EntityHierarchy} + */ + private static InheritanceType getLocallyDefinedInheritanceType(ClassDetails managedClass) { + final AnnotationUsage localAnnotation = managedClass.getUsage( JpaAnnotations.INHERITANCE ); + if ( localAnnotation == null ) { + return null; + } + + return localAnnotation.getAttributeValue( "strategy" ); + } + + private void ensureNoInheritanceAnnotationsOnSubclasses(IdentifiableTypeMetadata type) { + type.forEachSubType( (subType) -> { + if ( getLocallyDefinedInheritanceType( subType.getClassDetails() ) != null ) { + ORM_MODEL_LOGGER.debugf( + "@javax.persistence.Inheritance was specified on non-root entity [%s]; ignoring...", + type.getClassDetails().getName() + ); + } + ensureNoInheritanceAnnotationsOnSubclasses( subType ); + } ); + } + + @Override + public EntityTypeMetadata getRoot() { + return rootEntityTypeMetadata; + } + + @Override + public InheritanceType getInheritanceType() { + return inheritanceType; + } + + @Override + public String toString() { + return String.format( + Locale.ROOT, + "EntityHierarchyImpl(`%s` (%s))", + rootEntityTypeMetadata.getEntityName(), + inheritanceType.name() + ); + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityListenerRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityListenerRegistration.java new file mode 100644 index 0000000..ff0b7ed --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityListenerRegistration.java @@ -0,0 +1,148 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.boot.jaxb.mapping.JaxbEntityListener; +import org.hibernate.internal.util.MutableObject; +import org.hibernate.models.ModelsException; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.ClassDetailsRegistry; +import org.hibernate.models.source.spi.MethodDetails; + +/** + * @author Steve Ebersole + */ +public class EntityListenerRegistration { + + private final ClassDetails listenerClass; + + private final MethodDetails prePersistMethod; + private final MethodDetails postPersistMethod; + + private final MethodDetails preRemoveMethod; + private final MethodDetails postRemoveMethod; + + private final MethodDetails preUpdateMethod; + private final MethodDetails postUpdateMethod; + + private final MethodDetails postLoadMethod; + + public EntityListenerRegistration( + ClassDetails listenerClass, + MethodDetails prePersistMethod, + MethodDetails postPersistMethod, + MethodDetails preRemoveMethod, + MethodDetails postRemoveMethod, + MethodDetails preUpdateMethod, + MethodDetails postUpdateMethod, + MethodDetails postLoadMethod) { + this.listenerClass = listenerClass; + this.prePersistMethod = prePersistMethod; + this.postPersistMethod = postPersistMethod; + this.preRemoveMethod = preRemoveMethod; + this.postRemoveMethod = postRemoveMethod; + this.preUpdateMethod = preUpdateMethod; + this.postUpdateMethod = postUpdateMethod; + this.postLoadMethod = postLoadMethod; + + if ( prePersistMethod == null + && postPersistMethod == null + && preRemoveMethod == null + && postRemoveMethod == null + && preUpdateMethod == null + && postUpdateMethod == null + && postLoadMethod == null ) { + throw new ModelsException( "Mapping for entity-listener specified no callback methods - " + listenerClass.getClassName() ); + } + } + + public ClassDetails getListenerClass() { + return listenerClass; + } + + public MethodDetails getPrePersistMethod() { + return prePersistMethod; + } + + public MethodDetails getPostPersistMethod() { + return postPersistMethod; + } + + public MethodDetails getPreRemoveMethod() { + return preRemoveMethod; + } + + public MethodDetails getPostRemoveMethod() { + return postRemoveMethod; + } + + public MethodDetails getPreUpdateMethod() { + return preUpdateMethod; + } + + public MethodDetails getPostUpdateMethod() { + return postUpdateMethod; + } + + public MethodDetails getPostLoadMethod() { + return postLoadMethod; + } + public static EntityListenerRegistration from(JaxbEntityListener jaxbMapping, ClassDetailsRegistry classDetailsRegistry) { + final ClassDetails listenerClassDetails = classDetailsRegistry.resolveClassDetails( jaxbMapping.getClazz() ); + final MutableObject prePersistMethod = new MutableObject<>(); + final MutableObject postPersistMethod = new MutableObject<>(); + final MutableObject preRemoveMethod = new MutableObject<>(); + final MutableObject postRemoveMethod = new MutableObject<>(); + final MutableObject preUpdateMethod = new MutableObject<>(); + final MutableObject postUpdateMethod = new MutableObject<>(); + final MutableObject postLoadMethod = new MutableObject<>(); + + listenerClassDetails.forEachMethod( (index, methodDetails) -> { + // todo : make this sensitive to method arguments once we have MethodDetails tracking arguments + // for now, just match name + if ( jaxbMapping.getPrePersist() != null + && methodDetails.getName().equals( jaxbMapping.getPrePersist().getMethodName() ) ) { + prePersistMethod.set( methodDetails ); + } + else if ( jaxbMapping.getPostPersist().getMethodName() != null + && methodDetails.getName().equals( jaxbMapping.getPostPersist().getMethodName() ) ) { + postPersistMethod.set( methodDetails ); + } + else if ( jaxbMapping.getPreRemove() != null + && methodDetails.getName().equals( jaxbMapping.getPreRemove().getMethodName() ) ) { + preRemoveMethod.set( methodDetails ); + } + else if ( jaxbMapping.getPostRemove() != null + && methodDetails.getName().equals( jaxbMapping.getPostRemove().getMethodName() ) ) { + postRemoveMethod.set( methodDetails ); + } + else if ( jaxbMapping.getPreUpdate() != null + && methodDetails.getName().equals( jaxbMapping.getPreUpdate().getMethodName() ) ) { + preUpdateMethod.set( methodDetails ); + } + else if ( jaxbMapping.getPostUpdate() != null + && methodDetails.getName().equals( jaxbMapping.getPostUpdate().getMethodName() ) ) { + postUpdateMethod.set( methodDetails ); + } + else if ( jaxbMapping.getPostLoad() != null + && methodDetails.getName().equals( jaxbMapping.getPostLoad().getMethodName() ) ) { + postLoadMethod.set( methodDetails ); + } + } ); + + return new EntityListenerRegistration( + listenerClassDetails, + prePersistMethod.get(), + postPersistMethod.get(), + preRemoveMethod.get(), + postRemoveMethod.get(), + preUpdateMethod.get(), + postUpdateMethod.get(), + postLoadMethod.get() + ); + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityTypeMetadataImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityTypeMetadataImpl.java new file mode 100644 index 0000000..8aa1084 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/EntityTypeMetadataImpl.java @@ -0,0 +1,295 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.boot.model.naming.EntityNaming; +import org.hibernate.models.internal.StringHelper; +import org.hibernate.models.orm.spi.AttributeMetadata; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.EntityTypeMetadata; +import org.hibernate.models.orm.spi.IdentifiableTypeMetadata; +import org.hibernate.models.orm.spi.JpaAnnotations; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.ClassDetails; + +import jakarta.persistence.AccessType; +import jakarta.persistence.Entity; + +import static org.hibernate.internal.util.StringHelper.unqualify; + +/** + * @author Steve Ebersole + */ +public class EntityTypeMetadataImpl + extends AbstractIdentifiableTypeMetadata + implements EntityTypeMetadata, EntityNaming { + private final EntityHierarchy hierarchy; + + private final String entityName; + private final String jpaEntityName; + + private final List attributeList; + +// private final String proxy; +// +// private final String customLoaderQueryName; +// private final String[] synchronizedTableNames; +// private final int batchSize; +// private final boolean isDynamicInsert; +// private final boolean isDynamicUpdate; +// private final boolean isSelectBeforeUpdate; +// private final CustomSql customInsert; +// private final CustomSql customUpdate; +// private final CustomSql customDelete; +// private final String discriminatorMatchValue; +// private final boolean isLazy; + + /** + * This form is intended for construction of root Entity. + */ + public EntityTypeMetadataImpl( + ClassDetails classDetails, + EntityHierarchy hierarchy, + AccessType defaultAccessType, + Consumer typeConsumer, + OrmModelBuildingContext modelContext) { + super( classDetails, hierarchy, true, defaultAccessType, typeConsumer, modelContext ); + + this.hierarchy = hierarchy; + + // NOTE: there is no annotation for `entity-name`; it comes exclusively from + // XML mappings. by default, the `entityName` is simply the entity class name. + // all of which `ClassDetails#getName` already handles for us + this.entityName = getClassDetails().getName(); + + final AnnotationUsage entityAnnotation = classDetails.getUsage( JpaAnnotations.ENTITY ); + this.jpaEntityName = determineJpaEntityName( entityAnnotation, entityName ); + +// this.synchronizedTableNames = determineSynchronizedTableNames(); +// this.batchSize = determineBatchSize(); +// +// this.customLoaderQueryName = determineCustomLoader(); +// this.customInsert = extractCustomSql( classDetails.getAnnotation( HibernateAnnotations.SQL_INSERT ) ); +// this.customUpdate = extractCustomSql( classDetails.getAnnotation( HibernateAnnotations.SQL_UPDATE ) ); +// this.customDelete = extractCustomSql( classDetails.getAnnotation( HibernateAnnotations.SQL_DELETE ) ); +// +// this.isDynamicInsert = decodeDynamicInsert(); +// this.isDynamicUpdate = decodeDynamicUpdate(); +// this.isSelectBeforeUpdate = decodeSelectBeforeUpdate(); +// +// // Proxy generation +// final AnnotationUsage proxyAnnotation = classDetails.getAnnotation( HibernateAnnotations.PROXY ); +// if ( proxyAnnotation != null ) { +// final AnnotationUsage.AnnotationAttributeValue lazyValue = proxyAnnotation.getAttributeValue( "lazy" ); +// if ( lazyValue != null ) { +// this.isLazy = lazyValue.asBoolean(); +// } +// else { +// this.isLazy = true; +// } +// +// if ( this.isLazy ) { +// final AnnotationUsage.AnnotationAttributeValue proxyClassValue = proxyAnnotation.getAttributeValue( "proxyClass" ); +// if ( proxyClassValue != null && !proxyClassValue.isDefaultValue() ) { +// this.proxy = proxyClassValue.asString(); +// } +// else { +// this.proxy = null; +// } +// } +// else { +// this.proxy = null; +// } +// } +// else { +// // defaults are that it is lazy and that the class itself is the proxy class +// this.isLazy = true; +// this.proxy = getName(); +// } +// +// final AnnotationUsage discriminatorValueAnnotation = classDetails.getAnnotation( JpaAnnotations.DISCRIMINATOR_VALUE ); +// if ( discriminatorValueAnnotation != null ) { +// final AnnotationUsage.AnnotationAttributeValue discriminatorValueValue = discriminatorValueAnnotation.getValueAttributeValue(); +// this.discriminatorMatchValue = discriminatorValueValue.asString(); +// } +// else { +// this.discriminatorMatchValue = null; +// } + + this.attributeList = resolveAttributes(); + } + + /** + * This form is intended for construction of non-root Entity. + */ + public EntityTypeMetadataImpl( + ClassDetails classDetails, + EntityHierarchy hierarchy, + AbstractIdentifiableTypeMetadata superType, + Consumer typeConsumer, + OrmModelBuildingContext modelContext) { + super( classDetails, hierarchy, superType, typeConsumer, modelContext ); + this.hierarchy = hierarchy; + + // NOTE: this is no annotation for `entity-name`. it comes exclusively from + // XML mappings. by default, the `entityName` is simply the entity class name. + // all of which `ClassDetails#getName` already handles for us + this.entityName = getClassDetails().getName(); + + final AnnotationUsage entityAnnotation = classDetails.getUsage( JpaAnnotations.ENTITY ); + this.jpaEntityName = determineJpaEntityName( entityAnnotation, entityName ); + + this.attributeList = resolveAttributes(); + } + + private String determineJpaEntityName(AnnotationUsage entityAnnotation, String entityName) { + final String name = entityAnnotation.getAttributeValue( "name" ); + if ( StringHelper.isNotEmpty( name ) ) { + return name; + } + return unqualify( entityName ); + } + + @Override + protected List attributeList() { + return attributeList; + } + +// private String determineCustomLoader() { +// final AnnotationUsage loaderAnnotation = getManagedClass().getAnnotation( HibernateAnnotations.LOADER ); +// if ( loaderAnnotation != null ) { +// final AnnotationUsage.AnnotationAttributeValue namedQueryValue = loaderAnnotation.getAttributeValue( "namedQuery" ); +// return namedQueryValue.asString(); +// } +// return null; +// } +// +// private String[] determineSynchronizedTableNames() { +// final AnnotationUsage synchronizeAnnotation = getManagedClass().getAnnotation( HibernateAnnotations.SYNCHRONIZE ); +// if ( synchronizeAnnotation != null ) { +// return synchronizeAnnotation.getValueAttributeValue().getValue(); +// } +// return StringHelper.EMPTY_STRINGS; +// } +// +// private int determineBatchSize() { +// final AnnotationUsage batchSizeAnnotation = getManagedClass().getAnnotation( HibernateAnnotations.BATCH_SIZE ); +// if ( batchSizeAnnotation != null ) { +// return batchSizeAnnotation.getAttributeValue( "size" ).asInt(); +// } +// return -1; +// } +// +// private boolean decodeDynamicInsert() { +// final AnnotationUsage dynamicInsertAnnotation = getManagedClass().getAnnotation( HibernateAnnotations.DYNAMIC_INSERT ); +// if ( dynamicInsertAnnotation == null ) { +// return false; +// } +// +// return dynamicInsertAnnotation.getValueAttributeValue().asBoolean(); +// } +// +// private boolean decodeDynamicUpdate() { +// final AnnotationUsage dynamicUpdateAnnotation = getManagedClass().getAnnotation( HibernateAnnotations.DYNAMIC_UPDATE ); +// if ( dynamicUpdateAnnotation == null ) { +// return false; +// } +// return dynamicUpdateAnnotation.getValueAttributeValue().asBoolean(); +// } +// +// private boolean decodeSelectBeforeUpdate() { +// final AnnotationUsage selectBeforeUpdateAnnotation = getManagedClass().getAnnotation( HibernateAnnotations.SELECT_BEFORE_UPDATE ); +// if ( selectBeforeUpdateAnnotation == null ) { +// return false; +// } +// return selectBeforeUpdateAnnotation.getValueAttributeValue().asBoolean(); +// } + + @Override + public EntityHierarchy getHierarchy() { + return hierarchy; + } + +// @Override +// public EntityNaming getEntityNaming() { +// return this; +// } + + @Override + public String getEntityName() { + return entityName; + } + + @Override + public String getJpaEntityName() { + return jpaEntityName; + } + + @Override + public String getClassName() { + return getClassDetails().getClassName(); + } + +// @Override +// public String getCustomLoaderQueryName() { +// return customLoaderQueryName; +// } +// +// public String[] getSynchronizedTableNames() { +// return synchronizedTableNames; +// } +// +// public int getBatchSize() { +// return batchSize; +// } +// +// public boolean isDynamicInsert() { +// return isDynamicInsert; +// } +// +// public boolean isDynamicUpdate() { +// return isDynamicUpdate; +// } +// +// public boolean isSelectBeforeUpdate() { +// return isSelectBeforeUpdate; +// } +// +// public CustomSql getCustomInsert() { +// return customInsert; +// } +// +// public CustomSql getCustomUpdate() { +// return customUpdate; +// } +// +// public CustomSql getCustomDelete() { +// return customDelete; +// } +// +// public String getDiscriminatorMatchValue() { +// return discriminatorMatchValue; +// } +// +// public boolean isLazy() { +// return isLazy; +// } +// +// public String getProxy() { +// return proxy; +// } + + +// @Override +// public MetadataBuildingContext getBuildingContext() { +// return getModelContext()getModelProcessingContext().getMetadataBuildingContext(); +// } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/IdGeneratorRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/IdGeneratorRegistration.java new file mode 100644 index 0000000..3bb66ba --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/IdGeneratorRegistration.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.lang.annotation.Annotation; + +import org.hibernate.models.source.spi.AnnotationUsage; + +/** + * Represents both global and local id generator registrations + * + * @author Steve Ebersole + */ +public class IdGeneratorRegistration { + public enum Kind { + SEQUENCE, + TABLE, + UUID, + META, + GENERIC + } + + private final String name; + private final Kind kind; + private final AnnotationUsage configuration; + + public IdGeneratorRegistration(String name, Kind kind, AnnotationUsage configuration) { + this.name = name; + this.kind = kind; + this.configuration = configuration; + } + + /** + * The registration name. For local generators, this may be {@code null} + */ + public String getName() { + return name; + } + + /** + * The kind of generation + */ + public Kind getKind() { + return kind; + } + + /** + * The annotation which configures the generator + */ + public AnnotationUsage getConfiguration() { + return configuration; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JavaTypeRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JavaTypeRegistration.java new file mode 100644 index 0000000..ee73d37 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JavaTypeRegistration.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.models.source.spi.ClassDetails; + +/** + * @see org.hibernate.annotations.JavaTypeRegistration + * + * @author Steve Ebersole + */ +public class JavaTypeRegistration { + private final ClassDetails domainType; + private final ClassDetails descriptor; + + public JavaTypeRegistration(ClassDetails domainType, ClassDetails descriptor) { + this.domainType = domainType; + this.descriptor = descriptor; + } + + public ClassDetails getDomainType() { + return domainType; + } + + public ClassDetails getDescriptor() { + return descriptor; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JdbcTypeRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JdbcTypeRegistration.java new file mode 100644 index 0000000..0a5aab5 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/JdbcTypeRegistration.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.models.source.spi.ClassDetails; + +/** + * @see org.hibernate.annotations.JdbcTypeRegistration + * + * @author Steve Ebersole + */ +public class JdbcTypeRegistration { + private final Integer code; + private final ClassDetails descriptor; + + public JdbcTypeRegistration(Integer code, ClassDetails descriptor) { + this.code = code; + this.descriptor = descriptor; + } + + public Integer getCode() { + return code; + } + + public ClassDetails getDescriptor() { + return descriptor; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ManagedResourcesImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ManagedResourcesImpl.java new file mode 100644 index 0000000..d35c862 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ManagedResourcesImpl.java @@ -0,0 +1,103 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.hibernate.models.orm.spi.ManagedResources; + +/** + * @author Steve Ebersole + */ +public class ManagedResourcesImpl implements ManagedResources { + private final List packageNames; + private final List classNames; + private final List> loadedClasses; + private final List xmlMappings; + + public ManagedResourcesImpl( + List packageNames, + List classNames, + List> loadedClasses, + List xmlMappings) { + this.packageNames = packageNames; + this.classNames = classNames; + this.loadedClasses = loadedClasses; + this.xmlMappings = xmlMappings; + } + + @Override + public List getPackageNames() { + return packageNames; + } + + @Override + public List getClassNames() { + return classNames; + } + + @Override + public List> getLoadedClasses() { + return loadedClasses; + } + + @Override + public List getXmlMappings() { + return xmlMappings; + } + + + public static class Builder { + private List packageNames; + private List classNames; + private List> loadedClasses; + private List xmlMappings; + + public Builder addPackages(String... names) { + if ( packageNames == null ) { + packageNames = new ArrayList<>(); + } + Collections.addAll( packageNames, names ); + return this; + } + + public Builder addClassNames(String... names) { + if ( classNames == null ) { + classNames = new ArrayList<>(); + } + Collections.addAll( classNames, names ); + return this; + } + + public Builder addLoadedClasses(Class... classes) { + if ( loadedClasses == null ) { + loadedClasses = new ArrayList<>(); + } + Collections.addAll( loadedClasses, classes ); + return this; + } + + public Builder addXmlMappings(String... names) { + if ( xmlMappings == null ) { + xmlMappings = new ArrayList<>(); + } + Collections.addAll( xmlMappings, names ); + return this; + } + + public ManagedResources build() { + return new ManagedResourcesImpl( + packageNames == null ? Collections.emptyList() : packageNames, + classNames == null ? Collections.emptyList() : classNames, + loadedClasses == null ? Collections.emptyList() : loadedClasses, + xmlMappings == null ? Collections.emptyList() : xmlMappings + ); + } + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/MappedSuperclassTypeMetadataImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/MappedSuperclassTypeMetadataImpl.java new file mode 100644 index 0000000..4bfb903 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/MappedSuperclassTypeMetadataImpl.java @@ -0,0 +1,56 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.models.orm.spi.AttributeMetadata; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.IdentifiableTypeMetadata; +import org.hibernate.models.orm.spi.MappedSuperclassTypeMetadata; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.ClassDetails; + +import jakarta.persistence.AccessType; + +/** + * @author Steve Ebersole + */ +public class MappedSuperclassTypeMetadataImpl + extends AbstractIdentifiableTypeMetadata + implements MappedSuperclassTypeMetadata { + + private final List attributeList; + + public MappedSuperclassTypeMetadataImpl( + ClassDetails classDetails, + EntityHierarchy hierarchy, + AccessType defaultAccessType, + Consumer typeConsumer, + OrmModelBuildingContext modelContext) { + super( classDetails, hierarchy, false, defaultAccessType, typeConsumer, modelContext ); + + this.attributeList = resolveAttributes(); + } + + public MappedSuperclassTypeMetadataImpl( + ClassDetails classDetails, + EntityHierarchy hierarchy, + AbstractIdentifiableTypeMetadata superType, + Consumer typeConsumer, + OrmModelBuildingContext modelContext) { + super( classDetails, hierarchy, superType, typeConsumer, modelContext ); + + this.attributeList = resolveAttributes(); + } + + @Override + protected List attributeList() { + return attributeList; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/NamedQueryRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/NamedQueryRegistration.java new file mode 100644 index 0000000..545fffd --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/NamedQueryRegistration.java @@ -0,0 +1,56 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.lang.annotation.Annotation; + +import org.hibernate.models.source.spi.AnnotationUsage; + +/** + * @see org.hibernate.models.orm.spi.JpaAnnotations#NAMED_QUERY + * @see org.hibernate.models.orm.spi.JpaAnnotations#NAMED_NATIVE_QUERY + * @see org.hibernate.models.orm.spi.JpaAnnotations#NAMED_STORED_PROCEDURE_QUERY + * @see org.hibernate.models.orm.spi.HibernateAnnotations#NAMED_QUERY + * @see org.hibernate.models.orm.spi.HibernateAnnotations#NAMED_NATIVE_QUERY + * + * @author Steve Ebersole + */ +public class NamedQueryRegistration { + public enum Kind { + HQL, + NATIVE, + CALLABLE + } + + private final String name; + private final Kind kind; + private final boolean isJpa; + private final AnnotationUsage configuration; + + public NamedQueryRegistration(String name, Kind kind, boolean isJpa, AnnotationUsage configuration) { + this.name = name; + this.kind = kind; + this.isJpa = isJpa; + this.configuration = configuration; + } + + public String getName() { + return name; + } + + public Kind getKind() { + return kind; + } + + public boolean isJpa() { + return isJpa; + } + + public AnnotationUsage getConfiguration() { + return configuration; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/OrmModelBuildingContextImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/OrmModelBuildingContextImpl.java new file mode 100644 index 0000000..6a7b8f5 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/OrmModelBuildingContextImpl.java @@ -0,0 +1,77 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.boot.internal.ClassmateContext; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.AnnotationDescriptorRegistry; +import org.hibernate.models.source.spi.ClassDetailsRegistry; +import org.hibernate.models.source.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.ClassLoading; + +import org.jboss.jandex.IndexView; + +/** + * @author Steve Ebersole + */ +public class OrmModelBuildingContextImpl implements OrmModelBuildingContext { + private final ClassDetailsRegistry classDetailsRegistry; + private final AnnotationDescriptorRegistry annotationDescriptorRegistry; + private final ClassLoading classLoading; + private final IndexView jandexIndex; + private final ClassmateContext classmateContext; + + public OrmModelBuildingContextImpl( + SourceModelBuildingContext sourceModelBuildingContext, + ClassmateContext classmateContext) { + this( + sourceModelBuildingContext.getClassDetailsRegistry(), + sourceModelBuildingContext.getAnnotationDescriptorRegistry(), + sourceModelBuildingContext.getClassLoadingAccess(), + sourceModelBuildingContext.getJandexIndex(), + classmateContext + ); + } + + public OrmModelBuildingContextImpl( + ClassDetailsRegistry classDetailsRegistry, + AnnotationDescriptorRegistry annotationDescriptorRegistry, + ClassLoading classLoading, + IndexView jandexIndex, + ClassmateContext classmateContext) { + this.classDetailsRegistry = classDetailsRegistry; + this.annotationDescriptorRegistry = annotationDescriptorRegistry; + this.classLoading = classLoading; + this.jandexIndex = jandexIndex; + this.classmateContext = classmateContext; + } + + @Override + public ClassDetailsRegistry getClassDetailsRegistry() { + return classDetailsRegistry; + } + + @Override + public AnnotationDescriptorRegistry getAnnotationDescriptorRegistry() { + return annotationDescriptorRegistry; + } + + @Override + public ClassLoading getClassLoading() { + return classLoading; + } + + @Override + public IndexView getJandexIndex() { + return jandexIndex; + } + + @Override + public ClassmateContext getClassmateContext() { + return classmateContext; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java new file mode 100644 index 0000000..be08302 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultCollector.java @@ -0,0 +1,407 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.lang.annotation.Annotation; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.annotations.Parameter; +import org.hibernate.boot.jaxb.mapping.JaxbCollectionUserTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbCompositeUserTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbConfigurationParameter; +import org.hibernate.boot.jaxb.mapping.JaxbConverterRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbEmbeddableInstantiatorRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbEntityListener; +import org.hibernate.boot.jaxb.mapping.JaxbJavaTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbJdbcTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbUserTypeRegistration; +import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.metamodel.CollectionClassification; +import org.hibernate.models.internal.StringHelper; +import org.hibernate.models.orm.spi.ManagedResources; +import org.hibernate.models.orm.spi.ProcessResult; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.Processor; +import org.hibernate.models.source.spi.AnnotationTarget; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.ClassDetailsRegistry; +import org.hibernate.models.source.spi.SourceModelBuildingContext; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static org.hibernate.models.orm.spi.HibernateAnnotations.COLLECTION_TYPE_REG; +import static org.hibernate.models.orm.spi.HibernateAnnotations.COMPOSITE_TYPE_REG; +import static org.hibernate.models.orm.spi.HibernateAnnotations.CONVERTER_REG; +import static org.hibernate.models.orm.spi.HibernateAnnotations.EMBEDDABLE_INSTANTIATOR_REG; +import static org.hibernate.models.orm.spi.HibernateAnnotations.JAVA_TYPE_REG; +import static org.hibernate.models.orm.spi.HibernateAnnotations.JDBC_TYPE_REG; +import static org.hibernate.models.orm.spi.HibernateAnnotations.TYPE_REG; + +/** + * In-flight holder for various types of "global" registrations. Also acts as the + * {@linkplain #createResult builder} for {@linkplain ProcessResult} as returned + * by {@linkplain org.hibernate.models.orm.spi.Processor#process} + * + * @author Steve Ebersole + */ +public class ProcessResultCollector { + private final ClassDetailsRegistry classDetailsRegistry; + + private List entityListenerRegistrations; + private List autoAppliedConverters; + private List converterRegistrations; + private List javaTypeRegistrations; + private List jdbcTypeRegistrations; + private List userTypeRegistrations; + private List compositeUserTypeRegistrations; + private List collectionTypeRegistrations; + private List embeddableInstantiatorRegistrations; + + private Map globalIdGeneratorRegistrations; + private Map jpaNamedQueries; + private Map hibernateNamedHqlQueries; + private Map hibernateNamedNativeQueries; + + public ProcessResultCollector(SourceModelBuildingContext buildingContext) { + this( buildingContext.getClassDetailsRegistry() ); + } + public ProcessResultCollector(ClassDetailsRegistry classDetailsRegistry) { + this.classDetailsRegistry = classDetailsRegistry; + } + + public List getEntityListenerRegistrations() { + return entityListenerRegistrations; + } + + public List getConverterRegistrations() { + return converterRegistrations; + } + + public List getJavaTypeRegistrations() { + return javaTypeRegistrations; + } + + public List getJdbcTypeRegistrations() { + return jdbcTypeRegistrations; + } + + public List getUserTypeRegistrations() { + return userTypeRegistrations; + } + + public List getCompositeUserTypeRegistrations() { + return compositeUserTypeRegistrations; + } + + public List getCollectionTypeRegistrations() { + return collectionTypeRegistrations; + } + + public List getEmbeddableInstantiatorRegistrations() { + return embeddableInstantiatorRegistrations; + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // JavaTypeRegistration + + public void collectJavaTypeRegistrations(AnnotationTarget annotationTarget) { + annotationTarget.forEachUsage( JAVA_TYPE_REG, (usage) -> collectJavaTypeRegistration( + usage.getAttributeValue( "javaType" ), + usage.getAttributeValue( "descriptorClass" ) + ) ); + } + + public void collectJavaTypeRegistrations(List registrations) { + if ( CollectionHelper.isEmpty( registrations ) ) { + return; + } + + registrations.forEach( (reg) -> collectJavaTypeRegistration( + classDetailsRegistry.resolveClassDetails( reg.getClazz() ), + classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ) + ) ); + } + + public void collectJavaTypeRegistration(ClassDetails javaType, ClassDetails descriptor) { + if ( javaTypeRegistrations == null ) { + javaTypeRegistrations = new ArrayList<>(); + } + javaTypeRegistrations.add( new JavaTypeRegistration( javaType, descriptor ) ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // JdbcTypeRegistration + + public void collectJdbcTypeRegistrations(AnnotationTarget annotationTarget) { + annotationTarget.forEachUsage( JDBC_TYPE_REG, (usage) -> collectJdbcTypeRegistration( + usage.getAttributeValue( "registrationCode" ), + usage.getAttributeValue( "value" ) + ) ); + } + + public void collectJdbcTypeRegistrations(List registrations) { + if ( CollectionHelper.isEmpty( registrations ) ) { + return; + } + + registrations.forEach( (reg) -> collectJdbcTypeRegistration( + reg.getCode(), + classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ) + ) ); + } + + public void collectJdbcTypeRegistration(Integer registrationCode, ClassDetails descriptor) { + if ( jdbcTypeRegistrations == null ) { + jdbcTypeRegistrations = new ArrayList<>(); + } + jdbcTypeRegistrations.add( new JdbcTypeRegistration( registrationCode, descriptor ) ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // ConversionRegistration + + public void collectConverterRegistrations(AnnotationTarget annotationTarget) { + annotationTarget.forEachUsage( CONVERTER_REG, (usage) -> { + final ClassDetails domainType = usage.getAttributeValue( "domainType" ); + final ClassDetails converterType = usage.getAttributeValue( "converter" ); + final boolean autoApply = usage.getAttributeValue( "autoApply" ); + collectConverterRegistration( new ConversionRegistration( domainType, converterType, autoApply ) ); + } ); + } + + public void collectConverterRegistrations(List registrations) { + if ( CollectionHelper.isEmpty( registrations ) ) { + return; + } + + registrations.forEach( (registration) -> { + final ClassDetails explicitDomainType; + final String explicitDomainTypeName = registration.getClazz(); + if ( StringHelper.isNotEmpty( explicitDomainTypeName ) ) { + explicitDomainType = classDetailsRegistry.resolveClassDetails( explicitDomainTypeName ); + } + else { + explicitDomainType = null; + } + final ClassDetails converterType = classDetailsRegistry.resolveClassDetails( registration.getConverter() ); + final boolean autoApply = registration.isAutoApply(); + collectConverterRegistration( new ConversionRegistration( explicitDomainType, converterType, autoApply ) ); + } ); + } + + public void collectConverterRegistration(ConversionRegistration conversion) { + if ( converterRegistrations == null ) { + converterRegistrations = new ArrayList<>(); + } + converterRegistrations.add( conversion ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // UserTypeRegistration + + public void collectUserTypeRegistrations(AnnotationTarget annotationTarget) { + annotationTarget.forEachUsage( TYPE_REG, (usage) -> collectUserTypeRegistration( + usage.getAttributeValue( "basicClass" ), + usage.getAttributeValue( "userType" ) + ) ); + } + + public void collectUserTypeRegistrations(List registrations) { + if ( CollectionHelper.isEmpty( registrations ) ) { + return; + } + + registrations.forEach( (reg) -> { + final ClassDetails domainTypeDetails = classDetailsRegistry.resolveClassDetails( reg.getClazz() ); + final ClassDetails descriptorDetails = classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ); + collectUserTypeRegistration( domainTypeDetails, descriptorDetails ); + } ); + } + + public void collectUserTypeRegistration(ClassDetails domainClass, ClassDetails userTypeClass) { + if ( userTypeRegistrations == null ) { + userTypeRegistrations = new ArrayList<>(); + } + userTypeRegistrations.add( new UserTypeRegistration( domainClass, userTypeClass ) ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CompositeUserTypeRegistration + + public void collectCompositeUserTypeRegistrations(AnnotationTarget annotationTarget) { + annotationTarget.forEachUsage( COMPOSITE_TYPE_REG, (usage) -> collectCompositeUserTypeRegistration( + usage.getAttributeValue( "embeddableClass" ), + usage.getAttributeValue( "userType" ) + ) ); + } + + public void collectCompositeUserTypeRegistrations(List registrations) { + if ( CollectionHelper.isEmpty( registrations ) ) { + return; + } + + registrations.forEach( (reg) -> collectCompositeUserTypeRegistration( + classDetailsRegistry.resolveClassDetails( reg.getClazz() ), + classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ) + ) ); + } + + public void collectCompositeUserTypeRegistration(ClassDetails domainClass, ClassDetails userTypeClass) { + if ( compositeUserTypeRegistrations == null ) { + compositeUserTypeRegistrations = new ArrayList<>(); + } + compositeUserTypeRegistrations.add( new CompositeUserTypeRegistration( domainClass, userTypeClass ) ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // CollectionTypeRegistration + + public void collectCollectionTypeRegistrations(AnnotationTarget annotationTarget) { + annotationTarget.forEachUsage( COLLECTION_TYPE_REG, (usage) -> collectCollectionTypeRegistration( + usage.getAttributeValue( "classification" ), + usage.getAttributeValue( "type" ), + extractParameterMap( usage ) + ) ); + } + + private Map extractParameterMap(AnnotationUsage source) { + final List> parameters = source.getAttributeValue( "parameters" ); + + final Map result = new HashMap<>(); + for ( AnnotationUsage parameter : parameters ) { + result.put( + parameter.getAttributeValue( "name" ), + parameter.getAttributeValue( "value" ) + ); + } + return result; + } + + public void collectCollectionTypeRegistrations(List registrations) { + if ( CollectionHelper.isEmpty( registrations ) ) { + return; + } + + registrations.forEach( (reg) -> collectCollectionTypeRegistration( + reg.getClassification(), + classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ), + extractParameterMap( reg.getParameters() ) + ) ); + } + + private Map extractParameterMap(List parameters) { + if ( CollectionHelper.isEmpty( parameters ) ) { + return Collections.emptyMap(); + } + + final Map result = new HashMap<>(); + parameters.forEach( parameter -> result.put( parameter.getName(), parameter.getValue() ) ); + return result; + } + + public void collectCollectionTypeRegistration( + CollectionClassification classification, + ClassDetails userTypeClass, + Map parameters) { + if ( collectionTypeRegistrations == null ) { + collectionTypeRegistrations = new ArrayList<>(); + } + collectionTypeRegistrations.add( new CollectionTypeRegistration( classification, userTypeClass, parameters ) ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // EmbeddableInstantiatorRegistration + + public void collectEmbeddableInstantiatorRegistrations(AnnotationTarget annotationTarget) { + annotationTarget.forEachUsage( EMBEDDABLE_INSTANTIATOR_REG, (usage) -> collectEmbeddableInstantiatorRegistration( + usage.getAttributeValue( "embeddableClass" ), + usage.getAttributeValue( "instantiator" ) + ) ); + } + + public void collectEmbeddableInstantiatorRegistrations(List registrations) { + if ( CollectionHelper.isEmpty( registrations ) ) { + return; + } + + registrations.forEach( (reg) -> collectEmbeddableInstantiatorRegistration( + classDetailsRegistry.resolveClassDetails( reg.getEmbeddableClass() ), + classDetailsRegistry.resolveClassDetails( reg.getInstantiator() ) + ) ); + } + + public void collectEmbeddableInstantiatorRegistration(ClassDetails embeddableClass, ClassDetails instantiator) { + if ( embeddableInstantiatorRegistrations == null ) { + embeddableInstantiatorRegistrations = new ArrayList<>(); + } + embeddableInstantiatorRegistrations.add( new EmbeddableInstantiatorRegistration( embeddableClass, instantiator ) ); + } + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // EntityListenerRegistration + + public void collectEntityListenerRegistrations(List listeners) { + if ( CollectionHelper.isEmpty( listeners ) ) { + return; + } + + listeners.forEach( (listener) -> EntityListenerRegistration.from( listener, classDetailsRegistry ) ); + } + + + public void collectGlobalIdGeneratorRegistration( + String name, + IdGeneratorRegistration.Kind kind, + AnnotationUsage annotation) { + if ( globalIdGeneratorRegistrations == null ) { + globalIdGeneratorRegistrations = new HashMap<>(); + } + + globalIdGeneratorRegistrations.put( name, new IdGeneratorRegistration( name, kind, annotation ) ); + } + + /** + * Builder for {@linkplain ProcessResult} based on our internal state plus + * the incoming set of entity hierarchies. + * + * @param entityHierarchies Hierarchies to be {@linkplain ProcessResult#getEntityHierarchies() included} + * in the result + * + * @see org.hibernate.models.orm.spi.Processor#process + */ + public ProcessResult createResult(Set entityHierarchies) { + return new ProcessResultImpl( + entityHierarchies, + javaTypeRegistrations == null ? emptyList() : javaTypeRegistrations, + jdbcTypeRegistrations == null ? emptyList() : jdbcTypeRegistrations, + converterRegistrations == null ? emptyList() : converterRegistrations, + autoAppliedConverters == null ? emptyList() : autoAppliedConverters, + userTypeRegistrations == null ? emptyList() : userTypeRegistrations, + compositeUserTypeRegistrations == null ? emptyList() : compositeUserTypeRegistrations, + collectionTypeRegistrations == null ? emptyList() : collectionTypeRegistrations, + embeddableInstantiatorRegistrations == null ? emptyList() : embeddableInstantiatorRegistrations, + globalIdGeneratorRegistrations == null ? emptyMap() : globalIdGeneratorRegistrations, + jpaNamedQueries == null ? emptyMap() : jpaNamedQueries, + hibernateNamedHqlQueries == null ? emptyMap() : hibernateNamedHqlQueries, + hibernateNamedNativeQueries == null ? emptyMap() : hibernateNamedNativeQueries + ); + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java new file mode 100644 index 0000000..203548f --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/ProcessResultImpl.java @@ -0,0 +1,129 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.models.orm.spi.ProcessResult; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.source.spi.ClassDetails; + +/** + * @author Steve Ebersole + */ +public class ProcessResultImpl implements ProcessResult { + private final Set entityHierarchies; + + private final List javaTypeRegistrations; + private final List jdbcTypeRegistrations; + private final List converterRegistrations; + private final List autoAppliedConverters; + private final List userTypeRegistrations; + private final List compositeUserTypeRegistrations; + private final List collectionTypeRegistrations; + private final List embeddableInstantiatorRegistrations; + private final Map globalIdGeneratorRegistrations; + private final Map jpaNamedQueries; + private final Map hibernateNamedHqlQueries; + private final Map hibernateNamedNativeQueries; + + public ProcessResultImpl( + Set entityHierarchies, + List javaTypeRegistrations, + List jdbcTypeRegistrations, + List converterRegistrations, + List autoAppliedConverters, + List userTypeRegistrations, + List compositeUserTypeRegistrations, + List collectionTypeRegistrations, + List embeddableInstantiatorRegistrations, + Map globalIdGeneratorRegistrations, + Map jpaNamedQueries, + Map hibernateNamedHqlQueries, + Map hibernateNamedNativeQueries) { + this.entityHierarchies = entityHierarchies; + this.javaTypeRegistrations = javaTypeRegistrations; + this.jdbcTypeRegistrations = jdbcTypeRegistrations; + this.converterRegistrations = converterRegistrations; + this.autoAppliedConverters = autoAppliedConverters; + this.userTypeRegistrations = userTypeRegistrations; + this.compositeUserTypeRegistrations = compositeUserTypeRegistrations; + this.collectionTypeRegistrations = collectionTypeRegistrations; + this.embeddableInstantiatorRegistrations = embeddableInstantiatorRegistrations; + this.globalIdGeneratorRegistrations = globalIdGeneratorRegistrations; + this.jpaNamedQueries = jpaNamedQueries; + this.hibernateNamedHqlQueries = hibernateNamedHqlQueries; + this.hibernateNamedNativeQueries = hibernateNamedNativeQueries; + } + + @Override + public Set getEntityHierarchies() { + return entityHierarchies; + } + + @Override + public List getJavaTypeRegistrations() { + return javaTypeRegistrations; + } + + @Override + public List getJdbcTypeRegistrations() { + return jdbcTypeRegistrations; + } + + @Override + public List getConverterRegistrations() { + return converterRegistrations; + } + + @Override + public List getAutoAppliedConverters() { + return autoAppliedConverters; + } + + @Override + public List getUserTypeRegistrations() { + return userTypeRegistrations; + } + + @Override + public List getCompositeUserTypeRegistrations() { + return compositeUserTypeRegistrations; + } + + @Override + public List getCollectionTypeRegistrations() { + return collectionTypeRegistrations; + } + + @Override + public List getEmbeddableInstantiatorRegistrations() { + return embeddableInstantiatorRegistrations; + } + + @Override + public Map getGlobalIdGeneratorRegistrations() { + return globalIdGeneratorRegistrations; + } + + @Override + public Map getJpaNamedQueries() { + return jpaNamedQueries; + } + + @Override + public Map getHibernateNamedHqlQueries() { + return hibernateNamedHqlQueries; + } + + @Override + public Map getHibernateNamedNativeQueries() { + return hibernateNamedNativeQueries; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/StandardPersistentAttributeMemberResolver.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/StandardPersistentAttributeMemberResolver.java new file mode 100644 index 0000000..8eb2ac0 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/StandardPersistentAttributeMemberResolver.java @@ -0,0 +1,188 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Function; + +import org.hibernate.models.orm.AccessTypePlacementException; +import org.hibernate.models.orm.spi.JpaAnnotations; +import org.hibernate.models.orm.spi.OrmModelBuildingContext; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.FieldDetails; +import org.hibernate.models.source.spi.MemberDetails; +import org.hibernate.models.source.spi.MethodDetails; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; + +/** + * Standard implementation of the PersistentAttributeMemberResolver contract + * based strictly on the JPA specification. + * + * @author Steve Ebersole + */ +public class StandardPersistentAttributeMemberResolver extends AbstractPersistentAttributeMemberResolver { + /** + * Singleton access + */ + public static final StandardPersistentAttributeMemberResolver INSTANCE = new StandardPersistentAttributeMemberResolver(); + + @Override + protected List resolveAttributesMembers( + Function transientFieldChecker, + Function transientMethodChecker, + ClassDetails classDetails, + AccessType classLevelAccessType, + OrmModelBuildingContext processingContext) { + assert classLevelAccessType != null; + + final LinkedHashMap results = new LinkedHashMap<>(); + + processAttributeLevelAccess( + results::put, + transientFieldChecker, + transientMethodChecker, + classDetails, + processingContext + ); + + processClassLevelAccess( + results::containsKey, + results::put, + transientFieldChecker, + transientMethodChecker, + classDetails, + classLevelAccessType, + processingContext + ); + + return new ArrayList<>( results.values() ); + } + + private void processAttributeLevelAccess( + BiConsumer memberConsumer, + Function transientFieldChecker, + Function transientMethodChecker, + ClassDetails classDetails, + OrmModelBuildingContext processingContext) { + final List fields = classDetails.getFields(); + for ( int i = 0; i < fields.size(); i++ ) { + final FieldDetails fieldDetails = fields.get( i ); + processAttributeLevelAccessMember( fieldDetails, memberConsumer, transientFieldChecker, classDetails, processingContext ); + } + + final List methods = classDetails.getMethods(); + for ( int i = 0; i < methods.size(); i++ ) { + final MethodDetails methodDetails = methods.get( i ); + processAttributeLevelAccessMember( methodDetails, memberConsumer, transientMethodChecker, classDetails, processingContext ); + } + } + + private void processAttributeLevelAccessMember( + M memberDetails, + BiConsumer memberConsumer, + Function transiencyChecker, + ClassDetails classDetails, + OrmModelBuildingContext processingContext) { + final AnnotationUsage access = memberDetails.getUsage( JpaAnnotations.ACCESS ); + if ( access == null ) { + return; + } + + final AccessType attributeAccessType = access.getAttributeValue( "value" ); + + validateAttributeLevelAccess( + memberDetails, + attributeAccessType, + classDetails, + processingContext + ); + + if ( transiencyChecker.apply( memberDetails ) ) { + // the field is @Transient + return; + } + + memberConsumer.accept( memberDetails.resolveAttributeName(), memberDetails ); + } + + private void validateAttributeLevelAccess( + MemberDetails annotationTarget, + AccessType attributeAccessType, + ClassDetails classDetails, + OrmModelBuildingContext processingContext) { + // Apply the checks defined in section `2.3.2 Explicit Access Type` of the persistence specification + + // Mainly, it is never legal to: + // 1. specify @Access(FIELD) on a getter + // 2. specify @Access(PROPERTY) on a field + + if ( ( attributeAccessType == AccessType.FIELD && !annotationTarget.isField() ) + || ( attributeAccessType == AccessType.PROPERTY && annotationTarget.isField() ) ) { + throw new AccessTypePlacementException( classDetails, annotationTarget ); + } + } + + private void processClassLevelAccess( + Function alreadyProcessedChecker, + BiConsumer memberConsumer, + Function transientFieldChecker, + Function transientMethodChecker, + ClassDetails classDetails, + AccessType classLevelAccessType, + @SuppressWarnings("unused") OrmModelBuildingContext processingContext) { + if ( classLevelAccessType == AccessType.FIELD ) { + final List fields = classDetails.getFields(); + for ( int i = 0; i < fields.size(); i++ ) { + final FieldDetails fieldDetails = fields.get( i ); + if ( !fieldDetails.isPersistable() ) { + // the field cannot be a persistent attribute + continue; + } + + final String attributeName = fieldDetails.resolveAttributeName(); + if ( alreadyProcessedChecker.apply( attributeName ) ) { + continue; + } + + if ( transientFieldChecker.apply( fieldDetails ) ) { + // the field is @Transient + continue; + } + + memberConsumer.accept( attributeName, fieldDetails ); + } + } + else { + assert classLevelAccessType == AccessType.PROPERTY; + final List methods = classDetails.getMethods(); + for ( int i = 0; i < methods.size(); i++ ) { + final MethodDetails methodDetails = methods.get( i ); + if ( !methodDetails.isPersistable() ) { + continue; + } + + final String attributeName = methodDetails.resolveAttributeName(); + if ( alreadyProcessedChecker.apply( attributeName ) ) { + continue; + } + + if ( transientMethodChecker.apply( methodDetails ) ) { + // the method is @Transient + continue; + } + + memberConsumer.accept( attributeName, methodDetails ); + } + } + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/UserTypeRegistration.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/UserTypeRegistration.java new file mode 100644 index 0000000..9f47d6f --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/internal/UserTypeRegistration.java @@ -0,0 +1,30 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.internal; + +import org.hibernate.models.source.spi.ClassDetails; + +/** + * @author Steve Ebersole + */ +public class UserTypeRegistration { + private final ClassDetails domainClass; + private final ClassDetails userTypeClass; + + public UserTypeRegistration(ClassDetails domainClass, ClassDetails userTypeClass) { + this.domainClass = domainClass; + this.userTypeClass = userTypeClass; + } + + public ClassDetails getDomainClass() { + return domainClass; + } + + public ClassDetails getUserTypeClass() { + return userTypeClass; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/AttributeMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/AttributeMetadata.java index 9cdbf86..a0ec796 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/AttributeMetadata.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/AttributeMetadata.java @@ -10,13 +10,24 @@ import org.hibernate.models.source.spi.MemberDetails; /** + * Metadata about a persistent attribute + * * @author Steve Ebersole */ public interface AttributeMetadata { + /** + * The attribute name + */ String getName(); + /** + * The persistent nature of the attribute + */ AttributeNature getNature(); + /** + * The backing member + */ MemberDetails getMember(); /** diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/EntityTypeMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/EntityTypeMetadata.java index 57c1851..747891b 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/EntityTypeMetadata.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/EntityTypeMetadata.java @@ -7,7 +7,7 @@ package org.hibernate.models.orm.spi; /** - * Intermediate representation of an {@linkplain jakarta.persistence.metamodel.EntityType entity type} + * Metadata about an {@linkplain jakarta.persistence.metamodel.EntityType entity type} * * @author Steve Ebersole */ diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/IdentifiableTypeMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/IdentifiableTypeMetadata.java index a8b19ad..85aab21 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/IdentifiableTypeMetadata.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/IdentifiableTypeMetadata.java @@ -8,10 +8,8 @@ import java.util.function.Consumer; -import org.hibernate.models.source.spi.ClassDetails; - /** - * Intermediate representation of an {@linkplain jakarta.persistence.metamodel.IdentifiableType identifiable type} + * Metadata about an {@linkplain jakarta.persistence.metamodel.IdentifiableType identifiable type} * * @author Steve Ebersole */ @@ -34,6 +32,26 @@ default boolean isAbstract() { return getClassDetails().isAbstract(); } + /** + * Whether this type has subtypes + */ + boolean hasSubTypes(); + + /** + * Get the number of direct subtypes + */ + int getNumberOfSubTypes(); + + /** + * Get the direct subtypes + */ + Iterable getSubTypes(); + + /** + * Visit each direct subtype + */ + void forEachSubType(Consumer consumer); + // List getJpaCallbacks(); // void forEachJpaCallback(Consumer consumer); diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedResources.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedResources.java new file mode 100644 index 0000000..a57c6b1 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedResources.java @@ -0,0 +1,22 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.spi; + +import java.util.List; + +import org.hibernate.models.Copied; + +/** + * @author Steve Ebersole + */ +@Copied(org.hibernate.boot.model.process.spi.ManagedResources.class) +public interface ManagedResources { + List getPackageNames(); + List getClassNames(); + List> getLoadedClasses(); + List getXmlMappings(); +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedTypeMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedTypeMetadata.java index 034a39a..4b7d568 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedTypeMetadata.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ManagedTypeMetadata.java @@ -6,18 +6,15 @@ */ package org.hibernate.models.orm.spi; -import java.lang.annotation.Annotation; import java.util.Collection; import org.hibernate.models.internal.IndexedConsumer; -import org.hibernate.models.source.spi.AnnotationDescriptor; -import org.hibernate.models.source.spi.AnnotationUsage; import org.hibernate.models.source.spi.ClassDetails; import jakarta.persistence.AccessType; /** - * Intermediate representation of a {@linkplain jakarta.persistence.metamodel.ManagedType managed type} + * Metadata about a {@linkplain jakarta.persistence.metamodel.ManagedType managed type} * * @author Steve Ebersole */ diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/MappedSuperclassTypeMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/MappedSuperclassTypeMetadata.java index 5a3c15c..0be85f7 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/MappedSuperclassTypeMetadata.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/MappedSuperclassTypeMetadata.java @@ -7,7 +7,7 @@ package org.hibernate.models.orm.spi; /** - * Intermediate representation of a {@linkplain jakarta.persistence.metamodel.MappedSuperclassType mapped-superclass} + * Metadata about a {@linkplain jakarta.persistence.metamodel.MappedSuperclassType mapped-superclass} * * @author Steve Ebersole */ diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/OrmModelBuildingContext.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/OrmModelBuildingContext.java index 834baf5..b459c76 100644 --- a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/OrmModelBuildingContext.java +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/OrmModelBuildingContext.java @@ -14,7 +14,7 @@ import org.jboss.jandex.IndexView; /** - * Contextual information for building {@linkplain ManagedTypeMetadata} and friends. + * Contextual information used while building {@linkplain ManagedTypeMetadata} and friends. * * @author Steve Ebersole */ diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/PersistentAttributeMemberResolver.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/PersistentAttributeMemberResolver.java new file mode 100644 index 0000000..aeb25a5 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/PersistentAttributeMemberResolver.java @@ -0,0 +1,44 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.spi; + +import java.util.List; + +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.MemberDetails; + +import jakarta.persistence.AccessType; + +/** + * Contract responsible for resolving the members that identify the persistent + * attributes for a given class descriptor representing a managed type. + *

+ * These members (field or method) would be where we look for mapping annotations + * for the attribute. + *

+ * Additionally, whether the member is a field or method would tell us the default + * runtime {@linkplain org.hibernate.property.access.spi.PropertyAccessStrategy access strategy} + * + * @author Steve Ebersole + */ +public interface PersistentAttributeMemberResolver { + /** + * Given the class descriptor representing a ManagedType and the implicit AccessType + * to use, resolve the members that indicate persistent attributes. + * + * @param classDetails Descriptor of the class + * @param classLevelAccessType The implicit AccessType + * @param buildingContext The local context + * + * @return The list of "backing members" + */ + List resolveAttributesMembers( + ClassDetails classDetails, + AccessType classLevelAccessType, + OrmModelBuildingContext buildingContext); + +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java new file mode 100644 index 0000000..2d81e38 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/ProcessResult.java @@ -0,0 +1,55 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.spi; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.models.orm.internal.CollectionTypeRegistration; +import org.hibernate.models.orm.internal.CompositeUserTypeRegistration; +import org.hibernate.models.orm.internal.ConversionRegistration; +import org.hibernate.models.orm.internal.EmbeddableInstantiatorRegistration; +import org.hibernate.models.orm.internal.IdGeneratorRegistration; +import org.hibernate.models.orm.internal.JavaTypeRegistration; +import org.hibernate.models.orm.internal.JdbcTypeRegistration; +import org.hibernate.models.orm.internal.NamedQueryRegistration; +import org.hibernate.models.orm.internal.UserTypeRegistration; +import org.hibernate.models.source.spi.ClassDetails; + +/** + * The result of {@linkplain Processor#process processing} the domain model + * + * @author Steve Ebersole + */ +public interface ProcessResult { + Set getEntityHierarchies(); + + List getJavaTypeRegistrations(); + + List getJdbcTypeRegistrations(); + + List getConverterRegistrations(); + + List getAutoAppliedConverters(); + + List getUserTypeRegistrations(); + + List getCompositeUserTypeRegistrations(); + + List getCollectionTypeRegistrations(); + + List getEmbeddableInstantiatorRegistrations(); + + Map getGlobalIdGeneratorRegistrations(); + + Map getJpaNamedQueries(); + + Map getHibernateNamedHqlQueries(); + + Map getHibernateNamedNativeQueries(); +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/Processor.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/Processor.java new file mode 100644 index 0000000..0f0653e --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/spi/Processor.java @@ -0,0 +1,448 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.spi; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.hibernate.boot.internal.ClassmateContext; +import org.hibernate.boot.jaxb.Origin; +import org.hibernate.boot.jaxb.SourceType; +import org.hibernate.boot.jaxb.internal.MappingBinder; +import org.hibernate.boot.jaxb.mapping.JaxbEmbeddable; +import org.hibernate.boot.jaxb.mapping.JaxbEntity; +import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings; +import org.hibernate.boot.jaxb.mapping.JaxbMappedSuperclass; +import org.hibernate.boot.jaxb.spi.BindableMappingDescriptor; +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.internal.util.StringHelper; +import org.hibernate.internal.util.collections.CollectionHelper; +import org.hibernate.models.orm.internal.EntityHierarchyBuilder; +import org.hibernate.models.orm.internal.OrmModelBuildingContextImpl; +import org.hibernate.models.orm.internal.OrmModelLogging; +import org.hibernate.models.orm.internal.ProcessResultCollector; +import org.hibernate.models.orm.xml.XmlResourceException; +import org.hibernate.models.orm.xml.internal.ResourceStreamLocatorImpl; +import org.hibernate.models.orm.xml.spi.XmlResources; +import org.hibernate.models.source.UnknownClassException; +import org.hibernate.models.source.internal.jandex.JandexClassDetails; +import org.hibernate.models.source.internal.jandex.JandexPackageDetails; +import org.hibernate.models.source.internal.jdk.JdkBuilders; +import org.hibernate.models.source.internal.jdk.JdkPackageDetailsImpl; +import org.hibernate.models.source.spi.AnnotationDescriptorRegistry; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.ClassDetailsRegistry; +import org.hibernate.models.source.spi.PackageDetails; +import org.hibernate.models.source.spi.SourceModelBuildingContext; +import org.hibernate.models.spi.ClassLoading; + +import org.jboss.jandex.ClassInfo; +import org.jboss.jandex.IndexView; + +import jakarta.persistence.Embeddable; + +import static org.hibernate.boot.jaxb.internal.MappingBinder.NON_VALIDATING; +import static org.hibernate.models.orm.internal.EntityHierarchyBuilder.createEntityHierarchies; + +/** + * Processes {@linkplain ManagedResources managed resources} and produces a + * {@linkplain ProcessResult result} which collects broad categorizations of the + * classes defined by those resources based on annotations (and XML mappings). + * + * @author Steve Ebersole + */ +public class Processor { + public interface Options { + boolean areGeneratorsGlobal(); + boolean shouldIgnoreUnlistedClasses(); + } + + public static ProcessResult process( + ManagedResources managedResources, + List explicitlyListedClasses, + Options options, + SourceModelBuildingContext sourceModelBuildingContext) { + fillRegistries( sourceModelBuildingContext ); + + final OrmModelBuildingContext ormModelBuildingContext = new OrmModelBuildingContextImpl( + sourceModelBuildingContext, + new ClassmateContext() + ); + + return process( managedResources, explicitlyListedClasses, options, sourceModelBuildingContext, ormModelBuildingContext ); + } + + public static ProcessResult process( + ManagedResources managedResources, + List explicitlyListedClasses, + Options options, + SourceModelBuildingContext sourceModelBuildingContext, + OrmModelBuildingContext mappingBuildingContext) { + final XmlResources collectedXmlResources = collectXmlResources( managedResources, mappingBuildingContext ); + final ProcessResultCollector processResultCollector = new ProcessResultCollector( sourceModelBuildingContext.getClassDetailsRegistry() ); + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // process XML + // 1. handle registrations (JavaType, etc.) and named references (named query, etc.) + // 2. process managed types - + // a. create ClassDetails for all "complete" mappings + // b. collect "incomplete" (override) mappings + // 3. apply XML overrides + + final List entityOverrides = new ArrayList<>(); + final List mappedSuperclassesOverrides = new ArrayList<>(); + final List embeddableOverrides = new ArrayList<>(); + + final boolean xmlMappingsGloballyComplete = collectedXmlResources.getPersistenceUnitMetadata().areXmlMappingsComplete(); + + collectedXmlResources.getDocuments().forEach( (jaxbRoot) -> { + processResultCollector.collectJavaTypeRegistrations( jaxbRoot.getJavaTypeRegistrations() ); + processResultCollector.collectJdbcTypeRegistrations( jaxbRoot.getJdbcTypeRegistrations() ); + processResultCollector.collectConverterRegistrations( jaxbRoot.getConverterRegistrations() ); + processResultCollector.collectUserTypeRegistrations( jaxbRoot.getUserTypeRegistrations() ); + processResultCollector.collectCompositeUserTypeRegistrations( jaxbRoot.getCompositeUserTypeRegistrations() ); + processResultCollector.collectCollectionTypeRegistrations( jaxbRoot.getCollectionUserTypeRegistrations() ); + processResultCollector.collectEmbeddableInstantiatorRegistrations( jaxbRoot.getEmbeddableInstantiatorRegistrations() ); + processResultCollector.collectEntityListenerRegistrations( jaxbRoot.getPersistenceUnitMetadata().getPersistenceUnitDefaults().getEntityListeners().getEntityListener() ); + + jaxbRoot.getEmbeddables().forEach( (embeddable) -> { + if ( xmlMappingsGloballyComplete || embeddable.isMetadataComplete() ) { + applyCompleteEmbeddableMapping( embeddable, sourceModelBuildingContext ); + } + else { + embeddableOverrides.add( embeddable ); + } + } ); + + jaxbRoot.getMappedSuperclasses().forEach( (mappedSuperclass) -> { + if ( xmlMappingsGloballyComplete || mappedSuperclass.isMetadataComplete() ) { + applyCompleteMappedSuperclassMapping( mappedSuperclass, sourceModelBuildingContext ); + } + else { + mappedSuperclassesOverrides.add( mappedSuperclass ); + } + }); + + jaxbRoot.getEntities().forEach( (entity) -> { + if ( xmlMappingsGloballyComplete || entity.isMetadataComplete() ) { + applyCompleteEntityMapping( entity, sourceModelBuildingContext ); + } + else { + entityOverrides.add( entity ); + } + } ); + } ); + + final ClassInclusions classInclusions; + final PackageInclusions packageInclusions; + if ( options.shouldIgnoreUnlistedClasses() ) { + final List listedClasses = new ArrayList<>(); + final List listedPackages = new ArrayList<>(); + classInclusions = listedClasses::contains; + packageInclusions = listedPackages::contains; + if ( CollectionHelper.isEmpty( explicitlyListedClasses ) ) { + OrmModelLogging.ORM_MODEL_LOGGER.debugf( "Ignore unlisted classes was requested, but no classes were listed" ); + } + else { + collectListedResources( explicitlyListedClasses, listedClasses, listedPackages, sourceModelBuildingContext ); + } + } + else { + classInclusions = null; + packageInclusions = null; + } + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // process "global" annotations - id generators, registrations, named references + // todo : possibly handle annotation versions of registrations and named references at the same time as (XML#1)? + + final Set rootEntities = new HashSet<>(); + final Map allEntities = new HashMap<>(); + final Map mappedSuperClasses = new HashMap<>(); + final Map embeddables = new HashMap<>(); + + processResources( + classInclusions, + packageInclusions, + processResultCollector, + rootEntities, + allEntities, + mappedSuperClasses, + embeddables, + options, + mappingBuildingContext + ); + + applyEntityOverrides( allEntities, entityOverrides ); + applyMappedSuperclassOverrides( mappedSuperClasses, mappedSuperclassesOverrides ); + applyEmbeddableOverrides( embeddables, embeddableOverrides ); + + final Set entityHierarchies = createEntityHierarchies( + rootEntities, + (identifiableType) -> { + if ( identifiableType instanceof MappedSuperclassTypeMetadata ) { + mappedSuperClasses.remove( identifiableType.getClassDetails().getClassName() ); + } + }, + mappingBuildingContext + ); + + if ( OrmModelLogging.ORM_MODEL_LOGGER.isDebugEnabled() ) { + warnAboutUnusedMappedSuperclasses( mappedSuperClasses ); + } + + return processResultCollector.createResult( entityHierarchies ); + } + + private static void warnAboutUnusedMappedSuperclasses(Map mappedSuperClasses) { + assert OrmModelLogging.ORM_MODEL_LOGGER.isDebugEnabled(); + for ( Map.Entry entry : mappedSuperClasses.entrySet() ) { + OrmModelLogging.ORM_MODEL_LOGGER.debugf( + "Encountered MappedSuperclass [%s] which was unused in any entity hierarchies", + entry.getKey() + ); + } + } + + private static void applyEmbeddableOverrides( + Map embeddables, + List embeddableOverrides) { + + } + + private static void applyMappedSuperclassOverrides( + Map mappedSuperClasses, + List mappedSuperclassesOverrides) { + + } + + private static void applyEntityOverrides(Map allEntities, List entityOverrides) { + + } + + private static void applyCompleteEntityMapping( + JaxbEntity entity, + SourceModelBuildingContext sourceModelBuildingContext) { + + } + + private static void applyCompleteMappedSuperclassMapping(JaxbMappedSuperclass mappedSuperclass, + SourceModelBuildingContext sourceModelBuildingContext) { + + } + + private static void applyCompleteEmbeddableMapping(JaxbEmbeddable embeddable, + SourceModelBuildingContext sourceModelBuildingContext) { + + } + + private static void collectListedResources( + List explicitlyListedClasses, + List listedClasses, + List listedPackages, + SourceModelBuildingContext sourceModelBuildingContext) { + final ClassDetailsRegistry classDetailsRegistry = sourceModelBuildingContext.getClassDetailsRegistry(); + final ClassLoading classLoading = sourceModelBuildingContext.getClassLoadingAccess(); + + explicitlyListedClasses.forEach( (listed) -> { + if ( listed.endsWith( "package-info" ) ) { + // we know it is a package + final String packageName = StringHelper.qualifier( listed ); + final Package packageForName = classLoading.packageForName( packageName ); + assert packageForName != null; + listedPackages.add( classDetailsRegistry.resolvePackageDetails( + packageName, + () -> new JdkPackageDetailsImpl( packageForName, sourceModelBuildingContext ) + ) ); + } + else { + // could be a class or package + try { + final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails( listed ); + if ( classDetails != null ) { + listedClasses.add( classDetails ); + } + } + catch (UnknownClassException e) { + // see if it is a package + final Package packageForName = classLoading.packageForName( listed ); + if ( packageForName == null ) { + // todo : what to do here? + } + else { + listedPackages.add( classDetailsRegistry.resolvePackageDetails( + listed, + () -> new JdkPackageDetailsImpl( packageForName, sourceModelBuildingContext ) + ) ); + } + } + } + } ); + } + + private static XmlResources collectXmlResources( + ManagedResources managedResources, + OrmModelBuildingContext mappingBuildingContext) { + final ClassLoading classLoading = mappingBuildingContext.getClassLoading(); + final ResourceStreamLocatorImpl resourceStreamLocator = new ResourceStreamLocatorImpl( classLoading ); + final MappingBinder mappingBinder = new MappingBinder( resourceStreamLocator, NON_VALIDATING ); + final XmlResources collected = new XmlResources(); + + final List xmlMappings = managedResources.getXmlMappings(); + for ( int i = 0; i < xmlMappings.size(); i++ ) { + final String xmlMapping = xmlMappings.get( i ); + final URL resource = classLoading.locateResource( xmlMapping ); + if ( resource == null ) { + throw new XmlResourceException( "Unable to locate XML mapping - " + xmlMapping ); + } + try (InputStream inputStream = resource.openStream()) { + final Binding binding = mappingBinder.bind( + inputStream, + new Origin( SourceType.RESOURCE, xmlMapping ) + ); + collected.addDocument( (JaxbEntityMappings) binding.getRoot() ); + } + catch (IOException e) { + throw new XmlResourceException( "Unable to bind XML mapping - " + xmlMapping, e ); + } + } + + return collected; + } + + private static void fillRegistries(SourceModelBuildingContext buildingContext) { + final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry(); + final AnnotationDescriptorRegistry annotationDescriptorRegistry = buildingContext.getAnnotationDescriptorRegistry(); + final IndexView jandexIndex = buildingContext.getJandexIndex(); + + for ( ClassInfo knownClass : jandexIndex.getKnownClasses() ) { + final String className = knownClass.name().toString(); + if ( className.endsWith( "package-info" ) ) { + classDetailsRegistry.resolvePackageDetails( + className, + () -> new JandexPackageDetails( knownClass, buildingContext ) + ); + continue; + } + + if ( knownClass.isAnnotation() ) { + // it is always safe to load the annotation classes - we will never be enhancing them + //noinspection rawtypes + final Class annotationClass = buildingContext + .getClassLoadingAccess() + .classForName( className ); + //noinspection unchecked + annotationDescriptorRegistry.resolveDescriptor( + annotationClass, + (t) -> JdkBuilders.buildAnnotationDescriptor( annotationClass, buildingContext ) + ); + } + + classDetailsRegistry.resolveClassDetails( + className, + () -> new JandexClassDetails( knownClass, buildingContext ) + ); + } + } + + private static void processResources( + ClassInclusions classInclusions, + PackageInclusions packageInclusions, + ProcessResultCollector processResultCollector, + Set rootEntities, + Map allEntities, + Map mappedSuperClasses, + Map embeddables, + Options options, + OrmModelBuildingContext mappingBuildingContext) { + final ClassDetailsRegistry classDetailsRegistry = mappingBuildingContext.getClassDetailsRegistry(); + classDetailsRegistry.forEachClassDetails( (classDetails) -> { + if ( classInclusions != null && !classInclusions.shouldInclude( classDetails ) ) { + // skip this class + return; + } + + processResultCollector.collectJavaTypeRegistrations( classDetails ); + processResultCollector.collectJdbcTypeRegistrations( classDetails ); + processResultCollector.collectConverterRegistrations( classDetails ); + processResultCollector.collectUserTypeRegistrations( classDetails ); + processResultCollector.collectCompositeUserTypeRegistrations( classDetails ); + processResultCollector.collectCollectionTypeRegistrations( classDetails ); + processResultCollector.collectEmbeddableInstantiatorRegistrations( classDetails ); + + if ( classDetails.getUsage( JpaAnnotations.MAPPED_SUPERCLASS ) != null ) { + if ( classDetails.getClassName() != null ) { + mappedSuperClasses.put( classDetails.getClassName(), classDetails ); + } + processIdentifiableType( classDetails, mappingBuildingContext ); + } + else if ( classDetails.getUsage( JpaAnnotations.ENTITY ) != null ) { + if ( classDetails.getClassName() != null ) { + allEntities.put( classDetails.getClassName(), classDetails ); + } + if ( EntityHierarchyBuilder.isRoot( classDetails ) ) { + rootEntities.add( classDetails ); + } + processIdentifiableType( classDetails, mappingBuildingContext ); + } + else if ( classDetails.getUsage( Embeddable.class ) != null ) { + if ( classDetails.getClassName() != null ) { + embeddables.put( classDetails.getClassName(), classDetails ); + } + processNonIdentifiableType( classDetails, mappingBuildingContext ); + } + else { + processNonIdentifiableType( classDetails, mappingBuildingContext ); + } + } ); + + classDetailsRegistry.forEachPackageDetails( (packageDetails) -> { + if ( packageInclusions != null && !packageInclusions.shouldInclude( packageDetails ) ) { + // skip this class + return; + } + + processResultCollector.collectJavaTypeRegistrations( packageDetails ); + processResultCollector.collectJdbcTypeRegistrations( packageDetails ); + processResultCollector.collectConverterRegistrations( packageDetails ); + processResultCollector.collectUserTypeRegistrations( packageDetails ); + processResultCollector.collectCompositeUserTypeRegistrations( packageDetails ); + processResultCollector.collectCollectionTypeRegistrations( packageDetails ); + processResultCollector.collectEmbeddableInstantiatorRegistrations( packageDetails ); + } ); + } + + @FunctionalInterface + private interface ClassInclusions { + boolean shouldInclude(ClassDetails classDetails); + } + + @FunctionalInterface + private interface PackageInclusions { + boolean shouldInclude(PackageDetails packageDetails); + } + + private static void processIdentifiableType( + ClassDetails classDetails, + OrmModelBuildingContext mappingBuildingContext) { + + } + + private static void processNonIdentifiableType( + ClassDetails classDetails, + OrmModelBuildingContext mappingBuildingContext) { + + } + +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/XmlResourceException.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/XmlResourceException.java new file mode 100644 index 0000000..b5b1797 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/XmlResourceException.java @@ -0,0 +1,24 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml; + +import org.hibernate.models.ModelsException; + +/** + * Generally indicates a problem locating or binding an XML resource + * + * @author Steve Ebersole + */ +public class XmlResourceException extends ModelsException { + public XmlResourceException(String message) { + super( message ); + } + + public XmlResourceException(String message, Throwable cause) { + super( message, cause ); + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/PersistenceUnitMetadataImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/PersistenceUnitMetadataImpl.java new file mode 100644 index 0000000..f2ffd50 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/PersistenceUnitMetadataImpl.java @@ -0,0 +1,158 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.internal; + +import java.util.EnumSet; +import java.util.HashSet; +import java.util.Set; + +import org.hibernate.annotations.CascadeType; +import org.hibernate.boot.jaxb.mapping.JaxbEntityListener; +import org.hibernate.boot.jaxb.mapping.JaxbPersistenceUnitDefaults; +import org.hibernate.boot.jaxb.mapping.JaxbPersistenceUnitMetadata; +import org.hibernate.models.orm.internal.OrmModelLogging; +import org.hibernate.models.orm.xml.spi.PersistenceUnitMetadata; + +import jakarta.persistence.AccessType; + +import static org.hibernate.internal.util.StringHelper.isNotEmpty; +import static org.hibernate.internal.util.StringHelper.splitTrimmingTokens; + +/** + * @author Steve Ebersole + */ +public final class PersistenceUnitMetadataImpl implements PersistenceUnitMetadata { + private boolean quoteIdentifiers; + private boolean xmlComplete; + + private String defaultSchema; + private String defaultCatalog; + private AccessType accessType; + private String defaultAccessStrategy; + + private final EnumSet defaultCascadeTypes = EnumSet.noneOf( CascadeType.class ); + private final Set globalEntityListeners = new HashSet<>(); + + @Override + public boolean areXmlMappingsComplete() { + return xmlComplete; + } + + @Override + public String getDefaultSchema() { + return defaultSchema; + } + + @Override + public String getDefaultCatalog() { + return defaultCatalog; + } + + @Override + public AccessType getAccessType() { + return accessType; + } + + @Override + public String getDefaultAccessStrategyName() { + return defaultAccessStrategy; + } + + @Override + public EnumSet getDefaultCascadeTypes() { + return defaultCascadeTypes; + } + + @Override + public boolean useQuotedIdentifiers() { + return quoteIdentifiers; + } + + @Override + public Set getEntityListeners() { + return globalEntityListeners; + } + + public void apply(JaxbPersistenceUnitMetadata metadata) { + if ( metadata == null ) { + return; + } + + xmlComplete = xmlComplete || metadata.getXmlMappingMetadataComplete() != null; + + final JaxbPersistenceUnitDefaults defaults = metadata.getPersistenceUnitDefaults(); + if ( defaults == null ) { + return; + } + + quoteIdentifiers = quoteIdentifiers || defaults.getDelimitedIdentifiers() != null; + + if ( isNotEmpty( defaults.getCatalog() ) ) { + if ( defaultCatalog != null ) { + OrmModelLogging.ORM_MODEL_LOGGER.debugf( + "Setting already set default catalog : %s, %s", + defaultCatalog, + defaults.getCatalog() + ); + } + defaultCatalog = defaults.getCatalog(); + } + + if ( isNotEmpty( defaults.getSchema() ) ) { + if ( defaultSchema != null ) { + OrmModelLogging.ORM_MODEL_LOGGER.debugf( + "Setting already set default schema : %s, %s", + defaultSchema, + defaults.getSchema() + ); + } + defaultSchema = defaults.getSchema(); + } + + if ( defaults.getAccess() != null ) { + if ( accessType != null ) { + OrmModelLogging.ORM_MODEL_LOGGER.debugf( + "Overriding already set default AccessType : %s, %s", + defaults.getAccess(), + accessType + ); + } + accessType = defaults.getAccess(); + } + + if ( isNotEmpty( defaults.getDefaultAccess() ) ) { + if ( defaultAccessStrategy != null ) { + OrmModelLogging.ORM_MODEL_LOGGER.debugf( + "Overriding already set default access strategy : %s, %s", + accessType, + defaultAccessStrategy + ); + } + defaultAccessStrategy = defaults.getDefaultAccess(); + } + + if ( defaults.getCascadePersist() != null + || isNotEmpty( defaults.getDefaultCascade() ) ) { + if ( !defaultCascadeTypes.isEmpty() ) { + OrmModelLogging.ORM_MODEL_LOGGER.debugf( "Adding cascades to already defined set of default cascades" ); + } + + if ( defaults.getCascadePersist() != null ) { + defaultCascadeTypes.add( CascadeType.PERSIST ); + } + + if ( isNotEmpty( defaults.getDefaultCascade() ) ) { + final String[] actions = splitTrimmingTokens( ",", defaults.getDefaultCascade(), false ); + assert actions.length > 0; + + for ( int i = 0; i < actions.length; i++ ) { + defaultCascadeTypes.add( CascadeType.valueOf( actions[i] ) ); + } + } + } + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/ResourceStreamLocatorImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/ResourceStreamLocatorImpl.java new file mode 100644 index 0000000..dfe3d13 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/ResourceStreamLocatorImpl.java @@ -0,0 +1,36 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.internal; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import org.hibernate.boot.ResourceStreamLocator; +import org.hibernate.models.spi.ClassLoading; + +/** + * @author Steve Ebersole + */ +public class ResourceStreamLocatorImpl implements ResourceStreamLocator { + private final ClassLoading classLoadingAccess; + + public ResourceStreamLocatorImpl(ClassLoading classLoadingAccess) { + this.classLoadingAccess = classLoadingAccess; + } + + @Override + public InputStream locateResourceStream(String resourceName) { + final URL resource = classLoadingAccess.locateResource( resourceName ); + try { + return resource.openStream(); + } + catch (IOException e) { + throw new RuntimeException( e ); + } + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlDocumentImpl.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlDocumentImpl.java new file mode 100644 index 0000000..e45412e --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlDocumentImpl.java @@ -0,0 +1,309 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.internal; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmNamedNativeQueryType; +import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmNamedQueryType; +import org.hibernate.boot.jaxb.mapping.JaxbCollectionUserTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbCompositeUserTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbConverter; +import org.hibernate.boot.jaxb.mapping.JaxbConverterRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbEmbeddable; +import org.hibernate.boot.jaxb.mapping.JaxbEmbeddableInstantiatorRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbEntity; +import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings; +import org.hibernate.boot.jaxb.mapping.JaxbJavaTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbJdbcTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbMappedSuperclass; +import org.hibernate.boot.jaxb.mapping.JaxbNamedNativeQuery; +import org.hibernate.boot.jaxb.mapping.JaxbNamedQuery; +import org.hibernate.boot.jaxb.mapping.JaxbNamedStoredProcedureQuery; +import org.hibernate.boot.jaxb.mapping.JaxbUserTypeRegistration; +import org.hibernate.internal.util.NullnessHelper; +import org.hibernate.models.orm.xml.spi.PersistenceUnitMetadata; +import org.hibernate.models.orm.xml.spi.XmlDocument; + +import jakarta.persistence.AccessType; + +import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty; + +/** + * @author Steve Ebersole + */ +public class XmlDocumentImpl implements XmlDocument { + private final DefaultsImpl defaults; + private final List entityMappings; + private final List mappedSuperclassMappings; + private final List embeddableMappings; + private final List converters; + private final List converterRegistrations; + private final List javaTypeRegistrations; + private final List jdbcTypeRegistrations; + private final List userTypeRegistrations; + private final List compositeUserTypeRegistrations; + private final List collectionUserTypeRegistrations; + private final List embeddableInstantiatorRegistrations; + private final Map jpaNamedQueries; + private final Map jpaNamedNativeQueries; + private final Map hibernateNamedQueries; + private final Map hibernateNamedNativeQueries; + private final Map namedStoredProcedureQueries; + + private XmlDocumentImpl( + DefaultsImpl defaults, + List entityMappings, + List mappedSuperclassMappings, + List embeddableMappings, + List converters, + List converterRegistrations, + List javaTypeRegistrations, + List jdbcTypeRegistrations, + List userTypeRegistrations, + List compositeUserTypeRegistrations, + List collectionUserTypeRegistrations, + List embeddableInstantiatorRegistrations, + Map jpaNamedQueries, + Map jpaNamedNativeQueries, + Map namedStoredProcedureQueries, + Map hibernateNamedQueries, + Map hibernateNamedNativeQueries) { + this.defaults = defaults; + this.entityMappings = entityMappings; + this.mappedSuperclassMappings = mappedSuperclassMappings; + this.embeddableMappings = embeddableMappings; + this.converters = converters; + this.converterRegistrations = converterRegistrations; + this.javaTypeRegistrations = javaTypeRegistrations; + this.jdbcTypeRegistrations = jdbcTypeRegistrations; + this.userTypeRegistrations = userTypeRegistrations; + this.compositeUserTypeRegistrations = compositeUserTypeRegistrations; + this.collectionUserTypeRegistrations = collectionUserTypeRegistrations; + this.embeddableInstantiatorRegistrations = embeddableInstantiatorRegistrations; + this.jpaNamedQueries = jpaNamedQueries; + this.jpaNamedNativeQueries = jpaNamedNativeQueries; + this.namedStoredProcedureQueries = namedStoredProcedureQueries; + this.hibernateNamedQueries = hibernateNamedQueries; + this.hibernateNamedNativeQueries = hibernateNamedNativeQueries; + } + + @Override + public Defaults getDefaults() { + return defaults; + } + + @Override + public List getEntityMappings() { + return entityMappings; + } + + @Override + public List getMappedSuperclassMappings() { + return mappedSuperclassMappings; + } + + @Override + public List getEmbeddableMappings() { + return embeddableMappings; + } + + @Override + public List getConverters() { + return converters; + } + + @Override + public List getConverterRegistrations() { + return converterRegistrations; + } + + @Override + public List getJavaTypeRegistrations() { + return javaTypeRegistrations; + } + + @Override + public List getJdbcTypeRegistrations() { + return jdbcTypeRegistrations; + } + + @Override + public List getUserTypeRegistrations() { + return userTypeRegistrations; + } + + @Override + public List getCompositeUserTypeRegistrations() { + return compositeUserTypeRegistrations; + } + + @Override + public List getCollectionUserTypeRegistrations() { + return collectionUserTypeRegistrations; + } + + @Override + public List getEmbeddableInstantiatorRegistrations() { + return embeddableInstantiatorRegistrations; + } + + @Override + public Map getJpaNamedQueries() { + return jpaNamedQueries; + } + + @Override + public Map getJpaNamedNativeQueries() { + return jpaNamedNativeQueries; + } + + @Override + public Map getHibernateNamedQueries() { + return hibernateNamedQueries; + } + + @Override + public Map getHibernateNamedNativeQueries() { + return hibernateNamedNativeQueries; + } + + @Override + public Map getNamedStoredProcedureQueries() { + return namedStoredProcedureQueries; + } + + private static class DefaultsImpl implements Defaults { + private final String pckg; + private final AccessType accessType; + private final String accessorStrategy; + private final String catalog; + private final String schema; + private final boolean autoImport; + private final boolean impliedLaziness; + + private DefaultsImpl( + String pckg, + AccessType accessType, + String accessorStrategy, + String catalog, + String schema, + Boolean autoImport, + Boolean impliedLaziness) { + this.pckg = pckg; + this.accessType = accessType; + this.accessorStrategy = accessorStrategy; + this.catalog = catalog; + this.schema = schema; + this.autoImport = NullnessHelper.nullif( autoImport, true ); + this.impliedLaziness = NullnessHelper.nullif( impliedLaziness, false ); + } + + @Override + public String getPackage() { + return pckg; + } + + @Override + public AccessType getAccessType() { + return accessType; + } + + @Override + public String getAccessorStrategy() { + return accessorStrategy; + } + + @Override + public String getCatalog() { + return catalog; + } + + @Override + public String getSchema() { + return schema; + } + + @Override + public boolean isAutoImport() { + return autoImport; + } + + @Override + public boolean isLazinessImplied() { + return impliedLaziness; + } + + static DefaultsImpl consume(JaxbEntityMappings jaxbRoot, PersistenceUnitMetadata metadata) { + return new DefaultsImpl( + jaxbRoot.getPackage(), + NullnessHelper.coalesce( jaxbRoot.getAccess(), metadata.getAccessType() ), + NullnessHelper.coalesce( jaxbRoot.getAttributeAccessor(), metadata.getDefaultAccessStrategyName() ), + NullnessHelper.coalesce( jaxbRoot.getCatalog(), metadata.getDefaultCatalog() ), + NullnessHelper.coalesce( jaxbRoot.getSchema(), metadata.getDefaultSchema() ), + jaxbRoot.isAutoImport(), + jaxbRoot.isDefaultLazy() + ); + } + } + + public static XmlDocumentImpl consume(JaxbEntityMappings jaxbRoot, PersistenceUnitMetadata metadata) { + return new XmlDocumentImpl( + DefaultsImpl.consume( jaxbRoot, metadata ), + jaxbRoot.getEntities(), + jaxbRoot.getMappedSuperclasses(), + jaxbRoot.getEmbeddables(), + jaxbRoot.getConverters(), + jaxbRoot.getConverterRegistrations(), + jaxbRoot.getJavaTypeRegistrations(), + jaxbRoot.getJdbcTypeRegistrations(), + jaxbRoot.getUserTypeRegistrations(), + jaxbRoot.getCompositeUserTypeRegistrations(), + jaxbRoot.getCollectionUserTypeRegistrations(), + jaxbRoot.getEmbeddableInstantiatorRegistrations(), + toNamedQueryMap( jaxbRoot.getNamedQueries() ), + toNamedNativeQueryMap( jaxbRoot.getNamedNativeQueries() ), + toNamedProcedureQueryMap( jaxbRoot.getNamedProcedureQueries() ), + // not sure what's up with the Hibernate-specific named query nodes, but they are not in the root mapping + Collections.emptyMap(), + Collections.emptyMap() + ); + } + + private static Map toNamedQueryMap(List namedQueries) { + if ( isEmpty( namedQueries ) ) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + namedQueries.forEach( (query) -> map.put( query.getName(), query ) ); + return map; + } + + private static Map toNamedNativeQueryMap(List namedQueries) { + if ( isEmpty( namedQueries ) ) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + namedQueries.forEach( (query) -> map.put( query.getName(), query ) ); + return map; + } + + private static Map toNamedProcedureQueryMap(List namedQueries) { + if ( isEmpty( namedQueries ) ) { + return Collections.emptyMap(); + } + + final Map map = new HashMap<>(); + namedQueries.forEach( (query) -> map.put( query.getName(), query ) ); + return map; + } +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlProcessingState.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlProcessingState.java new file mode 100644 index 0000000..376b666 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/internal/XmlProcessingState.java @@ -0,0 +1,29 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.internal; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.boot.jaxb.mapping.ManagedType; +import org.hibernate.models.orm.xml.spi.XmlProcessingContext; + +/** + * @author Steve Ebersole + */ +public class XmlProcessingState { + private final XmlProcessingContext processingContext; + private final PersistenceUnitMetadataImpl persistenceUnitMetadata = new PersistenceUnitMetadataImpl(); + + private final List xmlOverrideMappings = new ArrayList<>(); + private final List completeMappings = new ArrayList<>(); + + public XmlProcessingState(XmlProcessingContext processingContext) { + this.processingContext = processingContext; + } + +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/PersistenceUnitMetadata.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/PersistenceUnitMetadata.java new file mode 100644 index 0000000..094adcc --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/PersistenceUnitMetadata.java @@ -0,0 +1,69 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.spi; + +import java.util.EnumSet; +import java.util.Set; + +import org.hibernate.annotations.CascadeType; +import org.hibernate.boot.jaxb.mapping.JaxbEntityListener; +import org.hibernate.property.access.spi.PropertyAccessStrategy; + +import jakarta.persistence.AccessType; + +/** + * @author Steve Ebersole + */ +public interface PersistenceUnitMetadata { + /** + * Whether XML mappings are complete for the entire persistent unit. + * See {@code entity-mappings/persistence-unit-metadata/xml-mapping-metadata-complete} + */ + boolean areXmlMappingsComplete(); + + /** + * Default schema in effect for the entire persistent unit. + * See {@code entity-mappings/persistence-unit-metadata/persistence-unit-defaults/schema} + */ + String getDefaultSchema(); + + /** + * Default catalog in effect for the entire persistent unit. + * See {@code entity-mappings/persistence-unit-metadata/persistence-unit-defaults/catalog} + */ + String getDefaultCatalog(); + + /** + * Default AccessType in effect for the entire persistence unit. + * See {@code entity-mappings/persistence-unit-metadata/persistence-unit-defaults/access} + */ + AccessType getAccessType(); + + /** + * Name of the default {@link PropertyAccessStrategy} in effect for the entire persistence unit + * See {@code entity-mappings/persistence-unit-metadata/persistence-unit-defaults/default-access} + */ + String getDefaultAccessStrategyName(); + + /** + * Cascades to apply by default for this persistence unit + * See {@code entity-mappings/persistence-unit-metadata/persistence-unit-defaults/default-cascade} + */ + EnumSet getDefaultCascadeTypes(); + + /** + * Whether to quote all database identifiers in the persistence unit + * See {@code entity-mappings/persistence-unit-metadata/persistence-unit-defaults/delimited-identifiers} + */ + boolean useQuotedIdentifiers(); + + /** + * Entity listeners in effect for the entire persistence unit + * See {@code entity-mappings/persistence-unit-metadata/persistence-unit-defaults/entity-listeners} + */ + Set getEntityListeners(); +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlDocument.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlDocument.java new file mode 100644 index 0000000..93c3480 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlDocument.java @@ -0,0 +1,80 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.spi; + +import java.util.List; +import java.util.Map; + +import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmNamedNativeQueryType; +import org.hibernate.boot.jaxb.hbm.spi.JaxbHbmNamedQueryType; +import org.hibernate.boot.jaxb.mapping.JaxbCollectionUserTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbCompositeUserTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbConverter; +import org.hibernate.boot.jaxb.mapping.JaxbConverterRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbEmbeddable; +import org.hibernate.boot.jaxb.mapping.JaxbEmbeddableInstantiatorRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbEntity; +import org.hibernate.boot.jaxb.mapping.JaxbJavaTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbJdbcTypeRegistration; +import org.hibernate.boot.jaxb.mapping.JaxbMappedSuperclass; +import org.hibernate.boot.jaxb.mapping.JaxbNamedNativeQuery; +import org.hibernate.boot.jaxb.mapping.JaxbNamedQuery; +import org.hibernate.boot.jaxb.mapping.JaxbNamedStoredProcedureQuery; +import org.hibernate.boot.jaxb.mapping.JaxbUserTypeRegistration; + +import jakarta.persistence.AccessType; + +/** + * @author Steve Ebersole + */ +public interface XmlDocument { + List getEntityMappings(); + + List getMappedSuperclassMappings(); + + List getEmbeddableMappings(); + + List getConverters(); + + List getConverterRegistrations(); + + List getJavaTypeRegistrations(); + + List getJdbcTypeRegistrations(); + + List getUserTypeRegistrations(); + + List getCompositeUserTypeRegistrations(); + + List getCollectionUserTypeRegistrations(); + + List getEmbeddableInstantiatorRegistrations(); + + Map getJpaNamedQueries(); + + Map getJpaNamedNativeQueries(); + + Map getHibernateNamedQueries(); + + Map getHibernateNamedNativeQueries(); + + Map getNamedStoredProcedureQueries(); + + interface Defaults { + String getPackage(); + AccessType getAccessType(); + String getAccessorStrategy(); + String getCatalog(); + String getSchema(); + boolean isAutoImport(); + boolean isLazinessImplied(); + } + + Defaults getDefaults(); + + +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlProcessingContext.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlProcessingContext.java new file mode 100644 index 0000000..a61a99e --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlProcessingContext.java @@ -0,0 +1,13 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.spi; + +/** + * @author Steve Ebersole + */ +public interface XmlProcessingContext { +} diff --git a/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlResources.java b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlResources.java new file mode 100644 index 0000000..33c4bc4 --- /dev/null +++ b/hibernate-models-orm/src/main/java/org/hibernate/models/orm/xml/spi/XmlResources.java @@ -0,0 +1,37 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml.spi; + +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings; +import org.hibernate.models.orm.xml.internal.PersistenceUnitMetadataImpl; + +/** + * @author Steve Ebersole + */ +public class XmlResources { + private final PersistenceUnitMetadataImpl persistenceUnitMetadata = new PersistenceUnitMetadataImpl(); + private final List documents = new ArrayList<>(); + + public XmlResources() { + } + + public PersistenceUnitMetadata getPersistenceUnitMetadata() { + return persistenceUnitMetadata; + } + + public List getDocuments() { + return documents; + } + + public void addDocument(JaxbEntityMappings document) { + persistenceUnitMetadata.apply( document.getPersistenceUnitMetadata() ); + documents.add( document ); + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/XmlHelper.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/XmlHelper.java new file mode 100644 index 0000000..4d80478 --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/XmlHelper.java @@ -0,0 +1,60 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import org.hibernate.boot.ResourceStreamLocator; +import org.hibernate.boot.jaxb.Origin; +import org.hibernate.boot.jaxb.SourceType; +import org.hibernate.boot.jaxb.internal.MappingBinder; +import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings; +import org.hibernate.boot.jaxb.spi.BindableMappingDescriptor; +import org.hibernate.boot.jaxb.spi.Binding; +import org.hibernate.models.orm.xml.XmlResourceException; +import org.hibernate.models.spi.ClassLoading; + +import static org.hibernate.boot.jaxb.internal.MappingBinder.NON_VALIDATING; + +/** + * @author Steve Ebersole + */ +public class XmlHelper { + public static JaxbEntityMappings loadMapping(String resourceName, ClassLoading classLoadingAccess) { + final ResourceStreamLocatorImpl resourceStreamLocator = new ResourceStreamLocatorImpl( classLoadingAccess ); + final MappingBinder mappingBinder = new MappingBinder( resourceStreamLocator, NON_VALIDATING ); + final Binding binding = mappingBinder.bind( + resourceStreamLocator.locateResourceStream( resourceName ), + new Origin( SourceType.RESOURCE, resourceName ) + ); + return (JaxbEntityMappings) binding.getRoot(); + } + + private static class ResourceStreamLocatorImpl implements ResourceStreamLocator { + private final ClassLoading classLoadingAccess; + + public ResourceStreamLocatorImpl(ClassLoading classLoadingAccess) { + this.classLoadingAccess = classLoadingAccess; + } + + @Override + public InputStream locateResourceStream(String resourceName) { + final URL resource = classLoadingAccess.locateResource( resourceName ); + if ( resource == null ) { + throw new XmlResourceException( "Could not locate XML mapping resource - " + resourceName ); + } + try { + return resource.openStream(); + } + catch (IOException e) { + throw new XmlResourceException( "Could not open XML mapping resource stream - " + resourceName, e ); + } + } + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyStringConverter.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyStringConverter.java new file mode 100644 index 0000000..b34c0e1 --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyStringConverter.java @@ -0,0 +1,26 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.process; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +/** + * @author Steve Ebersole + */ +@Converter(autoApply = true) +public class MyStringConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(String attribute) { + return null; + } + + @Override + public String convertToEntityAttribute(String dbData) { + return null; + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyUuidConverter.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyUuidConverter.java new file mode 100644 index 0000000..d711c3a --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/MyUuidConverter.java @@ -0,0 +1,28 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.process; + +import java.util.UUID; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Converter; + +/** + * @author Steve Ebersole + */ +@Converter +public class MyUuidConverter implements AttributeConverter { + @Override + public String convertToDatabaseColumn(UUID attribute) { + return null; + } + + @Override + public UUID convertToEntityAttribute(String dbData) { + return null; + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java new file mode 100644 index 0000000..faca3b6 --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Person.java @@ -0,0 +1,45 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.process; + +import java.util.UUID; + +import org.hibernate.annotations.ConverterRegistration; +import org.hibernate.annotations.GenericGenerator; +import org.hibernate.annotations.JavaTypeRegistration; +import org.hibernate.id.IncrementGenerator; +import org.hibernate.type.descriptor.java.StringJavaType; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.NamedNativeQuery; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.NamedStoredProcedureQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.TableGenerator; + +/** + * @author Steve Ebersole + */ +@Entity(name = "Person") +@Table(name = "persons") +@SequenceGenerator(name = "seq_gen", sequenceName = "id_seq") +@TableGenerator(name = "tbl_gen", table = "id_tbl") +@GenericGenerator(name = "increment_gen", type = IncrementGenerator.class) +@JavaTypeRegistration(javaType = String.class, descriptorClass = StringJavaType.class) +@ConverterRegistration(domainType = UUID.class, converter = MyUuidConverter.class) +@NamedQuery(name = "jpaHql", query = "from Person") +@NamedNativeQuery(name = "jpaNative", query = "select * from persons") +@NamedStoredProcedureQuery(name = "jpaCallable", procedureName = "jpa_callable") +@org.hibernate.annotations.NamedQuery(name = "ormHql", query = "from Person") +@org.hibernate.annotations.NamedNativeQuery(name = "ormNative", query = "select * from persons") +public class Person { + @Id + private Integer id; + private String name; +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/ProcessorSmokeTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/ProcessorSmokeTests.java new file mode 100644 index 0000000..090b371 --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/ProcessorSmokeTests.java @@ -0,0 +1,76 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.process; + +import org.hibernate.models.orm.internal.ManagedResourcesImpl; +import org.hibernate.models.orm.spi.ManagedResources; +import org.hibernate.models.source.SourceModelTestHelper; +import org.hibernate.models.source.internal.SourceModelBuildingContextImpl; +import org.hibernate.models.source.spi.ClassDetails; +import org.hibernate.models.source.spi.ClassDetailsRegistry; +import org.hibernate.type.CharBooleanConverter; +import org.hibernate.type.YesNoConverter; +import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.java.StringJavaType; + +import org.junit.jupiter.api.Test; + +import org.jboss.jandex.Index; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING; + +/** + * @author Steve Ebersole + */ +public class ProcessorSmokeTests { + @Test + void simpleTest() { + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // ManagedResources is built by scanning and from explicit resources + // during ORM bootstrap + final ManagedResourcesImpl.Builder managedResourcesBuilder = new ManagedResourcesImpl.Builder(); + managedResourcesBuilder + .addLoadedClasses( Person.class, MyStringConverter.class ) + .addPackages( "org.hibernate.models.orm.process" ); + final ManagedResources managedResources = managedResourcesBuilder.build(); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // The Jandex index would generally (1) be built by WF and passed + // to ORM or (2) be built by ORM + final Index jandexIndex = SourceModelTestHelper.buildJandexIndex( + SIMPLE_CLASS_LOADING, + Person.class, + MyStringConverter.class, + MyUuidConverter.class, + YesNoConverter.class, + CharBooleanConverter.class, + BasicValueConverter.class, + StringJavaType.class, + AbstractClassJavaType.class + ); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Above here is work done before hibernate-models. + // Below here is work done by hibernate-models. + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final SourceModelBuildingContextImpl buildingContext = SourceModelTestHelper.createBuildingContext( + jandexIndex, + SIMPLE_CLASS_LOADING + ); + final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry(); + final ClassDetails personClassDetails = classDetailsRegistry.findClassDetails( Person.class.getName() ); + assertThat( personClassDetails ).isNotNull(); + } + +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Root.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Root.java new file mode 100644 index 0000000..df0758c --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Root.java @@ -0,0 +1,25 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.process; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; +import jakarta.persistence.Table; + +/** + * @author Steve Ebersole + */ +@Entity(name = "Root") +@Table(name = "roots") +@Inheritance(strategy = InheritanceType.JOINED) +public class Root { + @Id + private Integer id; + private String name; +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java new file mode 100644 index 0000000..e147048 --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/SimpleProcessorTests.java @@ -0,0 +1,148 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.process; + +import java.util.Iterator; + +import org.hibernate.models.orm.internal.ManagedResourcesImpl; +import org.hibernate.models.orm.spi.EntityHierarchy; +import org.hibernate.models.orm.spi.EntityTypeMetadata; +import org.hibernate.models.orm.spi.IdentifiableTypeMetadata; +import org.hibernate.models.orm.spi.ManagedResources; +import org.hibernate.models.orm.spi.ProcessResult; +import org.hibernate.models.orm.spi.Processor; +import org.hibernate.models.source.SourceModelTestHelper; +import org.hibernate.models.source.internal.SourceModelBuildingContextImpl; +import org.hibernate.type.CharBooleanConverter; +import org.hibernate.type.YesNoConverter; +import org.hibernate.type.descriptor.converter.spi.BasicValueConverter; +import org.hibernate.type.descriptor.java.AbstractClassJavaType; +import org.hibernate.type.descriptor.java.StringJavaType; + +import org.junit.jupiter.api.Test; + +import org.jboss.jandex.Index; + +import jakarta.persistence.InheritanceType; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING; + +/** + * @author Steve Ebersole + */ +public class SimpleProcessorTests { + @Test + void testSimpleUsage() { + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // ManagedResources is built by scanning and from explicit resources + // during ORM bootstrap + final ManagedResourcesImpl.Builder managedResourcesBuilder = new ManagedResourcesImpl.Builder(); + managedResourcesBuilder + .addLoadedClasses( Person.class, Root.class, Sub.class, MyStringConverter.class, MyUuidConverter.class ) + .addPackages( "org.hibernate.models.orm.process" ); + final ManagedResources managedResources = managedResourcesBuilder.build(); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // The Jandex index would generally (1) be built by WF and passed + // to ORM or (2) be built by ORM + final Index jandexIndex = SourceModelTestHelper.buildJandexIndex( + SIMPLE_CLASS_LOADING, + Person.class, + Root.class, + Sub.class, + MyStringConverter.class, + MyUuidConverter.class, + YesNoConverter.class, + CharBooleanConverter.class, + BasicValueConverter.class, + StringJavaType.class, + AbstractClassJavaType.class + ); + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Above here is work done before hibernate-models. + // Below here is work done by hibernate-models. + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + final SourceModelBuildingContextImpl buildingContext = SourceModelTestHelper.createBuildingContext( + jandexIndex, + SIMPLE_CLASS_LOADING + ); + + final ProcessResult processResult = Processor.process( + managedResources, + null, + new Processor.Options() { + @Override + public boolean areGeneratorsGlobal() { + return false; + } + + @Override + public boolean shouldIgnoreUnlistedClasses() { + return false; + } + }, + buildingContext + ); + + assertThat( processResult.getEntityHierarchies() ).hasSize( 2 ); + final Iterator hierarchies = processResult.getEntityHierarchies().iterator(); + final EntityHierarchy one = hierarchies.next(); + final EntityHierarchy two = hierarchies.next(); + + assertThat( one.getRoot() ).isNotNull(); + assertThat( one.getRoot().getClassDetails() ).isNotNull(); + assertThat( one.getRoot().getClassDetails().getClassName() ).isNotNull(); + if ( one.getRoot().getClassDetails().getClassName().endsWith( "Person" ) ) { + validatePersonHierarchy( one ); + validateJoinedHierarchy( two ); + } + else { + validatePersonHierarchy( two ); + validateJoinedHierarchy( one ); + } + } + + private void validatePersonHierarchy(EntityHierarchy hierarchy) { + assertThat( hierarchy.getInheritanceType() ).isEqualTo( InheritanceType.SINGLE_TABLE ); + final EntityTypeMetadata personMetadata = hierarchy.getRoot(); + assertThat( personMetadata.getClassDetails().getClassName() ).isEqualTo( Person.class.getName() ); + assertThat( personMetadata.getJpaEntityName() ).isEqualTo( "Person" ); + assertThat( personMetadata.getEntityName() ).isEqualTo( Person.class.getName() ); + + assertThat( personMetadata.getSuperType() ).isNull(); + assertThat( personMetadata.hasSubTypes() ).isFalse(); + assertThat( personMetadata.getNumberOfSubTypes() ).isEqualTo( 0 ); + } + + private void validateJoinedHierarchy(EntityHierarchy hierarchy) { + assertThat( hierarchy.getInheritanceType() ).isEqualTo( InheritanceType.JOINED ); + final EntityTypeMetadata rootMetadata = hierarchy.getRoot(); + assertThat( rootMetadata.getClassDetails().getClassName() ).isEqualTo( Root.class.getName() ); + assertThat( rootMetadata.getJpaEntityName() ).isEqualTo( "Root" ); + assertThat( rootMetadata.getEntityName() ).isEqualTo( Root.class.getName() ); + + assertThat( rootMetadata.getSuperType() ).isNull(); + assertThat( rootMetadata.hasSubTypes() ).isTrue(); + assertThat( rootMetadata.getNumberOfSubTypes() ).isEqualTo( 1 ); + + final EntityTypeMetadata subMetadata = (EntityTypeMetadata) rootMetadata.getSubTypes().iterator().next(); + assertThat( subMetadata ).isNotNull(); + assertThat( subMetadata.getClassDetails().getClassName() ).isEqualTo( Sub.class.getName() ); + assertThat( subMetadata.getJpaEntityName() ).isEqualTo( "Sub" ); + assertThat( subMetadata.getEntityName() ).isEqualTo( Sub.class.getName() ); + assertThat( subMetadata.getSuperType() ).isEqualTo( rootMetadata ); + assertThat( subMetadata.hasSubTypes() ).isFalse(); + assertThat( subMetadata.getNumberOfSubTypes() ).isEqualTo( 0 ); + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Sub.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Sub.java new file mode 100644 index 0000000..a42a546 --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/process/Sub.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.process; + +import jakarta.persistence.Access; +import jakarta.persistence.AccessType; +import jakarta.persistence.Basic; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; + +/** + * @author Steve Ebersole + */ +@Entity(name = "Sub") +@Table(name = "subs") +@Access(AccessType.PROPERTY) +public class Sub extends Root { + private String moreDetails; + + @Basic + public String getMoreDetails() { + return moreDetails; + } + + public void setMoreDetails(String moreDetails) { + this.moreDetails = moreDetails; + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/MyUserType.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/MyUserType.java new file mode 100644 index 0000000..14013a5 --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/MyUserType.java @@ -0,0 +1,74 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.UUID; + +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.usertype.UserType; + +/** + * @author Steve Ebersole + */ +public class MyUserType implements UserType { + + @Override + public int getSqlType() { + return 0; + } + + @Override + public Class returnedClass() { + return null; + } + + @Override + public boolean equals(UUID x, UUID y) { + return false; + } + + @Override + public int hashCode(UUID x) { + return 0; + } + + @Override + public UUID nullSafeGet(ResultSet rs, int position, SharedSessionContractImplementor session, Object owner) + throws SQLException { + return null; + } + + @Override + public void nullSafeSet(PreparedStatement st, UUID value, int index, SharedSessionContractImplementor session) + throws SQLException { + + } + + @Override + public UUID deepCopy(UUID value) { + return null; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(UUID value) { + return null; + } + + @Override + public UUID assemble(Serializable cached, Object owner) { + return null; + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/SimpleEntity.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/SimpleEntity.java new file mode 100644 index 0000000..df2e47d --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/SimpleEntity.java @@ -0,0 +1,43 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Basic; + +/** + * @author Steve Ebersole + */ +@Entity +public class SimpleEntity { + @Id + private Integer id; + @Basic + private String name; + + protected SimpleEntity() { + // for Hibernate use + } + + public SimpleEntity(Integer id, String name) { + this.id = id; + this.name = name; + } + + public Integer getId() { + return id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java new file mode 100644 index 0000000..051827d --- /dev/null +++ b/hibernate-models-orm/src/test/java/org/hibernate/models/orm/xml/XmlProcessingSmokeTests.java @@ -0,0 +1,144 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.orm.xml; + +import java.util.List; + +import org.hibernate.boot.jaxb.mapping.JaxbEntityMappings; +import org.hibernate.models.orm.internal.ProcessResultCollector; +import org.hibernate.models.orm.xml.internal.XmlDocumentImpl; +import org.hibernate.models.orm.xml.spi.XmlResources; +import org.hibernate.models.orm.xml.spi.PersistenceUnitMetadata; +import org.hibernate.models.source.SourceModelTestHelper; +import org.hibernate.models.source.internal.StringTypeDescriptor; +import org.hibernate.models.source.spi.SourceModelBuildingContext; +import org.hibernate.type.descriptor.jdbc.ClobJdbcType; + +import org.junit.jupiter.api.Test; + +import static jakarta.persistence.AccessType.FIELD; +import static org.assertj.core.api.Assertions.assertThat; +import static org.hibernate.annotations.CascadeType.LOCK; +import static org.hibernate.annotations.CascadeType.PERSIST; +import static org.hibernate.annotations.CascadeType.REMOVE; +import static org.hibernate.models.internal.SimpleClassLoading.SIMPLE_CLASS_LOADING; +import static org.hibernate.models.orm.XmlHelper.loadMapping; + +/** + * @author Steve Ebersole + */ +public class XmlProcessingSmokeTests { + @Test + void testPersistenceUnitDefaults1() { + final XmlResources collector = new XmlResources(); + + final JaxbEntityMappings simple1 = loadMapping( "mappings/simple1.xml", SIMPLE_CLASS_LOADING ); + final JaxbEntityMappings simple2 = loadMapping( "mappings/simple2.xml", SIMPLE_CLASS_LOADING ); + collector.addDocument( simple1 ); + collector.addDocument( simple2); + + final PersistenceUnitMetadata metadata = collector.getPersistenceUnitMetadata(); + // xml-mappings-complete is a gated flag - once we see a true, it should always be considered true + assertThat( metadata.areXmlMappingsComplete() ).isTrue(); + // same for quoted identifiers + assertThat( metadata.useQuotedIdentifiers() ).isTrue(); + + // default cascades are additive + assertThat( metadata.getDefaultCascadeTypes() ).containsAll( List.of( PERSIST, REMOVE, LOCK ) ); + + // simple2.xml should take precedence + assertThat( metadata.getDefaultCatalog() ).isEqualTo( "catalog2" ); + assertThat( metadata.getDefaultSchema() ).isEqualTo( "schema2" ); + assertThat( metadata.getAccessType() ).isEqualTo( FIELD ); + assertThat( metadata.getDefaultAccessStrategyName() ).isEqualTo( "MIXED" ); + } + + @Test + void testPersistenceUnitDefaults2() { + final XmlResources collector = new XmlResources(); + + collector.addDocument( loadMapping( "mappings/simple2.xml", SIMPLE_CLASS_LOADING ) ); + collector.addDocument( loadMapping( "mappings/simple1.xml", SIMPLE_CLASS_LOADING ) ); + + final PersistenceUnitMetadata metadata = collector.getPersistenceUnitMetadata(); + // xml-mappings-complete is a gated flag - once we see a true, it should always be considered true + assertThat( metadata.areXmlMappingsComplete() ).isTrue(); + // same for quoted identifiers + assertThat( metadata.useQuotedIdentifiers() ).isTrue(); + + // default cascades are additive + assertThat( metadata.getDefaultCascadeTypes() ).containsAll( List.of( PERSIST, REMOVE, LOCK ) ); + + // simple1.xml should take precedence + assertThat( metadata.getDefaultCatalog() ).isEqualTo( "catalog1" ); + assertThat( metadata.getDefaultSchema() ).isEqualTo( "schema1" ); + assertThat( metadata.getAccessType() ).isEqualTo( FIELD ); + assertThat( metadata.getDefaultAccessStrategyName() ).isEqualTo( "MIXED" ); + } + + @Test + void testSimpleXmlDocumentBuilding() { + final XmlResources collector = new XmlResources(); + + final JaxbEntityMappings simple1 = loadMapping( "mappings/simple1.xml", SIMPLE_CLASS_LOADING ); + final JaxbEntityMappings simple2 = loadMapping( "mappings/simple2.xml", SIMPLE_CLASS_LOADING ); + collector.addDocument( simple1 ); + collector.addDocument( simple2 ); + + final PersistenceUnitMetadata metadata = collector.getPersistenceUnitMetadata(); + + final XmlDocumentImpl simple1Doc = XmlDocumentImpl.consume( simple1, metadata ); + assertThat( simple1Doc.getDefaults().getPackage() ).isEqualTo( "org.hibernate.models.orm.xml" ); + assertThat( simple1Doc.getEntityMappings() ).hasSize( 1 ); + assertThat( simple1Doc.getEntityMappings().get( 0 ).getClazz() ).isEqualTo( "SimpleEntity" ); + assertThat( simple1Doc.getEntityMappings().get( 0 ).getName() ).isNull(); + assertThat( simple1Doc.getEntityMappings().get( 0 ).isMetadataComplete() ).isNull(); + + final XmlDocumentImpl simple2Doc = XmlDocumentImpl.consume( simple2, metadata ); + assertThat( simple2Doc.getDefaults().getPackage() ).isNull(); + assertThat( simple2Doc.getEntityMappings() ).hasSize( 1 ); + assertThat( simple2Doc.getEntityMappings().get( 0 ).getClazz() ).isNull(); + assertThat( simple2Doc.getEntityMappings().get( 0 ).getName() ).isEqualTo( "DynamicEntity" ); + assertThat( simple2Doc.getEntityMappings().get( 0 ).isMetadataComplete() ).isTrue(); + } + + @Test + void testSimpleGlobalXmlProcessing() { + final SourceModelBuildingContext buildingContext = SourceModelTestHelper.createBuildingContext(); + final XmlResources collectedXmlResources = new XmlResources(); + + final JaxbEntityMappings xmlMapping = loadMapping( "mappings/globals.xml", SIMPLE_CLASS_LOADING ); + collectedXmlResources.addDocument( xmlMapping ); + + final ProcessResultCollector processResultCollector = new ProcessResultCollector( buildingContext.getClassDetailsRegistry() ); + collectedXmlResources.getDocuments().forEach( (jaxbRoot) -> { + processResultCollector.collectJavaTypeRegistrations( jaxbRoot.getJavaTypeRegistrations() ); + processResultCollector.collectJdbcTypeRegistrations( jaxbRoot.getJdbcTypeRegistrations() ); + processResultCollector.collectConverterRegistrations( jaxbRoot.getConverterRegistrations() ); + processResultCollector.collectUserTypeRegistrations( jaxbRoot.getUserTypeRegistrations() ); + processResultCollector.collectCompositeUserTypeRegistrations( jaxbRoot.getCompositeUserTypeRegistrations() ); + processResultCollector.collectCollectionTypeRegistrations( jaxbRoot.getCollectionUserTypeRegistrations() ); + processResultCollector.collectEmbeddableInstantiatorRegistrations( jaxbRoot.getEmbeddableInstantiatorRegistrations() ); + } ); + + assertThat( processResultCollector.getJavaTypeRegistrations() ).hasSize( 1 ); + assertThat( processResultCollector.getJavaTypeRegistrations().get(0).getDescriptor().getClassName() ) + .isEqualTo( StringTypeDescriptor.class.getName() ); + + assertThat( processResultCollector.getJdbcTypeRegistrations() ).hasSize( 1 ); + assertThat( processResultCollector.getJdbcTypeRegistrations().get(0).getDescriptor().getClassName() ) + .isEqualTo( ClobJdbcType.class.getName() ); + + assertThat( processResultCollector.getUserTypeRegistrations() ).hasSize( 1 ); + assertThat( processResultCollector.getUserTypeRegistrations().get(0).getUserTypeClass().getClassName() ) + .isEqualTo( MyUserType.class.getName() ); + + assertThat( processResultCollector.getConverterRegistrations() ).hasSize( 1 ); + assertThat( processResultCollector.getConverterRegistrations().get(0).getConverterType().getClassName() ) + .isEqualTo( org.hibernate.type.YesNoConverter.class.getName() ); + } +} diff --git a/hibernate-models-orm/src/test/resources/mappings/globals.xml b/hibernate-models-orm/src/test/resources/mappings/globals.xml new file mode 100644 index 0000000..6aa0a17 --- /dev/null +++ b/hibernate-models-orm/src/test/resources/mappings/globals.xml @@ -0,0 +1,16 @@ + + + + + + + + + diff --git a/hibernate-models-orm/src/test/resources/mappings/simple1.xml b/hibernate-models-orm/src/test/resources/mappings/simple1.xml new file mode 100644 index 0000000..89be9ad --- /dev/null +++ b/hibernate-models-orm/src/test/resources/mappings/simple1.xml @@ -0,0 +1,28 @@ + + + + + + schema1 + catalog1 + MIXED + REMOVE,LOCK + + + + org.hibernate.models.orm.xml + + + + + + + + \ No newline at end of file diff --git a/hibernate-models-orm/src/test/resources/mappings/simple2.xml b/hibernate-models-orm/src/test/resources/mappings/simple2.xml new file mode 100644 index 0000000..07e6161 --- /dev/null +++ b/hibernate-models-orm/src/test/resources/mappings/simple2.xml @@ -0,0 +1,27 @@ + + + + + + schema2 + catalog2 + + FIELD + + + + + + + + + + + \ No newline at end of file diff --git a/hibernate-models-source/hibernate-models-source.gradle b/hibernate-models-source/hibernate-models-source.gradle index 33df2d2..033328d 100644 --- a/hibernate-models-source/hibernate-models-source.gradle +++ b/hibernate-models-source/hibernate-models-source.gradle @@ -13,6 +13,7 @@ dependencies { annotationProcessor libs.logging annotationProcessor libs.loggingAnnotations + testImplementation project( ":hibernate-models-testing" ) testImplementation project( ":hibernate-models-orm" ) testImplementation testLibs.jpa testImplementation libs.hibernateCore diff --git a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/ClassDetailsRegistryImpl.java b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/ClassDetailsRegistryImpl.java index 5d7458a..bbde7be 100644 --- a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/ClassDetailsRegistryImpl.java +++ b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/ClassDetailsRegistryImpl.java @@ -70,7 +70,8 @@ public ClassDetailsRegistryImpl(ClassDetailsBuilder fallbackClassDetailsBuilder, return subTypeClassDetailsMap.get( superTypeName ); } - @Override public void forEachDirectSubType(String superTypeName, ClassDetailsConsumer consumer) { + @Override + public void forEachDirectSubType(String superTypeName, ClassDetailsConsumer consumer) { final List directSubTypes = getDirectSubTypes( superTypeName ); if ( directSubTypes == null ) { return; @@ -106,6 +107,10 @@ public ClassDetailsRegistryImpl(ClassDetailsBuilder fallbackClassDetailsBuilder, public ClassDetails resolveClassDetails( String name, ClassDetailsBuilder creator) { + if ( "void".equals( name ) ) { + return null; + } + final ClassDetails existing = classDetailsMap.get( name ); if ( existing != null ) { return existing; diff --git a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexClassDetails.java b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexClassDetails.java index c67382a..21d79c7 100644 --- a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexClassDetails.java +++ b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexClassDetails.java @@ -153,6 +153,6 @@ public Class toJavaClass() { @Override public String toString() { - return "ClassDetails(" + classInfo.name().toString() + ")"; + return "JandexClassDetails(" + classInfo.name().toString() + ")"; } } diff --git a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexPackageDetails.java b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexPackageDetails.java new file mode 100644 index 0000000..d30395e --- /dev/null +++ b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jandex/JandexPackageDetails.java @@ -0,0 +1,64 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.source.internal.jandex; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.models.source.internal.AnnotationTargetSupport; +import org.hibernate.models.source.spi.AnnotationDescriptor; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.PackageDetails; +import org.hibernate.models.source.spi.SourceModelBuildingContext; + +import org.jboss.jandex.AnnotationTarget; +import org.jboss.jandex.ClassInfo; + +/** + * @author Steve Ebersole + */ +public class JandexPackageDetails extends AbstractAnnotationTarget implements PackageDetails, AnnotationTargetSupport { + private final ClassInfo packageInfoClassInfo; + + public JandexPackageDetails(ClassInfo packageInfoClassInfo, SourceModelBuildingContext buildingContext) { + super( buildingContext ); + this.packageInfoClassInfo = packageInfoClassInfo; + } + + @Override + protected AnnotationTarget getJandexAnnotationTarget() { + return packageInfoClassInfo; + } + + @Override + public Kind getKind() { + return Kind.PACKAGE; + } + + @Override + public String getName() { + return packageInfoClassInfo.name().toString(); + } + + @Override + public List> getAllUsages(Class annotationType) { + return getAllUsages( getBuildingContext().getAnnotationDescriptorRegistry().getDescriptor( annotationType ) ); + } + + @Override + public void forEachUsage(Class type, Consumer> consumer) { + super.forEachUsage( type, consumer ); + } + + @Override + public void forEachUsage( + AnnotationDescriptor type, + Consumer> consumer) { + super.forEachUsage( type, consumer ); + } +} diff --git a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkBuilders.java b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkBuilders.java index fa72562..2b9120b 100644 --- a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkBuilders.java +++ b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkBuilders.java @@ -133,6 +133,26 @@ public static AnnotationDescriptor< return containerDescriptor; } + public static AnnotationDescriptorImpl buildAnnotationDescriptor( + Class annotationType, + SourceModelBuildingContext buildingContext) { + final Repeatable repeatable = annotationType.getDeclaredAnnotation( Repeatable.class ); + final AnnotationDescriptor repeatableContainer; + if ( repeatable != null ) { + final Class containerClass = repeatable.value(); + //noinspection unchecked,rawtypes + repeatableContainer = buildingContext.getAnnotationDescriptorRegistry().resolveDescriptor( + containerClass, + (at) -> (AnnotationDescriptor) buildAnnotationDescriptor( containerClass, (AnnotationDescriptor) null ) + ); + } + else { + repeatableContainer = null; + } + + return buildAnnotationDescriptor( annotationType, repeatableContainer ); + } + public static AnnotationDescriptorImpl buildAnnotationDescriptor( Class annotationType, AnnotationDescriptor repeatableContainer) { diff --git a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkClassDetails.java b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkClassDetails.java index ece3da7..65ce31e 100644 --- a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkClassDetails.java +++ b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkClassDetails.java @@ -163,10 +163,6 @@ public List getMethods() { @Override public String toString() { - return "ClassDetails(reflection) {" + - " name='" + name + "'," + - " managedClass='" + managedClass.getName() + "'," + - " sys-hash-code=" + System.identityHashCode( this ) + - "}"; + return "JdkClassDetails(" + name + ")"; } } diff --git a/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkPackageDetailsImpl.java b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkPackageDetailsImpl.java new file mode 100644 index 0000000..a66a8a3 --- /dev/null +++ b/hibernate-models-source/src/main/java/org/hibernate/models/source/internal/jdk/JdkPackageDetailsImpl.java @@ -0,0 +1,51 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * SPDX-License-Identifier: Apache-2.0 + * Copyright: Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.models.source.internal.jdk; + +import java.lang.annotation.Annotation; +import java.util.List; +import java.util.function.Consumer; + +import org.hibernate.models.source.internal.AnnotationTargetSupport; +import org.hibernate.models.source.spi.AnnotationDescriptor; +import org.hibernate.models.source.spi.AnnotationUsage; +import org.hibernate.models.source.spi.PackageDetails; +import org.hibernate.models.source.spi.SourceModelBuildingContext; + +/** + * @author Steve Ebersole + */ +public class JdkPackageDetailsImpl extends AbstractAnnotationTarget implements PackageDetails, AnnotationTargetSupport { + private final Package packageInfo; + + public JdkPackageDetailsImpl(Package packageInfo, SourceModelBuildingContext buildingContext) { + super( packageInfo::getDeclaredAnnotations, buildingContext ); + this.packageInfo = packageInfo; + } + + @Override + public String getName() { + return packageInfo.getName(); + } + + @Override + public void forEachUsage( + AnnotationDescriptor type, + Consumer> consumer) { + super.forEachUsage( type, consumer ); + } + + @Override + public void forEachUsage(Class type, Consumer> consumer) { + super.forEachUsage( type, consumer ); + } + + @Override + public List> getAllUsages(Class annotationType) { + return getAllUsages( getBuildingContext().getAnnotationDescriptorRegistry().getDescriptor( annotationType ) ); + } +} diff --git a/hibernate-models-source/src/test/java/org/hibernate/models/source/Counter.java b/hibernate-models-source/src/test/java/org/hibernate/models/source/Counter.java deleted file mode 100644 index 022ea8a..0000000 --- a/hibernate-models-source/src/test/java/org/hibernate/models/source/Counter.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * SPDX-License-Identifier: Apache-2.0 - * Copyright: Red Hat Inc. and Hibernate Authors - */ -package org.hibernate.models.source; - -/** - * @author Steve Ebersole - */ -public class Counter { - private int count; - - public Counter() { - this( 0 ); - } - - public Counter(int count) { - this.count = count; - } - - public void set(int count) { - this.count = count; - } - - public int get() { - return count; - } - - public int getAndIncrement() { - return count++; - } - - public int incrementAndGet() { - return ++count; - } - - public int getAndDecrement() { - return count--; - } - - public int decrementAndGet() { - return --count; - } -} diff --git a/hibernate-models-source/src/test/java/org/hibernate/models/source/MixedSourcesTests.java b/hibernate-models-source/src/test/java/org/hibernate/models/source/MixedSourcesTests.java index 9138077..3e1a713 100644 --- a/hibernate-models-source/src/test/java/org/hibernate/models/source/MixedSourcesTests.java +++ b/hibernate-models-source/src/test/java/org/hibernate/models/source/MixedSourcesTests.java @@ -29,8 +29,8 @@ public class MixedSourcesTests { @Test void testEntityNotInJandex() { - final Index index = TestHelper.buildJandexIndex( Embeddable2.class ); - final SourceModelBuildingContextImpl buildingContext = TestHelper.createBuildingContext( + final Index index = SourceModelTestHelper.buildJandexIndex( Embeddable2.class ); + final SourceModelBuildingContextImpl buildingContext = SourceModelTestHelper.createBuildingContext( index, Entity2.class, Embeddable2.class @@ -51,8 +51,8 @@ void testEntityNotInJandex() { @Test void testEmbeddableNotInJandex() { - final Index index = TestHelper.buildJandexIndex( Entity1.class ); - final SourceModelBuildingContextImpl buildingContext = TestHelper.createBuildingContext( + final Index index = SourceModelTestHelper.buildJandexIndex( Entity1.class ); + final SourceModelBuildingContextImpl buildingContext = SourceModelTestHelper.createBuildingContext( index, Entity1.class, Embeddable1.class diff --git a/hibernate-models-testing/hibernate-models-testing.gradle b/hibernate-models-testing/hibernate-models-testing.gradle new file mode 100644 index 0000000..086347a --- /dev/null +++ b/hibernate-models-testing/hibernate-models-testing.gradle @@ -0,0 +1,12 @@ +apply from: rootProject.file( "gradle/java-module.gradle" ) + +dependencies { + implementation project( ":hibernate-models-common" ) + implementation project( ":hibernate-models-source" ) + implementation project( ":hibernate-models-orm" ) + implementation libs.jandex + + testImplementation testLibs.jpa + testImplementation libs.hibernateCore + testRuntimeOnly testLibs.log4j +} diff --git a/hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelper.java b/hibernate-models-testing/src/main/java/org/hibernate/models/source/SourceModelTestHelper.java similarity index 99% rename from hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelper.java rename to hibernate-models-testing/src/main/java/org/hibernate/models/source/SourceModelTestHelper.java index a24287c..5d097ac 100644 --- a/hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelper.java +++ b/hibernate-models-testing/src/main/java/org/hibernate/models/source/SourceModelTestHelper.java @@ -31,7 +31,7 @@ /** * @author Steve Ebersole */ -public class TestHelper { +public class SourceModelTestHelper { public static SourceModelBuildingContext createBuildingContext(Class... modelClasses) { return createBuildingContext( SIMPLE_CLASS_LOADING, modelClasses ); diff --git a/hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelperSmokeTest.java b/hibernate-models-testing/src/test/java/org/hibernate/models/source/SourceModelTestHelperSmokeTests.java similarity index 90% rename from hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelperSmokeTest.java rename to hibernate-models-testing/src/test/java/org/hibernate/models/source/SourceModelTestHelperSmokeTests.java index f7e41dc..4eb7f76 100644 --- a/hibernate-models-source/src/test/java/org/hibernate/models/source/TestHelperSmokeTest.java +++ b/hibernate-models-testing/src/test/java/org/hibernate/models/source/SourceModelTestHelperSmokeTests.java @@ -8,6 +8,7 @@ import java.util.List; +import org.hibernate.internal.util.MutableInteger; import org.hibernate.models.source.internal.jandex.JandexClassDetails; import org.hibernate.models.source.spi.AnnotationDescriptor; import org.hibernate.models.source.spi.AnnotationUsage; @@ -23,6 +24,7 @@ import jakarta.persistence.Inheritance; import jakarta.persistence.NamedQuery; import jakarta.persistence.Table; +import org.assertj.core.api.Assertions; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.fail; @@ -30,10 +32,10 @@ /** * @author Steve Ebersole */ -public class TestHelperSmokeTest { +public class SourceModelTestHelperSmokeTests { @Test void testIt() { - final SourceModelBuildingContext buildingContext = TestHelper.createBuildingContext( AnEntity.class ); + final SourceModelBuildingContext buildingContext = SourceModelTestHelper.createBuildingContext( AnEntity.class ); final AnnotationDescriptor entityAnnDescriptor = buildingContext .getAnnotationDescriptorRegistry() @@ -83,9 +85,9 @@ void testIt() { final AnnotationUsage queryTwo = classDetails.getNamedUsage( NamedQuery.class, "two", "name" ); assertThat( queryTwo ).isNotNull(); - final Counter counter = new Counter(); + final MutableInteger counter = new MutableInteger(); classDetails.forEachUsage( NamedQuery.class, (usage) -> counter.incrementAndGet() ); - assertThat( counter.get() ).isEqualTo( 2 ); + Assertions.assertThat( counter.get() ).isEqualTo( 2 ); } @Entity(name="AnEntity") diff --git a/settings.gradle b/settings.gradle index d5f79c9..dbac749 100644 --- a/settings.gradle +++ b/settings.gradle @@ -81,6 +81,7 @@ dependencyResolutionManagement { include "hibernate-models-common" include "hibernate-models-source" include "hibernate-models-orm" +include "hibernate-models-testing" rootProject.children.each { project -> project.buildFileName = "${project.name}.gradle"