HHH-18534 - Remove the org.hibernate.boot.models.categorize package
This commit is contained in:
parent
f65f393a01
commit
ec5bbe4546
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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...
|
|
@ -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 = ",";
|
||||
|
|
|
@ -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 );
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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 + "`)";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {}
|
||||
}
|
|
@ -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 );
|
||||
} );
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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 {
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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> {
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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 + '}';
|
||||
}
|
||||
|
||||
}
|
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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);
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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;
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue