HHH-18534 - Remove the org.hibernate.boot.models.categorize package

This commit is contained in:
Steve Ebersole 2024-11-22 13:26:49 -06:00
parent f65f393a01
commit ec5bbe4546
56 changed files with 118 additions and 4135 deletions

View File

@ -59,7 +59,7 @@ import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext;
import org.hibernate.boot.models.categorize.internal.ClassLoaderServiceLoading;
import org.hibernate.boot.models.internal.ClassLoaderServiceLoading;
import org.hibernate.boot.models.internal.GlobalRegistrationsImpl;
import org.hibernate.boot.models.internal.ModelsHelper;
import org.hibernate.boot.models.spi.GlobalRegistrations;

View File

@ -230,7 +230,8 @@ public class MetadataBuildingProcess {
return metadataCollector.buildMetadataInstance( rootMetadataBuildingContext );
}
private static void coordinateProcessors(
@Internal
public static void coordinateProcessors(
ManagedResources managedResources,
MetadataBuildingOptions options,
MetadataBuildingContextRootImpl rootMetadataBuildingContext,

View File

@ -0,0 +1,21 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
/**
* An enum defining the nature (categorization) of a persistent attribute.
*
* @see jakarta.persistence.metamodel.Attribute.PersistentAttributeType
*/
public enum AttributeNature {
BASIC,
EMBEDDED,
ANY,
TO_ONE,
ELEMENT_COLLECTION,
MANY_TO_ANY,
MANY_TO_MANY,
ONE_TO_MANY
}

View File

@ -1,21 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Indicates that the class is copied from hibernate-core and should ultimately use that one
*
* @author Steve Ebersole
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Copied {
Class value() default void.class;
}

View File

@ -2,7 +2,7 @@
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
package org.hibernate.boot.models;
/**
* JPA defines 2 ways events callbacks can happen...

View File

@ -6,20 +6,21 @@ package org.hibernate.boot.models;
import java.util.EnumSet;
import org.hibernate.MappingException;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.AnnotationException;
import org.hibernate.Incubating;
/**
* Condition where an attribute indicates multiple {@linkplain AttributeMetadata.AttributeNature natures}
* Condition where an attribute indicates multiple {@linkplain AttributeNature natures}
*
* @author Steve Ebersole
*/
public class MultipleAttributeNaturesException extends MappingException {
@Incubating
public class MultipleAttributeNaturesException extends AnnotationException {
private final String attributeName;
public MultipleAttributeNaturesException(
String attributeName,
EnumSet<AttributeMetadata.AttributeNature> natures) {
EnumSet<AttributeNature> natures) {
super( craftMessage( attributeName, natures ) );
this.attributeName = attributeName;
}
@ -28,12 +29,12 @@ public class MultipleAttributeNaturesException extends MappingException {
return attributeName;
}
private static String craftMessage(String attributeName, EnumSet<AttributeMetadata.AttributeNature> natures) {
private static String craftMessage(String attributeName, EnumSet<AttributeNature> natures) {
final StringBuilder buffer = new StringBuilder( "Attribute `" )
.append( attributeName )
.append( "` expressed multiple natures [" );
String separator = "";
for ( AttributeMetadata.AttributeNature nature : natures ) {
for ( AttributeNature nature : natures ) {
buffer.append( separator );
buffer.append( nature.name() );
separator = ",";

View File

@ -1,21 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize;
import org.hibernate.Internal;
import org.jboss.logging.Logger;
/**
* todo : find the proper min/max id range
*
* @author Steve Ebersole
*/
@Internal
public interface ModelCategorizationLogging {
String NAME = "org.hibernate.models.orm";
Logger MODEL_CATEGORIZATION_LOGGER = Logger.getLogger( NAME );
}

View File

@ -1,244 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;
import org.hibernate.boot.models.categorize.spi.ClassAttributeAccessType;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.IdentifiableTypeMetadata;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.categorize.spi.MappedSuperclassTypeMetadata;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import jakarta.persistence.AccessType;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.ExcludeDefaultListeners;
import jakarta.persistence.ExcludeSuperclassListeners;
/**
* @author Steve Ebersole
*/
public abstract class AbstractIdentifiableTypeMetadata
extends AbstractManagedTypeMetadata
implements IdentifiableTypeMetadata {
private final EntityHierarchy hierarchy;
private final IdentifiableTypeMetadata superType;
private final Set<IdentifiableTypeMetadata> subTypes = new HashSet<>();
private final ClassAttributeAccessType classLevelAccessType;
/**
* Used when creating the hierarchy root-root
*
* @param implicitAccessType This is the hierarchy default
*/
public AbstractIdentifiableTypeMetadata(
ClassDetails classDetails,
EntityHierarchy hierarchy,
MappedSuperclassTypeMetadata superTypeMetadata,
AccessType implicitAccessType,
ModelCategorizationContext processingContext) {
super( classDetails, processingContext );
this.hierarchy = hierarchy;
this.superType = superTypeMetadata;
this.classLevelAccessType = CategorizationHelper.determineAccessType( classDetails, implicitAccessType );
}
public AbstractIdentifiableTypeMetadata(
ClassDetails classDetails,
EntityHierarchy hierarchy,
IdentifiableTypeMetadata superType,
ModelCategorizationContext processingContext) {
super( classDetails, processingContext );
assert superType != null;
this.hierarchy = hierarchy;
this.superType = superType;
// this is arguably more logical, but the specification is very clear that this should come
// from the hierarchy default not the super in section _2.3.2 Explicit Access Type_
//this.accessType = CategorizationHelper.determineAccessType( classDetails, superType.getAccessType() );
this.classLevelAccessType = CategorizationHelper.determineAccessType( classDetails, hierarchy.getDefaultAccessType() );
}
protected void postInstantiate(boolean rootEntityOrSubclass, HierarchyTypeConsumer typeConsumer) {
typeConsumer.acceptType( this );
// now we can effectively walk subs, although we skip that for the mapped-superclasses
// "above" the root entity
if ( rootEntityOrSubclass ) {
walkSubclasses( typeConsumer );
}
// the idea here is to collect up class-level annotations and to apply
// the maps from supers
collectConversionInfo();
collectAttributeOverrides();
collectAssociationOverrides();
}
private void walkSubclasses(HierarchyTypeConsumer typeConsumer) {
walkSubclasses( getClassDetails(), typeConsumer );
}
private void walkSubclasses(ClassDetails base, HierarchyTypeConsumer typeConsumer) {
final ClassDetailsRegistry classDetailsRegistry = getModelContext().getClassDetailsRegistry();
classDetailsRegistry.forEachDirectSubType( base.getName(), (subClassDetails) -> {
final AbstractIdentifiableTypeMetadata subTypeMetadata;
if ( CategorizationHelper.isEntity( subClassDetails ) ) {
subTypeMetadata = new EntityTypeMetadataImpl(
subClassDetails,
getHierarchy(),
this,
typeConsumer,
getModelContext()
);
addSubclass( subTypeMetadata );
}
else if ( CategorizationHelper.isMappedSuperclass( subClassDetails ) ) {
subTypeMetadata = new MappedSuperclassTypeMetadataImpl(
subClassDetails,
getHierarchy(),
this,
typeConsumer,
getModelContext()
);
addSubclass( subTypeMetadata );
}
else {
// skip over "intermediate" sub-types
walkSubclasses( subClassDetails, typeConsumer );
}
} );
}
protected void addSubclass(IdentifiableTypeMetadata subclass) {
subTypes.add( subclass );
}
@Override
public EntityHierarchy getHierarchy() {
return hierarchy;
}
@Override
public IdentifiableTypeMetadata getSuperType() {
return superType;
}
@Override
public boolean isAbstract() {
return getClassDetails().isAbstract();
}
@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<IdentifiableTypeMetadata> consumer) {
// assume this is called only after its constructor is complete
subTypes.forEach( consumer );
}
@Override
public Iterable<IdentifiableTypeMetadata> getSubTypes() {
// assume this is called only after its constructor is complete
return subTypes;
}
@Override
public ClassAttributeAccessType getClassLevelAccessType() {
return classLevelAccessType;
}
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
}
protected List<JpaEventListener> collectHierarchyEventListeners(JpaEventListener localCallback) {
final ClassDetails classDetails = getClassDetails();
final List<JpaEventListener> combined = new ArrayList<>();
if ( !classDetails.hasDirectAnnotationUsage( ExcludeSuperclassListeners.class ) ) {
final IdentifiableTypeMetadata superType = getSuperType();
if ( superType != null ) {
combined.addAll( superType.getHierarchyJpaEventListeners() );
}
}
applyLocalEventListeners( combined::add );
if ( localCallback != null ) {
combined.add( localCallback );
}
return combined;
}
private void applyLocalEventListeners(Consumer<JpaEventListener> consumer) {
final ClassDetails classDetails = getClassDetails();
final EntityListeners entityListenersAnnotation = classDetails.getDirectAnnotationUsage( EntityListeners.class );
if ( entityListenersAnnotation == null ) {
return;
}
final Class<?>[] entityListenerClasses = entityListenersAnnotation.value();
if ( CollectionHelper.isEmpty( entityListenerClasses ) ) {
return;
}
ArrayHelper.forEach( entityListenerClasses, (listenerClass) -> {
consumer.accept( JpaEventListener.from(
JpaEventListenerStyle.LISTENER,
getModelContext().getClassDetailsRegistry().findClassDetails( listenerClass.getName() )
) );
} );
}
protected List<JpaEventListener> collectCompleteEventListeners(ModelCategorizationContext modelContext) {
final ClassDetails classDetails = getClassDetails();
if ( classDetails.hasDirectAnnotationUsage( ExcludeDefaultListeners.class ) ) {
return getHierarchyJpaEventListeners();
}
final List<JpaEventListener> combined = new ArrayList<>();
combined.addAll( modelContext.getDefaultEventListeners() );
combined.addAll( getHierarchyJpaEventListeners() );
return combined;
}
}

View File

@ -1,176 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
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.boot.models.categorize.spi.AllMemberConsumer;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.ManagedTypeMetadata;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import static org.hibernate.internal.util.collections.CollectionHelper.arrayList;
/**
* 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 ModelCategorizationContext 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, ModelCategorizationContext 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,
ModelCategorizationContext modelContext) {
this.classDetails = classDetails;
this.modelContext = modelContext;
this.attributeRoleBase = attributeRoleBase;
this.attributePathBase = attributePathBase;
}
public ClassDetails getClassDetails() {
return classDetails;
}
public ModelCategorizationContext 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<AttributeMetadata> attributeList();
@Override
public int getNumberOfAttributes() {
return attributeList().size();
}
@Override
public Collection<AttributeMetadata> getAttributes() {
return attributeList();
}
@Override
public AttributeMetadata findAttribute(String name) {
final List<AttributeMetadata> attributeList = attributeList();
for ( int i = 0; i < attributeList.size(); i++ ) {
final AttributeMetadata attribute = attributeList.get( i );
if ( attribute.getName().equals( name ) ) {
return attribute;
}
}
return null;
}
@Override
public void forEachAttribute(IndexedConsumer<AttributeMetadata> consumer) {
for ( int i = 0; i < attributeList().size(); i++ ) {
consumer.accept( i, attributeList().get( i ) );
}
}
protected List<AttributeMetadata> resolveAttributes(AllMemberConsumer memberConsumer) {
final List<MemberDetails> backingMembers = getModelContext()
.getPersistentAttributeMemberResolver()
.resolveAttributesMembers( classDetails, getClassLevelAccessType(), memberConsumer );
final List<AttributeMetadata> attributeList = arrayList( backingMembers.size() );
for ( MemberDetails backingMember : backingMembers ) {
final AttributeMetadata attribute = new AttributeMetadataImpl(
backingMember.resolveAttributeName(),
CategorizationHelper.determineAttributeNature( classDetails, backingMember ),
backingMember
);
attributeList.add( attribute );
}
return attributeList;
}
// @Override
// public <A extends Annotation> List<AnnotationUsage<A>> findAnnotations(AnnotationDescriptor<A> type) {
// return classDetails.getAnnotations( type );
// }
//
// @Override
// public <A extends Annotation> void forEachAnnotation(AnnotationDescriptor<A> type, Consumer<AnnotationUsage<A>> 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;
}
}

View File

@ -1,99 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.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.boot.models.categorize.spi.AllMemberConsumer;
import org.hibernate.boot.models.categorize.spi.ClassAttributeAccessType;
import org.hibernate.boot.models.categorize.spi.PersistentAttributeMemberResolver;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.MethodDetails;
import jakarta.persistence.Transient;
/**
* "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.
* <p/>
* 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
*/
protected abstract List<MemberDetails> resolveAttributesMembers(
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
ClassAttributeAccessType classLevelAccessType);
@Override
public List<MemberDetails> resolveAttributesMembers(
ClassDetails classDetails,
ClassAttributeAccessType classLevelAccessType,
AllMemberConsumer memberConsumer) {
final Set<FieldDetails> transientFields = new HashSet<>();
final Set<MethodDetails> transientMethods = new HashSet<>();
collectMembersMarkedTransient(
transientFields::add,
transientMethods::add,
classDetails,
memberConsumer
);
return resolveAttributesMembers(
transientFields::contains,
transientMethods::contains,
classDetails,
classLevelAccessType
);
}
protected void collectMembersMarkedTransient(
final Consumer<FieldDetails> transientFieldConsumer,
final Consumer<MethodDetails> transientMethodConsumer,
ClassDetails classDetails,
AllMemberConsumer memberConsumer) {
final List<FieldDetails> fields = classDetails.getFields();
for ( int i = 0; i < fields.size(); i++ ) {
final FieldDetails fieldDetails = fields.get( i );
memberConsumer.acceptMember( fieldDetails );
if ( fieldDetails.hasDirectAnnotationUsage( Transient.class ) ) {
transientFieldConsumer.accept( fieldDetails );
}
}
final List<MethodDetails> methods = classDetails.getMethods();
for ( int i = 0; i < methods.size(); i++ ) {
final MethodDetails methodDetails = methods.get( i );
memberConsumer.acceptMember( methodDetails );
if ( methodDetails.hasDirectAnnotationUsage( Transient.class ) ) {
transientMethodConsumer.accept( methodDetails );
}
}
}
}

View File

@ -1,26 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.categorize.spi.AggregatedKeyMapping;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
/**
* @author Steve Ebersole
*/
public class AggregatedKeyMappingImpl implements AggregatedKeyMapping {
private final AttributeMetadata attribute;
public AggregatedKeyMappingImpl(AttributeMetadata attribute) {
this.attribute = attribute;
}
@Override
public AttributeMetadata getAttribute() {
return attribute;
}
}

View File

@ -1,43 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.models.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 + "`)";
}
}

View File

@ -1,24 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.BasicKeyMapping;
/**
* @author Steve Ebersole
*/
public class BasicKeyMappingImpl implements BasicKeyMapping {
private final AttributeMetadata attribute;
public BasicKeyMappingImpl(AttributeMetadata attribute) {
this.attribute = attribute;
}
@Override
public AttributeMetadata getAttribute() {
return attribute;
}
}

View File

@ -1,234 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.EnumSet;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyDiscriminator;
import org.hibernate.annotations.AnyKeyJavaClass;
import org.hibernate.annotations.AnyKeyJavaType;
import org.hibernate.annotations.AnyKeyJdbcType;
import org.hibernate.annotations.AnyKeyJdbcTypeCode;
import org.hibernate.annotations.CompositeType;
import org.hibernate.annotations.EmbeddableInstantiator;
import org.hibernate.annotations.JavaType;
import org.hibernate.annotations.JdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.Nationalized;
import org.hibernate.annotations.TenantId;
import org.hibernate.annotations.TimeZoneColumn;
import org.hibernate.annotations.TimeZoneStorage;
import org.hibernate.annotations.Type;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.MultipleAttributeNaturesException;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.ClassAttributeAccessType;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Basic;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Temporal;
import jakarta.persistence.Version;
import static org.hibernate.boot.models.categorize.ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.BASIC;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.ELEMENT_COLLECTION;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.EMBEDDED;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.MANY_TO_ANY;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.MANY_TO_MANY;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.ONE_TO_MANY;
/**
* @author Steve Ebersole
*/
public class CategorizationHelper {
public static ClassDetails toClassDetails(Class<?> clazz, ClassDetailsRegistry classDetailsRegistry) {
return classDetailsRegistry.resolveClassDetails( clazz.getName() );
}
public static boolean isMappedSuperclass(ClassDetails classDetails) {
return classDetails.hasDirectAnnotationUsage( MappedSuperclass.class );
}
public static boolean isEntity(ClassDetails classDetails) {
return classDetails.getDirectAnnotationUsage( Entity.class ) != null;
}
public static boolean isIdentifiable(ClassDetails classDetails) {
return isEntity( classDetails ) || isMappedSuperclass( classDetails );
}
public static ClassAttributeAccessType determineAccessType(ClassDetails classDetails, AccessType implicitAccessType) {
final Access annotation = classDetails.getDirectAnnotationUsage( Access.class );
if ( annotation != null ) {
final AccessType explicitValue = annotation.value();
assert explicitValue != null;
return explicitValue == AccessType.FIELD
? ClassAttributeAccessType.EXPLICIT_FIELD
: ClassAttributeAccessType.EXPLICIT_PROPERTY;
}
return implicitAccessType == AccessType.FIELD
? ClassAttributeAccessType.IMPLICIT_FIELD
: ClassAttributeAccessType.IMPLICIT_PROPERTY;
}
/**
* Determine the attribute's nature - is it a basic mapping, an embeddable, ...?
* </p>
* Also performs some simple validation around multiple natures being indicated
*/
public static AttributeMetadata.AttributeNature determineAttributeNature(ClassDetails declarer, MemberDetails backingMember) {
final EnumSet<AttributeMetadata.AttributeNature> natures = EnumSet.noneOf( AttributeMetadata.AttributeNature.class );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// first, look for explicit nature annotations
final Any any = backingMember.getDirectAnnotationUsage( Any.class );
final Basic basic = backingMember.getDirectAnnotationUsage( Basic.class );
final ElementCollection elementCollection = backingMember.getDirectAnnotationUsage( ElementCollection.class );
final Embedded embedded = backingMember.getDirectAnnotationUsage( Embedded.class );
final EmbeddedId embeddedId = backingMember.getDirectAnnotationUsage( EmbeddedId.class );
final ManyToAny manyToAny = backingMember.getDirectAnnotationUsage( ManyToAny.class );
final ManyToMany manyToMany = backingMember.getDirectAnnotationUsage( ManyToMany.class );
final ManyToOne manyToOne = backingMember.getDirectAnnotationUsage( ManyToOne.class );
final OneToMany oneToMany = backingMember.getDirectAnnotationUsage( OneToMany.class );
final OneToOne oneToOne = backingMember.getDirectAnnotationUsage( OneToOne.class );
if ( basic != null ) {
natures.add( AttributeMetadata.AttributeNature.BASIC );
}
if ( embedded != null
|| embeddedId != null
|| ( backingMember.getType() != null && backingMember.getType().determineRawClass().hasDirectAnnotationUsage( Embeddable.class ) ) ) {
natures.add( EMBEDDED );
}
if ( any != null ) {
natures.add( AttributeMetadata.AttributeNature.ANY );
}
if ( oneToOne != null || manyToOne != null ) {
natures.add( AttributeMetadata.AttributeNature.TO_ONE );
}
if ( elementCollection != null ) {
natures.add( ELEMENT_COLLECTION );
}
if ( oneToMany != null ) {
natures.add( ONE_TO_MANY );
}
if ( manyToMany != null ) {
natures.add( MANY_TO_MANY );
}
if ( manyToAny != null ) {
natures.add( MANY_TO_ANY );
}
// look at annotations that imply a nature
final boolean plural = oneToMany != null
|| manyToMany != null
|| elementCollection != null
|| manyToAny != null;
final boolean implicitlyBasic = backingMember.hasDirectAnnotationUsage( Temporal.class )
|| backingMember.hasDirectAnnotationUsage( Lob.class )
|| backingMember.hasDirectAnnotationUsage( Enumerated.class )
|| backingMember.hasDirectAnnotationUsage( Version.class )
|| backingMember.hasDirectAnnotationUsage( HibernateAnnotations.GENERATED.getAnnotationType() )
|| backingMember.hasDirectAnnotationUsage( Nationalized.class )
|| backingMember.hasDirectAnnotationUsage( TimeZoneColumn.class )
|| backingMember.hasDirectAnnotationUsage( TimeZoneStorage.class )
|| backingMember.hasDirectAnnotationUsage( Type.class )
|| backingMember.hasDirectAnnotationUsage( TenantId.class )
|| backingMember.hasDirectAnnotationUsage( JavaType.class )
|| backingMember.hasDirectAnnotationUsage( JdbcType.class )
|| backingMember.hasDirectAnnotationUsage( JdbcTypeCode.class );
final boolean implicitlyEmbedded = backingMember.hasDirectAnnotationUsage( EmbeddableInstantiator.class )
|| backingMember.hasDirectAnnotationUsage( CompositeType.class );
final boolean implicitlyAny = backingMember.hasDirectAnnotationUsage( AnyDiscriminator.class )
// || CollectionHelper.isNotEmpty( backingMember.getRepeatedAnnotationUsages( HibernateAnnotations.ANY_DISCRIMINATOR_VALUE ) )
// || backingMember.hasDirectAnnotationUsage( HibernateAnnotations.ANY_DISCRIMINATOR_VALUES )
|| backingMember.hasDirectAnnotationUsage( AnyKeyJavaType.class )
|| backingMember.hasDirectAnnotationUsage( AnyKeyJavaClass.class )
|| backingMember.hasDirectAnnotationUsage( AnyKeyJdbcType.class )
|| backingMember.hasDirectAnnotationUsage( AnyKeyJdbcTypeCode.class );
if ( !plural ) {
// first implicit basic nature
if ( implicitlyBasic ) {
natures.add( AttributeMetadata.AttributeNature.BASIC );
}
// then embedded
if ( implicitlyEmbedded ) {
natures.add( EMBEDDED );
}
// and any
if ( implicitlyAny ) {
natures.add( AttributeMetadata.AttributeNature.ANY );
}
}
else {
if ( elementCollection != null ) {
// for @ElementCollection, allow `@Basic` or `@Embedded` (though not both)
if ( natures.contains( BASIC ) ) {
if ( natures.contains( EMBEDDED ) ) {
// don't do anything, this is still an error
}
else {
MODEL_CATEGORIZATION_LOGGER.debugf( "Ignoring @Basic on @ElementCollection - %s", backingMember.resolveAttributeName() );
natures.remove( BASIC );
}
}
else if ( natures.contains( EMBEDDED ) ) {
MODEL_CATEGORIZATION_LOGGER.debugf( "Ignoring @Embedded on @ElementCollection - %s", backingMember.resolveAttributeName() );
natures.remove( EMBEDDED );
}
}
}
int size = natures.size();
return switch ( size ) {
case 0 -> {
MODEL_CATEGORIZATION_LOGGER.debugf(
"Implicitly interpreting attribute `%s` as BASIC",
backingMember.resolveAttributeName()
);
yield AttributeMetadata.AttributeNature.BASIC;
}
case 1 -> natures.iterator().next();
default -> throw new MultipleAttributeNaturesException(
declarer.getName() + "#" + backingMember.resolveAttributeName(),
natures
);
};
}
}

View File

@ -1,81 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.Map;
import java.util.Set;
import org.hibernate.boot.models.categorize.spi.CategorizedDomainModel;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.spi.GlobalRegistrations;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
/**
* @author Steve Ebersole
*/
public class CategorizedDomainModelImpl implements CategorizedDomainModel {
private final Set<EntityHierarchy> entityHierarchies;
private final Map<String, ClassDetails> mappedSuperclasses;
private final Map<String, ClassDetails> embeddables;
private final GlobalRegistrations globalRegistrations;
private final ClassDetailsRegistry classDetailsRegistry;
private final AnnotationDescriptorRegistry annotationDescriptorRegistry;
private final PersistenceUnitMetadata persistenceUnitMetadata;
public CategorizedDomainModelImpl(
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry annotationDescriptorRegistry,
PersistenceUnitMetadata persistenceUnitMetadata,
Set<EntityHierarchy> entityHierarchies,
Map<String, ClassDetails> mappedSuperclasses,
Map<String, ClassDetails> embeddables,
GlobalRegistrations globalRegistrations) {
this.classDetailsRegistry = classDetailsRegistry;
this.annotationDescriptorRegistry = annotationDescriptorRegistry;
this.persistenceUnitMetadata = persistenceUnitMetadata;
this.entityHierarchies = entityHierarchies;
this.mappedSuperclasses = mappedSuperclasses;
this.embeddables = embeddables;
this.globalRegistrations = globalRegistrations;
}
@Override
public ClassDetailsRegistry getClassDetailsRegistry() {
return classDetailsRegistry;
}
@Override
public AnnotationDescriptorRegistry getAnnotationDescriptorRegistry() {
return annotationDescriptorRegistry;
}
@Override
public PersistenceUnitMetadata getPersistenceUnitMetadata() {
return persistenceUnitMetadata;
}
@Override
public GlobalRegistrations getGlobalRegistrations() {
return globalRegistrations;
}
@Override
public Set<EntityHierarchy> getEntityHierarchies() {
return entityHierarchies;
}
public Map<String, ClassDetails> getMappedSuperclasses() {
return mappedSuperclasses;
}
@Override
public Map<String, ClassDetails> getEmbeddables() {
return embeddables;
}
}

View File

@ -1,238 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.IdentifiableTypeMetadata;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MethodDetails;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
/**
* 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 table context, giving access to needed services and information
*
* @return a set of {@code EntityHierarchySource} instances.
*/
public static Set<EntityHierarchy> createEntityHierarchies(
Set<ClassDetails> rootEntities,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext 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 table context, giving access to needed services and information
*
* @return a set of {@code EntityHierarchySource} instances.
*/
public static Set<EntityHierarchy> createEntityHierarchies(
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext buildingContext) {
return createEntityHierarchies(
collectRootEntityTypes( buildingContext.getClassDetailsRegistry() ),
typeConsumer,
buildingContext
);
}
private final ModelCategorizationContext modelContext;
public EntityHierarchyBuilder(ModelCategorizationContext modelContext) {
this.modelContext = modelContext;
}
private Set<EntityHierarchy> process(
Set<ClassDetails> rootEntities,
HierarchyTypeConsumer typeConsumer) {
final Set<EntityHierarchy> hierarchies = CollectionHelper.setOfSize( rootEntities.size() );
rootEntities.forEach( (rootEntity) -> {
final AccessType defaultAccessType = determineDefaultAccessTypeForHierarchy( rootEntity );
hierarchies.add( new EntityHierarchyImpl(
rootEntity,
defaultAccessType,
org.hibernate.cache.spi.access.AccessType.TRANSACTIONAL,
typeConsumer,
modelContext
) );
} );
return hierarchies;
}
private AccessType determineDefaultAccessTypeForHierarchy(ClassDetails rootEntityType) {
assert rootEntityType != null;
// // look for `@Access` at class level
// final AccessType classAnnotationValue = resolveDefaultAccessTypeFromClassAnnotation( rootEntityType );
// if ( classAnnotationValue != null ) {
// return classAnnotationValue;
// }
// look for `@Id` or `@EmbeddedId`
// todo (jpa32) : technically we could probably look for member with any "mapping" annotation
final AccessType accessFromAttribute = resolveDefaultAccessTypeFromMembers( rootEntityType );
if ( accessFromAttribute != null ) {
return accessFromAttribute;
}
// // 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 );
return null;
}
private AccessType resolveDefaultAccessTypeFromClassAnnotation(ClassDetails rootEntityType) {
ClassDetails current = rootEntityType;
while ( current != null ) {
final Access accessAnnotation = current.getDirectAnnotationUsage( Access.class );
if ( accessAnnotation != null ) {
return accessAnnotation.value();
}
current = current.getSuperClass();
}
return null;
}
private AccessType resolveDefaultAccessTypeFromMembers(ClassDetails rootEntityType) {
ClassDetails current = rootEntityType;
while ( current != null ) {
// look for `@Id` or `@EmbeddedId` (w/o `@Access`)
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.getSuperClass();
}
return null;
}
private AnnotationTarget determineIdMember(ClassDetails current) {
final List<MethodDetails> methods = current.getMethods();
for ( int i = 0; i < methods.size(); i++ ) {
final MethodDetails methodDetails = methods.get( i );
if ( methodDetails.hasDirectAnnotationUsage( Id.class )
|| methodDetails.hasDirectAnnotationUsage( EmbeddedId.class ) ) {
if ( methodDetails.getDirectAnnotationUsage( Access.class ) == null ) {
return methodDetails;
}
}
}
final List<FieldDetails> fields = current.getFields();
for ( int i = 0; i < fields.size(); i++ ) {
final FieldDetails fieldDetails = fields.get( i );
if ( fieldDetails.hasDirectAnnotationUsage( Id.class )
|| fieldDetails.hasDirectAnnotationUsage( EmbeddedId.class ) ) {
if ( fieldDetails.getDirectAnnotationUsage( Access.class ) == null ) {
return fieldDetails;
}
}
}
return null;
}
private Set<ClassDetails> collectRootEntityTypes() {
return collectRootEntityTypes( modelContext.getClassDetailsRegistry() );
}
private static Set<ClassDetails> collectRootEntityTypes(ClassDetailsRegistry classDetailsRegistry) {
final Set<ClassDetails> collectedTypes = new HashSet<>();
classDetailsRegistry.forEachClassDetails( (managedType) -> {
if ( managedType.getDirectAnnotationUsage( Entity.class ) != 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.getSuperClass() == null ) {
return true;
}
ClassDetails current = classInfo.getSuperClass();
while ( current != null ) {
if ( current.getDirectAnnotationUsage( Entity.class ) != null && !current.isAbstract() ) {
// a non-abstract super type has `@Entity` -> classInfo cannot be a root entity
return false;
}
current = current.getSuperClass();
}
// if we hit no opt-outs we have a root
return true;
}
/**
* Used in tests
*/
public static Set<EntityHierarchy> createEntityHierarchies(ModelCategorizationContext processingContext) {
return new EntityHierarchyBuilder( processingContext ).process(
collectRootEntityTypes( processingContext.getClassDetailsRegistry() ),
EntityHierarchyBuilder::ignore
);
}
private static void ignore(IdentifiableTypeMetadata it) {}
}

View File

@ -1,318 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.Locale;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.boot.models.categorize.ModelCategorizationLogging;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.CacheRegion;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.models.categorize.spi.IdentifiableTypeMetadata;
import org.hibernate.boot.models.categorize.spi.KeyMapping;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.boot.models.categorize.spi.NaturalIdCacheRegion;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.models.spi.ClassDetails;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
/**
*
* @author Steve Ebersole
*/
public class EntityHierarchyImpl implements EntityHierarchy {
private final IdentifiableTypeMetadata absoluteRootTypeMetadata;
private final EntityTypeMetadata rootEntityTypeMetadata;
private final InheritanceType inheritanceType;
private final jakarta.persistence.AccessType defaultAccessType;
private final OptimisticLockStyle optimisticLockStyle;
private final KeyMapping idMapping;
private final KeyMapping naturalIdMapping;
private final AttributeMetadata versionAttribute;
private final AttributeMetadata tenantIdAttribute;
private final CacheRegion cacheRegion;
private final NaturalIdCacheRegion naturalIdCacheRegion;
public EntityHierarchyImpl(
ClassDetails rootEntityClassDetails,
jakarta.persistence.AccessType defaultAccessType,
AccessType defaultCacheAccessType,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext categorizationContext) {
this.defaultAccessType = defaultAccessType;
final ClassDetails absoluteRootClassDetails = findRootRoot( rootEntityClassDetails );
final HierarchyMetadataCollector metadataCollector = new HierarchyMetadataCollector(
this,
rootEntityClassDetails,
typeConsumer,
categorizationContext
);
if ( CategorizationHelper.isEntity( absoluteRootClassDetails ) ) {
this.rootEntityTypeMetadata = new EntityTypeMetadataImpl(
absoluteRootClassDetails,
this,
defaultAccessType,
metadataCollector,
categorizationContext
);
this.absoluteRootTypeMetadata = rootEntityTypeMetadata;
}
else {
assert CategorizationHelper.isMappedSuperclass( absoluteRootClassDetails );
this.absoluteRootTypeMetadata = processRootMappedSuperclasses(
absoluteRootClassDetails,
this,
defaultAccessType,
metadataCollector,
categorizationContext
);
this.rootEntityTypeMetadata = new EntityTypeMetadataImpl(
rootEntityClassDetails,
this,
(AbstractIdentifiableTypeMetadata) absoluteRootTypeMetadata,
metadataCollector,
categorizationContext
);
}
this.inheritanceType = determineInheritanceType( metadataCollector );
this.optimisticLockStyle = determineOptimisticLockStyle( metadataCollector );
this.idMapping = metadataCollector.getIdMapping();
this.naturalIdMapping = metadataCollector.getNaturalIdMapping();
this.versionAttribute = metadataCollector.getVersionAttribute();
this.tenantIdAttribute = metadataCollector.getTenantIdAttribute();
this.cacheRegion = determineCacheRegion( metadataCollector, defaultCacheAccessType );
this.naturalIdCacheRegion = determineNaturalIdCacheRegion( metadataCollector, cacheRegion );
}
private static IdentifiableTypeMetadata processRootMappedSuperclasses(
ClassDetails absoluteRootClassDetails,
EntityHierarchyImpl entityHierarchy,
jakarta.persistence.AccessType defaultAccessType,
HierarchyMetadataCollector metadataCollector,
ModelCategorizationContext modelBuildingContext) {
return new MappedSuperclassTypeMetadataImpl(
absoluteRootClassDetails,
entityHierarchy,
null,
defaultAccessType,
metadataCollector,
modelBuildingContext
);
}
private ClassDetails findRootRoot(ClassDetails rootEntityClassDetails) {
if ( rootEntityClassDetails.getSuperClass() != null ) {
final ClassDetails match = walkSupers( rootEntityClassDetails.getSuperClass() );
if ( match != null ) {
return match;
}
}
return rootEntityClassDetails;
}
private ClassDetails walkSupers(ClassDetails type) {
assert type != null;
if ( type.getSuperClass() != null ) {
final ClassDetails match = walkSupers( type.getSuperClass() );
if ( match != null ) {
return match;
}
}
if ( CategorizationHelper.isIdentifiable( type ) ) {
return type;
}
return null;
}
@Override
public EntityTypeMetadata getRoot() {
return rootEntityTypeMetadata;
}
@Override
public IdentifiableTypeMetadata getAbsoluteRoot() {
return absoluteRootTypeMetadata;
}
@Override
public InheritanceType getInheritanceType() {
return inheritanceType;
}
@Override
public jakarta.persistence.AccessType getDefaultAccessType() {
return defaultAccessType;
}
@Override
public KeyMapping getIdMapping() {
return idMapping;
}
@Override
public KeyMapping getNaturalIdMapping() {
return naturalIdMapping;
}
@Override
public AttributeMetadata getVersionAttribute() {
return versionAttribute;
}
@Override
public AttributeMetadata getTenantIdAttribute() {
return tenantIdAttribute;
}
@Override
public OptimisticLockStyle getOptimisticLockStyle() {
return optimisticLockStyle;
}
@Override
public CacheRegion getCacheRegion() {
return cacheRegion;
}
@Override
public NaturalIdCacheRegion getNaturalIdCacheRegion() {
return naturalIdCacheRegion;
}
@Override
public void forEachType(HierarchyTypeVisitor typeVisitor) {
final IdentifiableTypeMetadata absoluteRoot = getAbsoluteRoot();
final HierarchyRelation hierarchyRelation;
if ( absoluteRoot == getRoot() ) {
hierarchyRelation = HierarchyRelation.ROOT;
}
else {
hierarchyRelation = HierarchyRelation.SUPER;
}
forEachType( absoluteRoot, null, hierarchyRelation, typeVisitor );
}
private void forEachType(
IdentifiableTypeMetadata type,
IdentifiableTypeMetadata superType,
HierarchyRelation hierarchyRelation,
HierarchyTypeVisitor typeVisitor) {
typeVisitor.visitType( type, superType, this, hierarchyRelation );
final HierarchyRelation nextRelation;
if ( hierarchyRelation == HierarchyRelation.SUPER ) {
if ( type == getRoot().getSuperType() ) {
// the next iteration will be the root
nextRelation = HierarchyRelation.ROOT;
}
else {
nextRelation = HierarchyRelation.SUPER;
}
}
else {
nextRelation = HierarchyRelation.SUB;
}
type.forEachSubType( subType -> forEachType( subType, type, nextRelation, typeVisitor ) );
}
@Override
public String toString() {
return String.format(
Locale.ROOT,
"EntityHierarchy(`%s` (%s))",
rootEntityTypeMetadata.getEntityName(),
inheritanceType.name()
);
}
private static final OptimisticLockStyle DEFAULT_LOCKING_STRATEGY = OptimisticLockStyle.VERSION;
private InheritanceType determineInheritanceType(HierarchyMetadataCollector metadataCollector) {
if ( ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER.isDebugEnabled() ) {
// Validate that there is no @Inheritance annotation further down the hierarchy
ensureNoInheritanceAnnotationsOnSubclasses( rootEntityTypeMetadata );
}
final Inheritance inheritanceAnnotation = metadataCollector.getInheritanceAnnotation();
if ( inheritanceAnnotation != null ) {
return inheritanceAnnotation.strategy();
}
return InheritanceType.SINGLE_TABLE;
}
private OptimisticLockStyle determineOptimisticLockStyle(HierarchyMetadataCollector metadataCollector) {
final OptimisticLocking optimisticLockingAnnotation = metadataCollector.getOptimisticLockingAnnotation();
if ( optimisticLockingAnnotation != null ) {
final OptimisticLockType lockingType = optimisticLockingAnnotation.type();
return OptimisticLockStyle.fromLockType( lockingType );
}
return DEFAULT_LOCKING_STRATEGY;
}
private CacheRegion determineCacheRegion(
HierarchyMetadataCollector metadataCollector,
AccessType defaultCacheAccessType) {
final Cache cacheAnnotation = metadataCollector.getCacheAnnotation();
return new CacheRegion( cacheAnnotation, defaultCacheAccessType, rootEntityTypeMetadata.getEntityName() );
}
private NaturalIdCacheRegion determineNaturalIdCacheRegion(
HierarchyMetadataCollector metadataCollector,
CacheRegion cacheRegion) {
final NaturalIdCache naturalIdCacheAnnotation = metadataCollector.getNaturalIdCacheAnnotation();
return new NaturalIdCacheRegion( naturalIdCacheAnnotation, cacheRegion );
}
/**
* 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 Inheritance localAnnotation = managedClass.getDirectAnnotationUsage( Inheritance.class );
if ( localAnnotation == null ) {
return null;
}
return localAnnotation.strategy();
}
private void ensureNoInheritanceAnnotationsOnSubclasses(IdentifiableTypeMetadata type) {
type.forEachSubType( (subType) -> {
if ( getLocallyDefinedInheritanceType( subType.getClassDetails() ) != null ) {
ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER.debugf(
"@javax.persistence.Inheritance was specified on non-root entity [%s]; ignoring...",
type.getClassDetails().getName()
);
}
ensureNoInheritanceAnnotationsOnSubclasses( subType );
} );
}
}

View File

@ -1,369 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.SQLUpdate;
import org.hibernate.annotations.Synchronize;
import org.hibernate.boot.model.CustomSql;
import org.hibernate.boot.model.naming.EntityNaming;
import org.hibernate.boot.models.annotations.spi.CustomSqlDetails;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.internal.util.collections.ArrayHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.ClassDetails;
import jakarta.persistence.AccessType;
import jakarta.persistence.Cacheable;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import static org.hibernate.internal.util.StringHelper.EMPTY_STRINGS;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.internal.util.StringHelper.unqualify;
/**
* @author Steve Ebersole
*/
public class EntityTypeMetadataImpl
extends AbstractIdentifiableTypeMetadata
implements EntityTypeMetadata, EntityNaming {
private final String entityName;
private final String jpaEntityName;
private final List<AttributeMetadata> attributeList;
private final boolean mutable;
private final boolean cacheable;
private final boolean isLazy;
private final String proxy;
private final int batchSize;
private final String discriminatorMatchValue;
private final boolean isDynamicInsert;
private final boolean isDynamicUpdate;
private final Map<String,CustomSql> customInsertMap;
private final Map<String,CustomSql> customUpdateMap;
private final Map<String,CustomSql> customDeleteMap;
private final String[] synchronizedTableNames;
private List<JpaEventListener> hierarchyEventListeners;
private List<JpaEventListener> completeEventListeners;
/**
* Form used when the entity is the absolute-root (no mapped-super) of the hierarchy
*/
public EntityTypeMetadataImpl(
ClassDetails classDetails,
EntityHierarchy hierarchy,
AccessType defaultAccessType,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext modelContext) {
super( classDetails, hierarchy, null, defaultAccessType, modelContext );
// NOTE: There is no annotation for `entity-name` - it comes exclusively from XML
// mappings. By default, the `entityName` is simply the entity class name.
// `ClassDetails#getName` already handles this all for us
this.entityName = getClassDetails().getName();
final Entity entityAnnotation = classDetails.getDirectAnnotationUsage( Entity.class );
this.jpaEntityName = determineJpaEntityName( entityAnnotation, entityName );
final LifecycleCallbackCollector lifecycleCallbackCollector = new LifecycleCallbackCollector( classDetails, modelContext );
this.attributeList = resolveAttributes( lifecycleCallbackCollector );
this.hierarchyEventListeners = collectHierarchyEventListeners( lifecycleCallbackCollector.resolve() );
this.completeEventListeners = collectCompleteEventListeners( modelContext );
this.mutable = determineMutability( classDetails, modelContext );
this.cacheable = determineCacheability( classDetails, modelContext );
this.synchronizedTableNames = determineSynchronizedTableNames();
this.batchSize = determineBatchSize();
this.isDynamicInsert = decodeDynamicInsert();
this.isDynamicUpdate = decodeDynamicUpdate();
this.customInsertMap = extractCustomSql( classDetails, SQLInsert.class );
this.customUpdateMap = extractCustomSql( classDetails, SQLUpdate.class );
this.customDeleteMap = extractCustomSql( classDetails, SQLDelete.class );
// defaults are that it is lazy and that the class itself is the proxy class
this.isLazy = true;
this.proxy = getEntityName();
final DiscriminatorValue discriminatorValueAnn = classDetails.getDirectAnnotationUsage( DiscriminatorValue.class );
if ( discriminatorValueAnn != null ) {
this.discriminatorMatchValue = discriminatorValueAnn.value();
}
else {
this.discriminatorMatchValue = null;
}
postInstantiate( true, typeConsumer );
}
/**
* Form used when the entity is NOT the absolute-root of the hierarchy
*/
public EntityTypeMetadataImpl(
ClassDetails classDetails,
EntityHierarchy hierarchy,
AbstractIdentifiableTypeMetadata superType,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext modelContext) {
super( classDetails, hierarchy, superType, modelContext );
// NOTE: There is no annotation for `entity-name` - it comes exclusively from XML
// mappings. By default, the `entityName` is simply the entity class name.
// `ClassDetails#getName` already handles this all for us
this.entityName = getClassDetails().getName();
final Entity entityAnnotation = classDetails.getDirectAnnotationUsage( Entity.class );
this.jpaEntityName = determineJpaEntityName( entityAnnotation, entityName );
final LifecycleCallbackCollector lifecycleCallbackCollector = new LifecycleCallbackCollector( classDetails, modelContext );
this.attributeList = resolveAttributes( lifecycleCallbackCollector );
this.hierarchyEventListeners = collectHierarchyEventListeners( lifecycleCallbackCollector.resolve() );
this.completeEventListeners = collectCompleteEventListeners( modelContext );
this.mutable = determineMutability( classDetails, modelContext );
this.cacheable = determineCacheability( classDetails, modelContext );
this.synchronizedTableNames = determineSynchronizedTableNames();
this.batchSize = determineBatchSize();
this.isDynamicInsert = decodeDynamicInsert();
this.isDynamicUpdate = decodeDynamicUpdate();
this.customInsertMap = extractCustomSql( classDetails, SQLInsert.class );
this.customUpdateMap = extractCustomSql( classDetails, SQLUpdate.class );
this.customDeleteMap = extractCustomSql( classDetails, SQLDelete.class );
// defaults are that it is lazy and that the class itself is the proxy class
this.isLazy = true;
this.proxy = getEntityName();
final DiscriminatorValue discriminatorValueAnn = classDetails.getDirectAnnotationUsage( DiscriminatorValue.class );
if ( discriminatorValueAnn != null ) {
this.discriminatorMatchValue = discriminatorValueAnn.value();
}
else {
this.discriminatorMatchValue = null;
}
postInstantiate( true, typeConsumer );
}
@Override
protected List<AttributeMetadata> attributeList() {
return attributeList;
}
@Override
public String getEntityName() {
return entityName;
}
@Override
public String getJpaEntityName() {
return jpaEntityName;
}
@Override
public String getClassName() {
return getClassDetails().getClassName();
}
@Override
public boolean isMutable() {
return mutable;
}
@Override
public boolean isCacheable() {
return cacheable;
}
@Override
public String[] getSynchronizedTableNames() {
return synchronizedTableNames;
}
@Override
public int getBatchSize() {
return batchSize;
}
@Override
public boolean isDynamicInsert() {
return isDynamicInsert;
}
@Override
public boolean isDynamicUpdate() {
return isDynamicUpdate;
}
@Override
public Map<String, CustomSql> getCustomInserts() {
return customInsertMap;
}
@Override
public Map<String, CustomSql> getCustomUpdates() {
return customUpdateMap;
}
@Override
public Map<String, CustomSql> getCustomDeletes() {
return customDeleteMap;
}
public String getDiscriminatorMatchValue() {
return discriminatorMatchValue;
}
public boolean isLazy() {
return isLazy;
}
public String getProxy() {
return proxy;
}
@Override
public List<JpaEventListener> getHierarchyJpaEventListeners() {
return hierarchyEventListeners;
}
@Override
public List<JpaEventListener> getCompleteJpaEventListeners() {
return completeEventListeners;
}
private String determineJpaEntityName(Entity entityAnnotation, String entityName) {
final String name = entityAnnotation.name();
if ( isNotEmpty( name ) ) {
return name;
}
return unqualify( entityName );
}
private boolean determineMutability(ClassDetails classDetails, ModelCategorizationContext modelContext) {
final Immutable immutableAnn = classDetails.getDirectAnnotationUsage( Immutable.class );
return immutableAnn == null;
}
private boolean determineCacheability(
ClassDetails classDetails,
ModelCategorizationContext modelContext) {
final Cacheable cacheableAnn = classDetails.getDirectAnnotationUsage( Cacheable.class );
switch ( modelContext.getSharedCacheMode() ) {
case NONE: {
return false;
}
case ALL: {
return true;
}
case DISABLE_SELECTIVE: {
// Disable caching for all `@Cacheable(false)`, enabled otherwise (including no annotation)
//noinspection RedundantIfStatement
if ( cacheableAnn == null || cacheableAnn.value() ) {
// not disabled
return true;
}
else {
// disable, there was an explicit `@Cacheable(false)`
return false;
}
}
default: {
// ENABLE_SELECTIVE
// UNSPECIFIED
// Enable caching for all `@Cacheable(true)`, disable otherwise (including no annotation)
//noinspection RedundantIfStatement
if ( cacheableAnn != null && cacheableAnn.value() ) {
// enable, there was an explicit `@Cacheable(true)`
return true;
}
else {
return false;
}
}
}
}
/**
* Build a CustomSql reference from {@link SQLInsert},
* {@link SQLUpdate}, {@link SQLDelete}
* or {@link org.hibernate.annotations.SQLDeleteAll} annotations
*/
public static <A extends Annotation> Map<String,CustomSql> extractCustomSql(ClassDetails classDetails, Class<A> annotationType) {
final A[] annotationUsages = classDetails.getRepeatedAnnotationUsages( annotationType, null );
if ( CollectionHelper.isEmpty( annotationUsages ) ) {
return Collections.emptyMap();
}
final Map<String, CustomSql> result = new HashMap<>();
ArrayHelper.forEach( annotationUsages, (customSqlAnnotation) -> {
final CustomSqlDetails customSqlDetails = (CustomSqlDetails) customSqlAnnotation;
final String sql = customSqlDetails.sql();
final boolean isCallable = customSqlDetails.callable();
final ResultCheckStyle checkValue = customSqlDetails.check();
final ExecuteUpdateResultCheckStyle checkStyle;
if ( checkValue == null ) {
checkStyle = isCallable
? ExecuteUpdateResultCheckStyle.NONE
: ExecuteUpdateResultCheckStyle.COUNT;
}
else {
checkStyle = ExecuteUpdateResultCheckStyle.fromResultCheckStyle( checkValue );
}
result.put(
customSqlDetails.table(),
new CustomSql( sql, isCallable, checkStyle )
);
} );
return result;
}
private String[] determineSynchronizedTableNames() {
final Synchronize synchronizeAnnotation = getClassDetails().getDirectAnnotationUsage( Synchronize.class );
if ( synchronizeAnnotation != null ) {
return synchronizeAnnotation.value();
}
return EMPTY_STRINGS;
}
private int determineBatchSize() {
final BatchSize batchSizeAnnotation = getClassDetails().getDirectAnnotationUsage( BatchSize.class );
if ( batchSizeAnnotation != null ) {
return batchSizeAnnotation.size();
}
return -1;
}
private boolean decodeDynamicInsert() {
return getClassDetails().getDirectAnnotationUsage( DynamicInsert.class ) != null;
}
private boolean decodeDynamicUpdate() {
return getClassDetails().getDirectAnnotationUsage( DynamicUpdate.class ) != null;
}
}

View File

@ -1,310 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.TenantId;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.models.categorize.spi.IdentifiableTypeMetadata;
import org.hibernate.boot.models.categorize.spi.KeyMapping;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.Inheritance;
import jakarta.persistence.Version;
/**
* Used to collect useful details about a hierarchy as we build its metadata
*
* @implNote The HierarchyTypeConsumer is called from root down. We make use
* of that detail in a number of places here in the code.
*
* @author Steve Ebersole
*/
public class HierarchyMetadataCollector implements HierarchyTypeConsumer {
private final EntityHierarchy entityHierarchy;
private final ClassDetails rootEntityClassDetails;
private final HierarchyTypeConsumer delegateConsumer;
private final ModelCategorizationContext categorizationContext;
private boolean belowRootEntity;
private EntityTypeMetadata rootEntityMetadata;
private Inheritance inheritanceAnnotation;
private OptimisticLocking optimisticLockingAnnotation;
private Cache cacheAnnotation;
private NaturalIdCache naturalIdCacheAnnotation;
private KeyMapping idMapping;
private AttributeMetadata versionAttribute;
private AttributeMetadata tenantIdAttribute;
private IdClass idClassAnnotation;
private Object collectedIdAttributes;
private Object collectedNaturalIdAttributes;
public HierarchyMetadataCollector(
EntityHierarchy entityHierarchy,
ClassDetails rootEntityClassDetails,
HierarchyTypeConsumer delegateConsumer,
ModelCategorizationContext categorizationContext) {
this.entityHierarchy = entityHierarchy;
this.rootEntityClassDetails = rootEntityClassDetails;
this.delegateConsumer = delegateConsumer;
this.categorizationContext = categorizationContext;
}
public EntityTypeMetadata getRootEntityMetadata() {
return rootEntityMetadata;
}
public KeyMapping getIdMapping() {
if ( idMapping == null ) {
idMapping = buildIdMapping();
}
return idMapping;
}
public Inheritance getInheritanceAnnotation() {
return inheritanceAnnotation;
}
public AttributeMetadata getVersionAttribute() {
return versionAttribute;
}
public AttributeMetadata getTenantIdAttribute() {
return tenantIdAttribute;
}
public OptimisticLocking getOptimisticLockingAnnotation() {
return optimisticLockingAnnotation;
}
public Cache getCacheAnnotation() {
return cacheAnnotation;
}
public NaturalIdCache getNaturalIdCacheAnnotation() {
return naturalIdCacheAnnotation;
}
private KeyMapping buildIdMapping() {
if ( collectedIdAttributes == null ) {
throw new AnnotationException( "Unable to determine id attribute(s) - " + rootEntityClassDetails.getName() );
}
if ( collectedIdAttributes instanceof List ) {
//noinspection unchecked
final List<AttributeMetadata> idAttributes = (List<AttributeMetadata>) collectedIdAttributes;
final ClassDetails idClassDetails;
if ( idClassAnnotation == null ) {
idClassDetails = null;
}
else {
idClassDetails = toClassDetails( idClassAnnotation.value() );
}
return new NonAggregatedKeyMappingImpl( idAttributes, idClassDetails );
}
final AttributeMetadata idAttribute = (AttributeMetadata) collectedIdAttributes;
if ( idAttribute.getNature() == AttributeMetadata.AttributeNature.BASIC ) {
return new BasicKeyMappingImpl( idAttribute );
}
if ( idAttribute.getNature() == AttributeMetadata.AttributeNature.EMBEDDED ) {
return new AggregatedKeyMappingImpl( idAttribute );
}
if ( idAttribute.getNature() == AttributeMetadata.AttributeNature.TO_ONE ) {
final List<AttributeMetadata> idAttributes = List.of( idAttribute );
final ClassDetails idClassDetails;
if ( idClassAnnotation == null ) {
idClassDetails = null;
}
else {
idClassDetails = toClassDetails( idClassAnnotation.value() );
}
return new NonAggregatedKeyMappingImpl( idAttributes, idClassDetails );
}
throw new ModelsException(
String.format(
Locale.ROOT,
"Unexpected attribute nature [%s] - %s",
idAttribute.getNature(),
entityHierarchy.getRoot().getEntityName()
)
);
}
private ClassDetails toClassDetails(Class<?> value) {
if ( value == null ) {
return null;
}
return categorizationContext.getClassDetailsRegistry().getClassDetails( value.getName() );
}
public KeyMapping getNaturalIdMapping() {
if ( collectedNaturalIdAttributes == null ) {
return null;
}
if ( collectedNaturalIdAttributes instanceof List ) {
//noinspection unchecked
final List<AttributeMetadata> attributes = (List<AttributeMetadata>) collectedNaturalIdAttributes;
return new NonAggregatedKeyMappingImpl( attributes, null );
}
final AttributeMetadata attribute = (AttributeMetadata) collectedNaturalIdAttributes;
if ( attribute.getNature() == AttributeMetadata.AttributeNature.BASIC ) {
return new BasicKeyMappingImpl( attribute );
}
if ( attribute.getNature() == AttributeMetadata.AttributeNature.EMBEDDED ) {
return new AggregatedKeyMappingImpl( attribute );
}
if ( attribute.getNature() == AttributeMetadata.AttributeNature.TO_ONE ) {
return new BasicKeyMappingImpl( attribute );
}
throw new ModelsException(
String.format(
Locale.ROOT,
"Unexpected attribute nature [%s] - %s",
attribute.getNature(),
entityHierarchy.getRoot().getEntityName()
)
);
}
@Override
public void acceptType(IdentifiableTypeMetadata typeMetadata) {
if ( delegateConsumer != null ) {
delegateConsumer.acceptType( typeMetadata );
}
if ( belowRootEntity ) {
return;
}
final ClassDetails classDetails = typeMetadata.getClassDetails();
if ( classDetails == rootEntityClassDetails ) {
rootEntityMetadata = (EntityTypeMetadata) typeMetadata;
belowRootEntity = true;
}
inheritanceAnnotation = applyLocalAnnotation( Inheritance.class, classDetails, inheritanceAnnotation );
optimisticLockingAnnotation = applyLocalAnnotation( OptimisticLocking.class, classDetails, optimisticLockingAnnotation );
cacheAnnotation = applyLocalAnnotation( Cache.class, classDetails, cacheAnnotation );
naturalIdCacheAnnotation = applyLocalAnnotation( NaturalIdCache.class, classDetails, naturalIdCacheAnnotation );
idClassAnnotation = applyLocalAnnotation( IdClass.class, classDetails, idClassAnnotation );
final boolean collectIds = collectedIdAttributes == null;
if ( collectIds || versionAttribute == null || tenantIdAttribute == null ) {
// walk the attributes
typeMetadata.forEachAttribute( (index, attributeMetadata) -> {
final MemberDetails attributeMember = attributeMetadata.getMember();
if ( collectIds ) {
final EmbeddedId eIdAnn = attributeMember.getDirectAnnotationUsage( EmbeddedId.class );
if ( eIdAnn != null ) {
collectIdAttribute( attributeMetadata );
}
final Id idAnn = attributeMember.getDirectAnnotationUsage( Id.class );
if ( idAnn != null ) {
collectIdAttribute( attributeMetadata );
}
}
if ( attributeMember.getDirectAnnotationUsage( NaturalId.class ) != null ) {
collectNaturalIdAttribute( attributeMetadata );
}
if ( versionAttribute == null ) {
if ( attributeMember.getDirectAnnotationUsage( Version.class ) != null ) {
versionAttribute = attributeMetadata;
}
}
if ( tenantIdAttribute == null ) {
if ( attributeMember.getDirectAnnotationUsage( TenantId.class ) != null ) {
tenantIdAttribute = attributeMetadata;
}
}
} );
}
}
private <A extends Annotation> A applyLocalAnnotation(Class<A> annotationType, ClassDetails classDetails, A currentValue) {
final A localInheritanceAnnotation = classDetails.getDirectAnnotationUsage( annotationType );
if ( localInheritanceAnnotation != null ) {
// the one "closest" to the root-entity should win
return localInheritanceAnnotation;
}
return currentValue;
}
public void collectIdAttribute(AttributeMetadata member) {
assert member != null;
if ( collectedIdAttributes == null ) {
collectedIdAttributes = member;
}
else if ( collectedIdAttributes instanceof List ) {
//noinspection unchecked,rawtypes
final List<AttributeMetadata> membersList = (List) collectedIdAttributes;
membersList.add( member );
}
else if ( collectedIdAttributes instanceof AttributeMetadata ) {
final ArrayList<AttributeMetadata> combined = new ArrayList<>();
combined.add( (AttributeMetadata) collectedIdAttributes );
combined.add( member );
collectedIdAttributes = combined;
}
}
public void collectNaturalIdAttribute(AttributeMetadata member) {
assert member != null;
if ( collectedNaturalIdAttributes == null ) {
collectedNaturalIdAttributes = member;
}
else if ( collectedNaturalIdAttributes instanceof List ) {
//noinspection unchecked,rawtypes
final List<AttributeMetadata> membersList = (List) collectedNaturalIdAttributes;
membersList.add( member );
}
else if ( collectedNaturalIdAttributes instanceof AttributeMetadata ) {
final ArrayList<AttributeMetadata> combined = new ArrayList<>();
combined.add( (AttributeMetadata) collectedNaturalIdAttributes );
combined.add( member );
collectedNaturalIdAttributes = combined;
}
}
}

View File

@ -1,17 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.categorize.spi.IdentifiableTypeMetadata;
/**
* Consumer of types as we walk the managed-type hierarchy
*
* @author Steve Ebersole
*/
@FunctionalInterface
public interface HierarchyTypeConsumer {
void acceptType(IdentifiableTypeMetadata type);
}

View File

@ -1,129 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.lang.annotation.Annotation;
import java.util.Locale;
import org.hibernate.boot.models.categorize.spi.AllMemberConsumer;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.MethodDetails;
import jakarta.persistence.PostLoad;
import jakarta.persistence.PostPersist;
import jakarta.persistence.PostRemove;
import jakarta.persistence.PostUpdate;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreRemove;
import jakarta.persistence.PreUpdate;
import static org.hibernate.boot.models.spi.JpaEventListener.matchesSignature;
/**
* @author Steve Ebersole
*/
public class LifecycleCallbackCollector implements AllMemberConsumer {
private final ClassDetails managedTypeDetails;
private final ModelCategorizationContext modelContext;
private MethodDetails prePersist;
private MethodDetails postPersist;
private MethodDetails preUpdate;
private MethodDetails postUpdate;
private MethodDetails preRemove;
private MethodDetails postRemove;
private MethodDetails postLoad;
public LifecycleCallbackCollector(ClassDetails managedTypeDetails, ModelCategorizationContext modelContext) {
this.managedTypeDetails = managedTypeDetails;
this.modelContext = modelContext;
}
@Override
public void acceptMember(MemberDetails memberDetails) {
if ( memberDetails.isField() ) {
return;
}
final MethodDetails methodDetails = (MethodDetails) memberDetails;
if ( methodDetails.hasDirectAnnotationUsage( PrePersist.class )
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
prePersist = apply( methodDetails, PrePersist.class, managedTypeDetails, prePersist );
}
else if ( methodDetails.hasDirectAnnotationUsage( PostPersist.class )
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postPersist = apply( methodDetails, PostPersist.class, managedTypeDetails, postPersist );
}
else if ( methodDetails.hasDirectAnnotationUsage( PreRemove.class )
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
preRemove = apply( methodDetails, PreRemove.class, managedTypeDetails, preRemove );
}
else if ( methodDetails.hasDirectAnnotationUsage( PostRemove.class )
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postRemove = apply( methodDetails, PostRemove.class, managedTypeDetails, postRemove );
}
else if ( methodDetails.hasDirectAnnotationUsage( PreUpdate.class )
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
preUpdate = apply( methodDetails, PreUpdate.class, managedTypeDetails, preUpdate );
}
else if ( methodDetails.hasDirectAnnotationUsage( PostUpdate.class )
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postUpdate = apply( methodDetails, PostUpdate.class, managedTypeDetails, postUpdate );
}
else if ( methodDetails.hasDirectAnnotationUsage( PostLoad.class )
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postLoad = apply( methodDetails, PostLoad.class, managedTypeDetails, postLoad );
}
}
private static <A extends Annotation> MethodDetails apply(
MethodDetails incomingValue,
Class<A> annotationType,
ClassDetails managedTypeDetails,
MethodDetails currentValue) {
if ( currentValue != null ) {
throw new ModelsException(
String.format(
Locale.ROOT,
"Encountered multiple @%s methods [%s] - %s, %s",
annotationType.getSimpleName(),
managedTypeDetails.getClassName(),
currentValue.getName(),
incomingValue.getName()
)
);
}
return incomingValue;
}
public JpaEventListener resolve() {
if ( prePersist != null
|| postPersist != null
|| preUpdate != null
|| postUpdate != null
|| preRemove != null
|| postRemove != null
|| postLoad != null ) {
return new JpaEventListener(
JpaEventListenerStyle.CALLBACK,
managedTypeDetails,
prePersist,
postPersist,
preRemove,
postRemove,
preUpdate,
postUpdate,
postLoad
);
}
return null;
}
}

View File

@ -1,88 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.List;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.MappedSuperclassTypeMetadata;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.models.spi.ClassDetails;
import jakarta.persistence.AccessType;
/**
* @author Steve Ebersole
*/
public class MappedSuperclassTypeMetadataImpl
extends AbstractIdentifiableTypeMetadata
implements MappedSuperclassTypeMetadata {
private final List<AttributeMetadata> attributeList;
private final List<JpaEventListener> hierarchyEventListeners;
private final List<JpaEventListener> completeEventListeners;
/**
* Form used when the mapped-superclass is the absolute-root of the hierarchy ("above" the root entity)
*/
public MappedSuperclassTypeMetadataImpl(
ClassDetails classDetails,
EntityHierarchy hierarchy,
MappedSuperclassTypeMetadataImpl superTypeMetadata,
AccessType defaultAccessType,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext modelContext) {
super( classDetails, hierarchy, superTypeMetadata, defaultAccessType, modelContext );
final LifecycleCallbackCollector lifecycleCallbackCollector = new LifecycleCallbackCollector( classDetails, modelContext );
this.attributeList = resolveAttributes( lifecycleCallbackCollector );
this.hierarchyEventListeners = collectHierarchyEventListeners( lifecycleCallbackCollector.resolve() );
this.completeEventListeners = collectCompleteEventListeners( modelContext );
if ( superTypeMetadata != null ) {
superTypeMetadata.addSubclass( this );
}
postInstantiate( false, typeConsumer );
}
/**
* Form used when the mapped-superclass is NOT the absolute-root of the hierarchy
*/
public MappedSuperclassTypeMetadataImpl(
ClassDetails classDetails,
EntityHierarchy hierarchy,
AbstractIdentifiableTypeMetadata superType,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext modelContext) {
super( classDetails, hierarchy, superType, modelContext );
final LifecycleCallbackCollector lifecycleCallbackCollector = new LifecycleCallbackCollector( classDetails, modelContext );
this.attributeList = resolveAttributes( lifecycleCallbackCollector );
this.hierarchyEventListeners = collectHierarchyEventListeners( lifecycleCallbackCollector.resolve() );
this.completeEventListeners = collectCompleteEventListeners( modelContext );
postInstantiate( true, typeConsumer );
}
@Override
protected List<AttributeMetadata> attributeList() {
return attributeList;
}
@Override
public List<JpaEventListener> getHierarchyJpaEventListeners() {
return hierarchyEventListeners;
}
@Override
public List<JpaEventListener> getCompleteJpaEventListeners() {
return completeEventListeners;
}
}

View File

@ -1,67 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.List;
import org.hibernate.boot.models.spi.GlobalRegistrations;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetailsRegistry;
import jakarta.persistence.SharedCacheMode;
/**
* @author Steve Ebersole
*/
public class ModelCategorizationContextImpl implements ModelCategorizationContext {
private final ClassDetailsRegistry classDetailsRegistry;
private final AnnotationDescriptorRegistry annotationDescriptorRegistry;
private final GlobalRegistrations globalRegistrations;
private final SharedCacheMode sharedCacheMode;
public ModelCategorizationContextImpl(
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry annotationDescriptorRegistry,
GlobalRegistrations globalRegistrations) {
this( classDetailsRegistry, annotationDescriptorRegistry, globalRegistrations, SharedCacheMode.UNSPECIFIED );
}
public ModelCategorizationContextImpl(
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry annotationDescriptorRegistry,
GlobalRegistrations globalRegistrations,
SharedCacheMode sharedCacheMode) {
this.classDetailsRegistry = classDetailsRegistry;
this.annotationDescriptorRegistry = annotationDescriptorRegistry;
this.globalRegistrations = globalRegistrations;
this.sharedCacheMode = sharedCacheMode;
}
@Override
public ClassDetailsRegistry getClassDetailsRegistry() {
return classDetailsRegistry;
}
@Override
public AnnotationDescriptorRegistry getAnnotationDescriptorRegistry() {
return annotationDescriptorRegistry;
}
public GlobalRegistrations getGlobalRegistrations() {
return globalRegistrations;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return sharedCacheMode;
}
@Override
public List<JpaEventListener> getDefaultEventListeners() {
return getGlobalRegistrations().getEntityListenerRegistrations();
}
}

View File

@ -1,57 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.List;
import org.hibernate.boot.models.categorize.spi.AttributeConsumer;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.NonAggregatedKeyMapping;
import org.hibernate.models.spi.ClassDetails;
/**
* @author Steve Ebersole
*/
public class NonAggregatedKeyMappingImpl implements NonAggregatedKeyMapping {
private final List<AttributeMetadata> idAttributes;
private final ClassDetails idClassType;
public NonAggregatedKeyMappingImpl(List<AttributeMetadata> idAttributes, ClassDetails idClassType) {
this.idAttributes = idAttributes;
this.idClassType = idClassType;
}
@Override
public List<AttributeMetadata> getIdAttributes() {
return idAttributes;
}
@Override
public ClassDetails getIdClassType() {
return idClassType;
}
@Override
public ClassDetails getKeyType() {
return idClassType;
}
@Override
public void forEachAttribute(AttributeConsumer consumer) {
for ( int i = 0; i < idAttributes.size(); i++ ) {
consumer.accept( i, idAttributes.get( i ) );
}
}
@Override
public boolean contains(AttributeMetadata attributeMetadata) {
for ( int i = 0; i < idAttributes.size(); i++ ) {
if ( idAttributes.get( i ) == attributeMetadata ) {
return true;
}
}
return false;
}
}

View File

@ -1,315 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyDiscriminator;
import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyDiscriminatorValues;
import org.hibernate.annotations.AnyKeyJavaClass;
import org.hibernate.annotations.AnyKeyJavaType;
import org.hibernate.annotations.AnyKeyJdbcType;
import org.hibernate.annotations.AnyKeyJdbcTypeCode;
import org.hibernate.annotations.JavaType;
import org.hibernate.annotations.JdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.Nationalized;
import org.hibernate.annotations.TenantId;
import org.hibernate.annotations.TimeZoneColumn;
import org.hibernate.annotations.TimeZoneStorage;
import org.hibernate.annotations.Type;
import org.hibernate.boot.internal.AnyKeyType;
import org.hibernate.boot.models.AccessTypePlacementException;
import org.hibernate.boot.models.AnnotationPlacementException;
import org.hibernate.boot.models.categorize.spi.ClassAttributeAccessType;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.MethodDetails;
import jakarta.annotation.Generated;
import jakarta.persistence.Access;
import jakarta.persistence.AccessType;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embedded;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Temporal;
import jakarta.persistence.Version;
/**
* 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<MemberDetails> resolveAttributesMembers(
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
ClassAttributeAccessType classLevelAccessType) {
final LinkedHashMap<String,MemberDetails> results = new LinkedHashMap<>();
processAttributeLevelAccess(
results::put,
transientFieldChecker,
transientMethodChecker,
classDetails,
classLevelAccessType
);
processClassLevelAccess(
results::containsKey,
results::put,
transientFieldChecker,
transientMethodChecker,
classDetails,
classLevelAccessType
);
return new ArrayList<>( results.values() );
}
private <M extends MemberDetails> void processAttributeLevelAccess(
BiConsumer<String,MemberDetails> memberConsumer,
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
ClassAttributeAccessType classLevelAccessType) {
final List<FieldDetails> fields = classDetails.getFields();
for ( int i = 0; i < fields.size(); i++ ) {
final FieldDetails fieldDetails = fields.get( i );
processAttributeLevelAccessMember( fieldDetails, memberConsumer, transientFieldChecker, classDetails, classLevelAccessType );
}
final List<MethodDetails> methods = classDetails.getMethods();
for ( int i = 0; i < methods.size(); i++ ) {
final MethodDetails methodDetails = methods.get( i );
processAttributeLevelAccessMember( methodDetails, memberConsumer, transientMethodChecker, classDetails, classLevelAccessType );
}
}
private <M extends MemberDetails> void processAttributeLevelAccessMember(
M memberDetails,
BiConsumer<String,MemberDetails> memberConsumer,
Function<M,Boolean> transiencyChecker,
ClassDetails classDetails,
ClassAttributeAccessType classLevelAccessType) {
if ( transiencyChecker.apply( memberDetails ) ) {
// the field is transient
return;
}
final Access access = memberDetails.getDirectAnnotationUsage( Access.class );
if ( access == null ) {
checkForMisplacedAnnotations( classDetails, memberDetails, classLevelAccessType );
return;
}
final AccessType attributeAccessType = access.value();
validateAttributeLevelAccess( memberDetails, attributeAccessType, classDetails );
memberConsumer.accept( memberDetails.resolveAttributeName(), memberDetails );
}
private <M extends MemberDetails> void checkForMisplacedAnnotations(
ClassDetails classDetails,
M memberDetails,
ClassAttributeAccessType classLevelAccessType) {
// We have a case where the member did not define `@Access`.
//
// In such a case the member would only be an attribute backer if the member
// kind matched the class access-type. If the member kind does *not* match
// the class access-type, validate that it does not declare any mapping annotations
if ( classLevelAccessType == null ) {
// nothing to check
return;
}
if ( !matchesAccessType( memberDetails, classLevelAccessType ) ) {
if ( containsMappingAnnotations( memberDetails ) ) {
if ( memberDetails.getKind() == AnnotationTarget.Kind.FIELD ) {
throw new AnnotationPlacementException(
String.format(
Locale.ROOT,
"Field `%s#%s` declared mapping annotations even though it is not a persistent attribute",
classDetails.getName(),
memberDetails.getName()
)
);
}
else {
assert memberDetails.getKind() == AnnotationTarget.Kind.METHOD;
throw new AnnotationPlacementException(
String.format(
Locale.ROOT,
"Method `%s#%s` declared mapping annotations even though it is not a persistent attribute",
classDetails.getName(),
memberDetails.getName()
)
);
}
}
}
}
private <M extends MemberDetails> boolean matchesAccessType(
M memberDetails,
ClassAttributeAccessType classLevelAccessType) {
assert classLevelAccessType != null;
if ( classLevelAccessType.getJpaAccessType() == AccessType.FIELD ) {
return memberDetails.getKind() == AnnotationTarget.Kind.FIELD;
}
else {
return memberDetails.getKind() == AnnotationTarget.Kind.METHOD
&& ( (MethodDetails) memberDetails ).getMethodKind() == MethodDetails.MethodKind.GETTER;
}
}
private <M extends MemberDetails> boolean containsMappingAnnotations(M memberDetails) {
// todo (jpa32) : better way to do this?
return memberDetails.hasDirectAnnotationUsage( Id.class )
|| memberDetails.hasDirectAnnotationUsage( EmbeddedId.class )
|| memberDetails.hasDirectAnnotationUsage( Version.class )
|| memberDetails.hasDirectAnnotationUsage( Basic.class )
|| memberDetails.hasDirectAnnotationUsage( Embedded.class )
|| memberDetails.hasDirectAnnotationUsage( ManyToOne.class )
|| memberDetails.hasDirectAnnotationUsage( OneToOne.class )
|| memberDetails.hasDirectAnnotationUsage( ElementCollection.class )
|| memberDetails.hasDirectAnnotationUsage( ManyToMany.class )
|| memberDetails.hasDirectAnnotationUsage( OneToMany.class )
|| memberDetails.hasDirectAnnotationUsage( Any.class )
|| memberDetails.hasDirectAnnotationUsage( ManyToAny.class )
|| memberDetails.hasDirectAnnotationUsage( AnyKeyJavaClass.class )
|| memberDetails.hasDirectAnnotationUsage( AnyKeyJavaType.class )
|| memberDetails.hasDirectAnnotationUsage( AnyKeyJdbcType.class )
|| memberDetails.hasDirectAnnotationUsage( AnyKeyJdbcTypeCode.class )
|| memberDetails.hasDirectAnnotationUsage( AnyKeyType.class )
|| memberDetails.hasDirectAnnotationUsage( AnyDiscriminator.class )
|| memberDetails.hasDirectAnnotationUsage( AnyDiscriminatorValue.class )
|| memberDetails.hasDirectAnnotationUsage( AnyDiscriminatorValues.class )
|| memberDetails.hasDirectAnnotationUsage( Column.class )
|| memberDetails.hasDirectAnnotationUsage( Enumerated.class )
|| memberDetails.hasDirectAnnotationUsage( Lob.class )
|| memberDetails.hasDirectAnnotationUsage( Temporal.class )
|| memberDetails.hasDirectAnnotationUsage( Nationalized.class )
|| memberDetails.hasDirectAnnotationUsage( TenantId.class )
|| memberDetails.hasDirectAnnotationUsage( Generated.class )
|| memberDetails.hasDirectAnnotationUsage( TimeZoneColumn.class )
|| memberDetails.hasDirectAnnotationUsage( TimeZoneStorage.class )
|| memberDetails.hasDirectAnnotationUsage( Type.class )
|| memberDetails.hasDirectAnnotationUsage( JavaType.class )
|| memberDetails.hasDirectAnnotationUsage( JdbcType.class )
|| memberDetails.hasDirectAnnotationUsage( JdbcTypeCode.class );
}
private void validateAttributeLevelAccess(
MemberDetails annotationTarget,
AccessType attributeAccessType,
ClassDetails classDetails) {
// 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
// todo (jpa32) : pass along access to JpaCompliance and use a new `JpaCompliance#isAnnotationPlacementComplianceEnabled` method here
// - for now, just allow it as we interpret the actual attribute AccessType value to dictate the state access
if ( !isAnnotationPlacementComplianceEnabled() ) {
return;
}
if ( ( attributeAccessType == AccessType.FIELD && !annotationTarget.isField() )
|| ( attributeAccessType == AccessType.PROPERTY && annotationTarget.isField() ) ) {
throw new AccessTypePlacementException( classDetails, annotationTarget );
}
}
private boolean isAnnotationPlacementComplianceEnabled() {
return false;
}
private void processClassLevelAccess(
Function<String,Boolean> alreadyProcessedChecker,
BiConsumer<String, MemberDetails> memberConsumer,
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
ClassAttributeAccessType classLevelAccessType) {
if ( classLevelAccessType == null ) {
return;
}
if ( classLevelAccessType.getJpaAccessType() == AccessType.FIELD ) {
final List<FieldDetails> 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.getJpaAccessType() == AccessType.PROPERTY;
final List<MethodDetails> 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 );
}
}
}
}

View File

@ -1,26 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
/**
* Support for processing an application's domain model, as known through
* {@linkplain org.hibernate.boot.model.process.spi.ManagedResources} and ultimately
* producing a mildly {@linkplain org.hibernate.boot.models.categorize.spi.CategorizedDomainModel categorized model}
* representing entities, embeddables, etc.
* <p/>
* Happens in 2 steps -<ol>
* <li>
* Create the "source metamodel" - {@linkplain org.hibernate.models.spi.ClassDetails classes},
* {@linkplain org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl XML}, etc.
* </li>
* <li>
* Process this "source metamodel" and produce the {@linkplain org.hibernate.boot.models.categorize.spi.CategorizedDomainModel categorized model}
* </li>
* </ol>
*
* @author Steve Ebersole
*/
package org.hibernate.boot.models.categorize;

View File

@ -1,15 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
/**
* CompositeIdMapping which is physically an embeddable and represented by a single attribute.
*
* @see jakarta.persistence.EmbeddedId
*
* @author Steve Ebersole
*/
public interface AggregatedKeyMapping extends CompositeKeyMapping, SingleAttributeKeyMapping {
}

View File

@ -1,15 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.MemberDetails;
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface AllMemberConsumer {
void acceptMember(MemberDetails memberDetails);
}

View File

@ -1,15 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.internal.util.IndexedConsumer;
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface AttributeConsumer extends IndexedConsumer<AttributeMetadata> {
}

View File

@ -1,46 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.MemberDetails;
/**
* Metadata about a persistent attribute
*
* @author Steve Ebersole
*/
public interface AttributeMetadata extends TableOwner {
/**
* The attribute name
*/
String getName();
/**
* The persistent nature of the attribute
*/
AttributeNature getNature();
/**
* The backing member
*/
MemberDetails getMember();
/**
* An enum defining the nature (categorization) of a persistent attribute.
*
* @see jakarta.persistence.metamodel.Attribute.PersistentAttributeType
*/
enum AttributeNature {
BASIC,
EMBEDDED,
ANY,
TO_ONE,
ELEMENT_COLLECTION,
MANY_TO_ANY,
MANY_TO_MANY,
ONE_TO_MANY
}
}

View File

@ -1,11 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
/**
* @author Steve Ebersole
*/
public interface BasicKeyMapping extends SingleAttributeKeyMapping {
}

View File

@ -1,130 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.boot.CacheRegionDefinition;
import org.hibernate.cache.spi.access.AccessType;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.ModelsException;
/**
* Models the caching options for an entity, natural-id, or collection.
*
* @author Steve Ebersole
* @author Hardy Ferentschik
*/
public class CacheRegion {
private String regionName;
private AccessType accessType;
private boolean cacheLazyProperties;
public CacheRegion(
Cache cacheAnnotation,
AccessType implicitCacheAccessType,
String implicitRegionName) {
if ( cacheAnnotation == null ) {
regionName = implicitRegionName;
accessType = implicitCacheAccessType;
cacheLazyProperties = true;
}
else {
final String explicitRegionName = cacheAnnotation.region();
regionName = StringHelper.isEmpty( explicitRegionName ) ? implicitRegionName : explicitRegionName;
accessType = interpretAccessStrategy( cacheAnnotation.usage() );
// default for includeLazy is true
// default for include is "all"
final boolean includeLazy = cacheAnnotation.includeLazy();
final String include = cacheAnnotation.include();
assert "all".equals( include ) || "non-lazy".equals( include );
cacheLazyProperties = includeLazy && include.equals( "all" ) ;
}
}
private AccessType interpretAccessStrategy(CacheConcurrencyStrategy usage) {
if ( usage == null ) {
return null;
}
switch ( usage ) {
case NONE: {
return null;
}
case READ_ONLY: {
return AccessType.READ_ONLY;
}
case READ_WRITE: {
return AccessType.READ_WRITE;
}
case NONSTRICT_READ_WRITE: {
return AccessType.NONSTRICT_READ_WRITE;
}
case TRANSACTIONAL: {
return AccessType.TRANSACTIONAL;
}
default: {
throw new ModelsException( "Unexpected cache concurrency strategy specified - " + usage );
}
}
}
public String getRegionName() {
return regionName;
}
public void setRegionName(String regionName) {
this.regionName = regionName;
}
public AccessType getAccessType() {
return accessType;
}
public void setAccessType(AccessType accessType) {
this.accessType = accessType;
}
public boolean isCacheLazyProperties() {
return cacheLazyProperties;
}
public void setCacheLazyProperties(boolean cacheLazyProperties) {
this.cacheLazyProperties = cacheLazyProperties;
}
public void overlay(CacheRegionDefinition overrides) {
if ( overrides == null ) {
return;
}
accessType = AccessType.fromExternalName( overrides.getUsage() );
if ( StringHelper.isEmpty( overrides.getRegion() ) ) {
regionName = overrides.getRegion();
}
// ugh, primitive boolean
cacheLazyProperties = overrides.isCacheLazy();
}
public void overlay(CacheRegion overrides) {
if ( overrides == null ) {
return;
}
this.accessType = overrides.accessType;
this.regionName = overrides.regionName;
this.cacheLazyProperties = overrides.cacheLazyProperties;
}
@Override
public String toString() {
return "Caching{" +
"region='" + regionName + '\''
+ ", accessType=" + accessType
+ ", cacheLazyProperties=" + cacheLazyProperties + '}';
}
}

View File

@ -1,101 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.Map;
import java.util.Set;
import org.hibernate.boot.models.spi.GlobalRegistrations;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.internal.util.KeyedConsumer;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
/**
* The application's domain model, understood at a very rudimentary level - we know
* a class is an entity, a mapped-superclass, ... And we know about persistent attributes,
* but again on a very rudimentary level.
* <p/>
* We also know about all {@linkplain #getGlobalRegistrations() global registrations} -
* sequence-generators, named-queries, ...
*
* @author Steve Ebersole
*/
public interface CategorizedDomainModel {
/**
* Registry of all known classes
*/
ClassDetailsRegistry getClassDetailsRegistry();
/**
* Registry of all known {@linkplain java.lang.annotation.Annotation} descriptors (classes)
*/
AnnotationDescriptorRegistry getAnnotationDescriptorRegistry();
PersistenceUnitMetadata getPersistenceUnitMetadata();
/**
* Global registrations collected while processing the persistence-unit.
*/
GlobalRegistrations getGlobalRegistrations();
/**
* All entity hierarchies defined in the persistence unit
*/
Set<EntityHierarchy> getEntityHierarchies();
/**
* Iteration over the {@linkplain #getEntityHierarchies() entity hierarchies}
*/
default void forEachEntityHierarchy(IndexedConsumer<EntityHierarchy> hierarchyConsumer) {
final Set<EntityHierarchy> entityHierarchies = getEntityHierarchies();
if ( entityHierarchies.isEmpty() ) {
return;
}
int pos = 0;
for ( EntityHierarchy entityHierarchy : entityHierarchies ) {
hierarchyConsumer.accept( pos, entityHierarchy );
pos++;
}
}
/**
* All mapped-superclasses defined in the persistence unit
*/
Map<String,ClassDetails> getMappedSuperclasses();
/**
* Iteration over the {@linkplain #getMappedSuperclasses() mapped superclasses}
*/
default void forEachMappedSuperclass(KeyedConsumer<String, ClassDetails> consumer) {
final Map<String, ClassDetails> mappedSuperclasses = getMappedSuperclasses();
if ( mappedSuperclasses.isEmpty() ) {
return;
}
mappedSuperclasses.forEach( consumer::accept );
}
/**
* All embeddables defined in the persistence unit
*/
Map<String,ClassDetails> getEmbeddables();
/**
* Iteration over the {@linkplain #getEmbeddables() embeddables}
*/
default void forEachEmbeddable(KeyedConsumer<String, ClassDetails> consumer) {
final Map<String, ClassDetails> embeddables = getEmbeddables();
if ( embeddables.isEmpty() ) {
return;
}
embeddables.forEach( consumer::accept );
}
}

View File

@ -1,56 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import jakarta.persistence.AccessType;
/**
* Possible class-level {@linkplain jakarta.persistence.AccessType} values.
*
* @author Steve Ebersole
*/
public enum ClassAttributeAccessType {
/**
* The class explicitly defined field access via {@linkplain jakarta.persistence.Access}
*/
EXPLICIT_FIELD(true, AccessType.FIELD),
/**
* The class explicitly defined property access via {@linkplain jakarta.persistence.Access}
*/
EXPLICIT_PROPERTY(true, AccessType.PROPERTY),
/**
* The class implicitly defined field access.
*/
IMPLICIT_FIELD(false, AccessType.FIELD),
/**
* The class implicitly defined property access.
*/
IMPLICIT_PROPERTY(false, AccessType.PROPERTY);
private final boolean explicit;
private final AccessType jpaAccessType;
ClassAttributeAccessType(boolean explicit, AccessType jpaAccessType) {
this.explicit = explicit;
this.jpaAccessType = jpaAccessType;
}
/**
* Whether the access-type was explicitly specified
*/
public boolean isExplicit() {
return explicit;
}
/**
* The corresponding {@linkplain jakarta.persistence.AccessType}
*/
public AccessType getJpaAccessType() {
return jpaAccessType;
}
}

View File

@ -1,14 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
/**
* Id-mapping which is embeddable - either {@linkplain AggregatedKeyMapping physically}
* or {@linkplain NonAggregatedKeyMapping virtually}.
*
* @author Steve Ebersole
*/
public interface CompositeKeyMapping extends KeyMapping {
}

View File

@ -1,82 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.engine.OptimisticLockStyle;
import jakarta.persistence.AccessType;
import jakarta.persistence.InheritanceType;
/**
* Models an entity hierarchy comprised of {@linkplain EntityTypeMetadata entity}
* and {@linkplain MappedSuperclassTypeMetadata mapped-superclass} types.
*
* @author Steve Ebersole
*/
public interface EntityHierarchy {
/**
* The hierarchy's root type.
*/
EntityTypeMetadata getRoot();
/**
* The absolute root of the hierarchy, which might be a mapped-superclass
* above the {@linkplain #getRoot() root entity}
*/
IdentifiableTypeMetadata getAbsoluteRoot();
/**
* Visit each type in the hierarchy, top down starting from {@linkplain #getAbsoluteRoot()}
*/
void forEachType(HierarchyTypeVisitor typeVisitor);
/**
* The default access-type for the hierarchy. See section <i>2.3.1 Default Access Type</i>
* of the Jakarta Persistence specification.
*/
AccessType getDefaultAccessType();
/**
* The inheritance strategy for the hierarchy.
*/
InheritanceType getInheritanceType();
KeyMapping getIdMapping();
KeyMapping getNaturalIdMapping();
AttributeMetadata getVersionAttribute();
AttributeMetadata getTenantIdAttribute();
/**
* Style of optimistic locking for the hierarchy.
*/
OptimisticLockStyle getOptimisticLockStyle();
/**
* The caching configuration for entities in this hierarchy.
*/
CacheRegion getCacheRegion();
/**
* The caching configuration for this hierarchy's {@linkplain org.hibernate.annotations.NaturalId natural-id}
*/
NaturalIdCacheRegion getNaturalIdCacheRegion();
/**
* Describes a type's place in the hierarchy relative to the {@linkplain #getRoot() root entity}
*/
enum HierarchyRelation { SUPER, ROOT, SUB }
@FunctionalInterface
interface HierarchyTypeVisitor {
void visitType(
IdentifiableTypeMetadata type,
IdentifiableTypeMetadata superType,
EntityHierarchy hierarchy,
HierarchyRelation relation);
}
}

View File

@ -1,87 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.Map;
import org.hibernate.boot.model.CustomSql;
import org.hibernate.boot.model.naming.EntityNaming;
/**
* Metadata about an {@linkplain jakarta.persistence.metamodel.EntityType entity type}
*
* @author Steve Ebersole
*/
public interface EntityTypeMetadata extends IdentifiableTypeMetadata, EntityNaming {
@Override
default Kind getManagedTypeKind() {
return Kind.ENTITY;
}
/**
* The Hibernate notion of entity-name, used for dynamic models
*/
String getEntityName();
/**
* The JPA notion of entity-name, used for HQL references (import)
*/
String getJpaEntityName();
/**
* Whether the state of the entity is written to the database (mutable) or not (immutable)
*/
boolean isMutable();
/**
* Whether this entity is cacheable.
*
* @see jakarta.persistence.Cacheable
* @see org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor#getSharedCacheMode()
*/
boolean isCacheable();
/**
* Any tables to which this entity maps that Hibernate does not know about.
*
* @see org.hibernate.annotations.View
* @see org.hibernate.annotations.Subselect
*/
String[] getSynchronizedTableNames();
/**
* A size to use for the entity with batch loading
*/
int getBatchSize();
/**
* Whether to perform dynamic inserts.
*
* @see org.hibernate.annotations.DynamicInsert
*/
boolean isDynamicInsert();
/**
* Whether to perform dynamic updates.
*
* @see org.hibernate.annotations.DynamicUpdate
*/
boolean isDynamicUpdate();
/**
* Custom SQL to perform an INSERT of this entity
*/
Map<String, CustomSql> getCustomInserts();
/**
* Custom SQL to perform an UPDATE of this entity
*/
Map<String, CustomSql> getCustomUpdates();
/**
* Custom SQL to perform an DELETE of this entity
*/
Map<String, CustomSql> getCustomDeletes();
}

View File

@ -1,71 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.List;
import java.util.function.Consumer;
import org.hibernate.boot.models.spi.JpaEventListener;
/**
* Metadata about an {@linkplain jakarta.persistence.metamodel.IdentifiableType identifiable type}
*
* @author Steve Ebersole
*/
public interface IdentifiableTypeMetadata extends ManagedTypeMetadata, TableOwner {
/**
* The hierarchy in which this IdentifiableType occurs.
*/
EntityHierarchy getHierarchy();
/**
* The super-type, if one
*/
IdentifiableTypeMetadata getSuperType();
/**
* Whether this type is considered abstract.
*/
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<IdentifiableTypeMetadata> getSubTypes();
/**
* Visit each direct subtype
*/
void forEachSubType(Consumer<IdentifiableTypeMetadata> consumer);
/**
* Event listeners in effect for this type, minus
* {@linkplain jakarta.persistence.ExcludeDefaultListeners default listeners}.
*
* @apiNote Kept separate from {@linkplain #getCompleteJpaEventListeners()}
* to facilitate types building their complete set with their
* {@linkplain jakarta.persistence.ExcludeSuperclassListeners superclass listeners}.
*/
List<JpaEventListener> getHierarchyJpaEventListeners();
/**
* Event listeners in effect for this type, including
* {@linkplain jakarta.persistence.ExcludeDefaultListeners default listeners}
*/
List<JpaEventListener> getCompleteJpaEventListeners();
}

View File

@ -1,18 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.ClassDetails;
/**
* @author Steve Ebersole
*/
public interface KeyMapping {
ClassDetails getKeyType();
void forEachAttribute(AttributeConsumer consumer);
boolean contains(AttributeMetadata attributeMetadata);
}

View File

@ -1,230 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.hibernate.Internal;
import org.hibernate.boot.internal.MetadataBuilderImpl;
import org.hibernate.boot.internal.RootMappingDefaults;
import org.hibernate.boot.model.process.spi.ManagedResources;
import org.hibernate.boot.models.categorize.ModelCategorizationLogging;
import org.hibernate.boot.models.categorize.internal.ClassLoaderServiceLoading;
import org.hibernate.boot.models.internal.DomainModelCategorizationCollector;
import org.hibernate.boot.models.internal.GlobalRegistrationsImpl;
import org.hibernate.boot.models.categorize.internal.ModelCategorizationContextImpl;
import org.hibernate.boot.models.internal.ModelsHelper;
import org.hibernate.boot.models.xml.internal.PersistenceUnitMetadataImpl;
import org.hibernate.boot.models.xml.spi.XmlPreProcessingResult;
import org.hibernate.boot.models.xml.spi.XmlPreProcessor;
import org.hibernate.boot.models.xml.spi.XmlProcessingResult;
import org.hibernate.boot.models.xml.spi.XmlProcessor;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.models.internal.BasicModelBuildingContextImpl;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import static org.hibernate.boot.models.categorize.internal.EntityHierarchyBuilder.createEntityHierarchies;
import static org.hibernate.internal.util.collections.CollectionHelper.mutableJoin;
/**
* Processes a {@linkplain ManagedResources} (classes, mapping, etc.) and
* produces a {@linkplain CategorizedDomainModel categorized domain model}
*
* @author Steve Ebersole
*/
public class ManagedResourcesProcessor {
public static CategorizedDomainModel processManagedResources(
ManagedResources managedResources,
MetadataBuildingOptions metadataBuildingOptions,
BootstrapContext bootstrapContext) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// - pre-process the XML
// - collect all known classes
// - resolve (possibly building) Jandex index
// - build the SourceModelBuildingContext
//
// INPUTS:
// - serviceRegistry
// - managedResources
// - bootstrapContext (supplied Jandex index, if one)
//
// OUTPUTS:
// - xmlPreProcessingResult
// - allKnownClassNames (technically could be included in xmlPreProcessingResult)
// - sourceModelBuildingContext
final ClassLoaderService classLoaderService = bootstrapContext.getServiceRegistry().getService( ClassLoaderService.class );
final ClassLoaderServiceLoading classLoading = new ClassLoaderServiceLoading( classLoaderService );
final PersistenceUnitMetadataImpl persistenceUnitMetadata = new PersistenceUnitMetadataImpl();
final XmlPreProcessingResult xmlPreProcessingResult = XmlPreProcessor.preProcessXmlResources(
managedResources,
persistenceUnitMetadata
);
//noinspection unchecked
final List<String> allKnownClassNames = mutableJoin(
managedResources.getAnnotatedClassReferences().stream().map( Class::getName ).collect( Collectors.toList() ),
managedResources.getAnnotatedClassNames(),
xmlPreProcessingResult.getMappedClasses()
);
managedResources.getAnnotatedPackageNames().forEach( (packageName) -> {
try {
final Class<?> packageInfoClass = classLoading.classForName( packageName + ".package-info" );
allKnownClassNames.add( packageInfoClass.getName() );
}
catch (ClassLoadingException classLoadingException) {
// no package-info, so there can be no annotations... just skip it
}
} );
managedResources.getAnnotatedClassReferences().forEach( (clazz) -> allKnownClassNames.add( clazz.getName() ) );
// At this point we know all managed class names across all sources.
// Resolve the Jandex Index and build the SourceModelBuildingContext.
final BasicModelBuildingContextImpl sourceModelBuildingContext = new BasicModelBuildingContextImpl(
classLoading,
ModelsHelper::preFillRegistries
);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// - process metadata-complete XML
// - collect overlay XML
// - process annotations (including those from metadata-complete XML)
// - apply overlay XML
//
// INPUTS:
// - "options" (areIdGeneratorsGlobal, etc)
// - xmlPreProcessingResult
// - sourceModelBuildingContext
//
// OUTPUTS
// - rootEntities
// - mappedSuperClasses
// - embeddables
// JPA id generator global-ity thing
final boolean areIdGeneratorsGlobal = true;
final ClassDetailsRegistry classDetailsRegistry = sourceModelBuildingContext.getClassDetailsRegistry();
final AnnotationDescriptorRegistry descriptorRegistry = sourceModelBuildingContext.getAnnotationDescriptorRegistry();
final GlobalRegistrationsImpl globalRegistrations = new GlobalRegistrationsImpl( sourceModelBuildingContext, bootstrapContext );
final DomainModelCategorizationCollector modelCategorizationCollector = new DomainModelCategorizationCollector(
areIdGeneratorsGlobal,
globalRegistrations,
sourceModelBuildingContext
);
final RootMappingDefaults rootMappingDefaults = new RootMappingDefaults(
metadataBuildingOptions.getMappingDefaults(),
persistenceUnitMetadata
);
final XmlProcessingResult xmlProcessingResult = XmlProcessor.processXml(
xmlPreProcessingResult,
modelCategorizationCollector,
sourceModelBuildingContext,
bootstrapContext,
rootMappingDefaults
);
allKnownClassNames.forEach( (className) -> {
final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails( className );
modelCategorizationCollector.apply( classDetails );
} );
xmlPreProcessingResult.getMappedNames().forEach( (className) -> {
final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails( className );
modelCategorizationCollector.apply( classDetails );
} );
xmlProcessingResult.apply( xmlPreProcessingResult.getPersistenceUnitMetadata() );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// - create entity-hierarchies
// - create the CategorizedDomainModel
//
// INPUTS:
// - rootEntities
// - mappedSuperClasses
// - embeddables
//
// OUTPUTS:
// - CategorizedDomainModel
// Collect the entity hierarchies based on the set of `rootEntities`
final ModelCategorizationContextImpl mappingBuildingContext = new ModelCategorizationContextImpl(
classDetailsRegistry,
descriptorRegistry,
globalRegistrations
);
final Set<EntityHierarchy> entityHierarchies;
if ( ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER.isDebugEnabled() ) {
final Map<String,ClassDetails> unusedMappedSuperClasses = new HashMap<>( modelCategorizationCollector.getMappedSuperclasses() );
entityHierarchies = createEntityHierarchies(
modelCategorizationCollector.getRootEntities(),
(identifiableType) -> {
if ( identifiableType instanceof MappedSuperclassTypeMetadata ) {
unusedMappedSuperClasses.remove( identifiableType.getClassDetails().getClassName() );
}
},
mappingBuildingContext
);
warnAboutUnusedMappedSuperclasses( unusedMappedSuperClasses );
}
else {
entityHierarchies = createEntityHierarchies(
modelCategorizationCollector.getRootEntities(),
ManagedResourcesProcessor::ignore,
mappingBuildingContext
);
}
return modelCategorizationCollector.createResult(
entityHierarchies,
xmlPreProcessingResult.getPersistenceUnitMetadata(),
classDetailsRegistry,
descriptorRegistry
);
}
private static void ignore(IdentifiableTypeMetadata identifiableTypeMetadata) {
}
private static void warnAboutUnusedMappedSuperclasses(Map<String, ClassDetails> mappedSuperClasses) {
assert ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER.isDebugEnabled();
for ( Map.Entry<String, ClassDetails> entry : mappedSuperClasses.entrySet() ) {
ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER.debugf(
"Encountered MappedSuperclass [%s] which was unused in any entity hierarchies",
entry.getKey()
);
}
}
/**
* For testing use only
*/
@Internal
public static CategorizedDomainModel processManagedResources(
ManagedResources managedResources,
BootstrapContext bootstrapContext) {
return processManagedResources(
managedResources,
new MetadataBuilderImpl.MetadataBuildingOptionsImpl( bootstrapContext.getServiceRegistry() ),
bootstrapContext
);
}
}

View File

@ -1,50 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.Collection;
import org.hibernate.internal.util.IndexedConsumer;
import org.hibernate.models.spi.ClassDetails;
/**
* Metadata about a {@linkplain jakarta.persistence.metamodel.ManagedType managed type}
*
* @author Steve Ebersole
*/
public interface ManagedTypeMetadata {
enum Kind { ENTITY, MAPPED_SUPER, EMBEDDABLE }
Kind getManagedTypeKind();
/**
* The underlying managed-class
*/
ClassDetails getClassDetails();
/**
* The class-level access type
*/
ClassAttributeAccessType getClassLevelAccessType();
/**
* Get the number of declared attributes
*/
int getNumberOfAttributes();
/**
* Get the declared attributes
*/
Collection<AttributeMetadata> getAttributes();
AttributeMetadata findAttribute(String name);
/**
* Visit each declared attributes
*/
void forEachAttribute(IndexedConsumer<AttributeMetadata> consumer);
}

View File

@ -1,17 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
/**
* Metadata about a {@linkplain jakarta.persistence.metamodel.MappedSuperclassType mapped-superclass}
*
* @author Steve Ebersole
*/
public interface MappedSuperclassTypeMetadata extends IdentifiableTypeMetadata {
@Override
default Kind getManagedTypeKind() {
return Kind.MAPPED_SUPER;
}
}

View File

@ -1,33 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.List;
import org.hibernate.boot.models.categorize.internal.StandardPersistentAttributeMemberResolver;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetailsRegistry;
import jakarta.persistence.SharedCacheMode;
/**
* Contextual information used while building {@linkplain ManagedTypeMetadata} and friends.
*
* @author Steve Ebersole
*/
public interface ModelCategorizationContext {
ClassDetailsRegistry getClassDetailsRegistry();
AnnotationDescriptorRegistry getAnnotationDescriptorRegistry();
SharedCacheMode getSharedCacheMode();
default PersistentAttributeMemberResolver getPersistentAttributeMemberResolver() {
return StandardPersistentAttributeMemberResolver.INSTANCE;
}
List<JpaEventListener> getDefaultEventListeners();
}

View File

@ -1,39 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.internal.util.StringHelper;
/**
* Details about caching related to the natural-id of an entity
*
* @see CacheRegion
*
* @author Steve Ebersole
*/
public class NaturalIdCacheRegion {
private final String regionName;
public NaturalIdCacheRegion(NaturalIdCache cacheAnnotation, CacheRegion cacheRegion) {
this.regionName = determineRegionName( cacheAnnotation, cacheRegion );
}
private static String determineRegionName(NaturalIdCache cacheAnnotation, CacheRegion cacheRegion) {
if ( cacheAnnotation != null ) {
final String explicitRegionName = cacheAnnotation.region();
if ( StringHelper.isNotEmpty( explicitRegionName ) ) {
return explicitRegionName;
}
}
// use the default value
return cacheRegion.getRegionName() + "##NaturalId";
}
public String getRegionName() {
return regionName;
}
}

View File

@ -1,34 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.List;
import org.hibernate.models.spi.ClassDetails;
/**
* CompositeIdMapping which is virtually an embeddable and represented by one-or-more
* {@linkplain #getIdAttributes id-attributes} identified by one-or-more {@code @Id}
* annotations.
* Also defines an {@linkplain #getIdClassType() id-class} which is used for loading.
* @see jakarta.persistence.Id
* @see jakarta.persistence.IdClass
*
* @author Steve Ebersole
*/
public interface NonAggregatedKeyMapping extends CompositeKeyMapping {
/**
* The attributes making up the composition.
*/
List<AttributeMetadata> getIdAttributes();
/**
* Details about the {@linkplain jakarta.persistence.IdClass id-class}.
*
* @see jakarta.persistence.IdClass
*/
ClassDetails getIdClassType();
}

View File

@ -1,41 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.List;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
/**
* Contract responsible for resolving the members that identify the persistent
* attributes for a given class descriptor representing a managed type.
* <p/>
* These members (field or method) would be where we look for mapping annotations
* for the attribute.
* <p/>
* 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 allMemberConsumer Optional callback for each member on the class
*
* @return The list of "backing members"
*/
List<MemberDetails> resolveAttributesMembers(
ClassDetails classDetails,
ClassAttributeAccessType classLevelAccessType,
AllMemberConsumer allMemberConsumer);
}

View File

@ -1,32 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.ClassDetails;
/**
* @author Steve Ebersole
*/
public interface SingleAttributeKeyMapping extends KeyMapping {
AttributeMetadata getAttribute();
default String getAttributeName() {
return getAttribute().getName();
}
default ClassDetails getKeyType() {
return getAttribute().getMember().getType().determineRawClass();
}
@Override
default void forEachAttribute(AttributeConsumer consumer) {
consumer.accept( 0, getAttribute() );
}
@Override
default boolean contains(AttributeMetadata attributeMetadata) {
return attributeMetadata == getAttribute();
}
}

View File

@ -1,13 +0,0 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
/**
* Marker for things which can "own" a table
*
* @author Steve Ebersole
*/
public interface TableOwner {
}

View File

@ -2,7 +2,7 @@
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
package org.hibernate.boot.models.internal;
import java.net.URL;
import java.util.Collection;

View File

@ -4,38 +4,30 @@
*/
package org.hibernate.boot.models.internal;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerContainerImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitDefaultsImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitMetadataImpl;
import org.hibernate.boot.models.categorize.internal.CategorizedDomainModelImpl;
import org.hibernate.boot.models.categorize.spi.CategorizedDomainModel;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.spi.GlobalRegistrations;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.SourceModelBuildingContext;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Converter;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.MappedSuperclass;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerContainerImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitDefaultsImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitMetadataImpl;
import org.hibernate.boot.models.spi.GlobalRegistrations;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* In-flight holder for various things as we process metadata sources
*
* @author Steve Ebersole
*/
public class DomainModelCategorizationCollector {
private final boolean areIdGeneratorsGlobal;
@ -162,20 +154,4 @@ public class DomainModelCategorizationCollector {
// if we hit no opt-outs we have a root
return true;
}
public CategorizedDomainModel createResult(
Set<EntityHierarchy> entityHierarchies,
PersistenceUnitMetadata persistenceUnitMetadata,
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry annotationDescriptorRegistry) {
return new CategorizedDomainModelImpl(
classDetailsRegistry,
annotationDescriptorRegistry,
persistenceUnitMetadata,
entityHierarchies,
mappedSuperclasses,
embeddables,
getGlobalRegistrations()
);
}
}

View File

@ -61,7 +61,7 @@ import org.hibernate.boot.models.spi.GlobalRegistrations;
import org.hibernate.boot.models.spi.JavaTypeRegistration;
import org.hibernate.boot.models.spi.JdbcTypeRegistration;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.JpaEventListenerStyle;
import org.hibernate.boot.models.spi.NamedNativeQueryRegistration;
import org.hibernate.boot.models.spi.NamedQueryRegistration;
import org.hibernate.boot.models.spi.NamedStoredProcedureQueryRegistration;

View File

@ -4,11 +4,8 @@
*/
package org.hibernate.boot.models.spi;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Objects;
import org.hibernate.boot.spi.ClassmateContext;
import com.fasterxml.classmate.ResolvedType;
import jakarta.persistence.AttributeConverter;
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorBypassedImpl;
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorStandardImpl;
import org.hibernate.boot.model.convert.internal.ConverterHelper;
@ -16,7 +13,7 @@ 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.boot.models.Copied;
import org.hibernate.boot.spi.ClassmateContext;
import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.descriptor.converter.internal.JpaAttributeConverterImpl;
@ -24,21 +21,21 @@ 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;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Objects;
/**
* A registered conversion.
*
* @see org.hibernate.annotations.ConverterRegistration
*
* @todo copied from RegisteredConversion because of the "early" creation of `ConverterDescriptor`
* upstream. Technically the conversion from ClassDetails to Class should be fine since
* conversions are only valid for basic types which we will never enhance.
* @apiNote Largely a copy of {@linkplain RegisteredConversion} to avoid early creation of
* {@linkplain ConverterDescriptor}. Technically the conversion from ClassDetails to Class
* should be fine since conversions are only valid for basic types which we will never enhance.
*
* @author Steve Ebersole
*/
@Copied(RegisteredConversion.class)
public class ConversionRegistration {
private final Class<?> explicitDomainType;
private final Class<? extends AttributeConverter<?,?>> converterType;

View File

@ -6,7 +6,7 @@ package org.hibernate.boot.models.spi;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitDefaultsImpl;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.JpaEventListenerStyle;
import org.hibernate.internal.util.MutableObject;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.ClassDetails;

View File

@ -118,7 +118,7 @@ import org.hibernate.boot.models.annotations.internal.UniqueConstraintJpaAnnotat
import org.hibernate.boot.models.annotations.internal.UuidGeneratorAnnotation;
import org.hibernate.boot.models.annotations.spi.CustomSqlDetails;
import org.hibernate.boot.models.annotations.spi.DatabaseObjectDetails;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.JpaEventListenerStyle;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.boot.models.xml.internal.db.ForeignKeyProcessing;
import org.hibernate.boot.models.xml.internal.db.JoinColumnProcessing;

View File

@ -31,7 +31,7 @@ import org.hibernate.boot.jaxb.spi.JaxbBindableMappingDescriptor;
import org.hibernate.boot.model.process.internal.ManagedResourcesImpl;
import org.hibernate.boot.model.process.spi.ManagedResources;
import org.hibernate.boot.model.process.spi.MetadataBuildingProcess;
import org.hibernate.boot.models.categorize.internal.ClassLoaderServiceLoading;
import org.hibernate.boot.models.internal.ClassLoaderServiceLoading;
import org.hibernate.boot.models.internal.DomainModelCategorizationCollector;
import org.hibernate.boot.models.internal.GlobalRegistrationsImpl;
import org.hibernate.boot.models.internal.ModelsHelper;

View File

@ -4,23 +4,27 @@
*/
package org.hibernate.orm.test.boot.models.xml.globals;
import java.util.List;
import org.hibernate.boot.internal.BootstrapContextImpl;
import org.hibernate.boot.internal.InFlightMetadataCollectorImpl;
import org.hibernate.boot.internal.MetadataBuilderImpl;
import org.hibernate.boot.internal.MetadataBuildingContextRootImpl;
import org.hibernate.boot.model.process.spi.ManagedResources;
import org.hibernate.boot.model.process.spi.MetadataBuildingProcess;
import org.hibernate.boot.model.source.internal.annotations.AdditionalManagedResourcesImpl;
import org.hibernate.boot.models.categorize.spi.CategorizedDomainModel;
import org.hibernate.boot.model.source.internal.annotations.DomainModelSource;
import org.hibernate.boot.models.spi.JpaEventListener;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.models.spi.MethodDetails;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.boot.models.categorize.spi.ManagedResourcesProcessor.processManagedResources;
import static org.hibernate.boot.model.process.spi.MetadataBuildingProcess.coordinateProcessors;
import static org.hibernate.models.spi.ClassDetails.VOID_CLASS_DETAILS;
/**
@ -35,25 +39,61 @@ public class JpaEventListenerTests {
.addXmlMappings( "mappings/models/globals.xml" )
.build();
final StandardServiceRegistry serviceRegistry = registryScope.getRegistry();
final BootstrapContextImpl bootstrapContext = new BootstrapContextImpl(
serviceRegistry,
new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry )
);
final CategorizedDomainModel categorizedDomainModel = processManagedResources(
managedResources,
bootstrapContext
);
final List<JpaEventListener> registrations = categorizedDomainModel
.getGlobalRegistrations()
.getEntityListenerRegistrations();
final InFlightMetadataCollector metadataCollector = buildMetadataCollector( managedResources, registryScope );
final List<JpaEventListener> registrations = metadataCollector.getGlobalRegistrations().getEntityListenerRegistrations();
assertThat( registrations ).hasSize( 1 );
final JpaEventListener registration = registrations.get( 0 );
final MethodDetails postPersistMethod = registration.getPostPersistMethod();
assertThat( postPersistMethod ).isNotNull();
assertThat( postPersistMethod.getReturnType() ).isEqualTo( VOID_CLASS_DETAILS );
assertThat( postPersistMethod.getArgumentTypes() ).hasSize( 1 );
}
private InFlightMetadataCollector buildMetadataCollector(ManagedResources managedResources, ServiceRegistryScope registryScope) {
final StandardServiceRegistry serviceRegistry = registryScope.getRegistry();
final ClassLoaderService classLoaderService = serviceRegistry.requireService( ClassLoaderService.class );
assert classLoaderService != null;
final MetadataBuilderImpl.MetadataBuildingOptionsImpl options = new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry );
final BootstrapContextImpl bootstrapContext = new BootstrapContextImpl( serviceRegistry, options );
options.setBootstrapContext( bootstrapContext );
final InFlightMetadataCollectorImpl metadataCollector = new InFlightMetadataCollectorImpl( bootstrapContext, options );
final DomainModelSource domainModelSource = MetadataBuildingProcess.processManagedResources(
managedResources,
metadataCollector,
bootstrapContext,
options.getMappingDefaults()
);
final MetadataBuildingContextRootImpl rootMetadataBuildingContext = new MetadataBuildingContextRootImpl(
"orm",
bootstrapContext,
options,
metadataCollector,
domainModelSource.getEffectiveMappingDefaults()
);
managedResources.getAttributeConverterDescriptors().forEach( metadataCollector::addAttributeConverter );
bootstrapContext.getTypeConfiguration().scope( rootMetadataBuildingContext );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Set up the processors and start binding
// NOTE : this becomes even more simplified after we move purely
// to unified model
// final IndexView jandexView = domainModelSource.getJandexIndex();
coordinateProcessors(
managedResources,
options,
rootMetadataBuildingContext,
domainModelSource,
metadataCollector
);
return metadataCollector;
}
}