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.relational.SqlStringGenerationContext;
|
||||||
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
|
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
|
||||||
import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext;
|
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.GlobalRegistrationsImpl;
|
||||||
import org.hibernate.boot.models.internal.ModelsHelper;
|
import org.hibernate.boot.models.internal.ModelsHelper;
|
||||||
import org.hibernate.boot.models.spi.GlobalRegistrations;
|
import org.hibernate.boot.models.spi.GlobalRegistrations;
|
||||||
|
|
|
@ -230,7 +230,8 @@ public class MetadataBuildingProcess {
|
||||||
return metadataCollector.buildMetadataInstance( rootMetadataBuildingContext );
|
return metadataCollector.buildMetadataInstance( rootMetadataBuildingContext );
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void coordinateProcessors(
|
@Internal
|
||||||
|
public static void coordinateProcessors(
|
||||||
ManagedResources managedResources,
|
ManagedResources managedResources,
|
||||||
MetadataBuildingOptions options,
|
MetadataBuildingOptions options,
|
||||||
MetadataBuildingContextRootImpl rootMetadataBuildingContext,
|
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
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
* Copyright Red Hat Inc. and Hibernate Authors
|
* 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...
|
* JPA defines 2 ways events callbacks can happen...
|
|
@ -6,20 +6,21 @@ package org.hibernate.boot.models;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
|
|
||||||
import org.hibernate.MappingException;
|
import org.hibernate.AnnotationException;
|
||||||
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
|
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
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
public class MultipleAttributeNaturesException extends MappingException {
|
@Incubating
|
||||||
|
public class MultipleAttributeNaturesException extends AnnotationException {
|
||||||
private final String attributeName;
|
private final String attributeName;
|
||||||
|
|
||||||
public MultipleAttributeNaturesException(
|
public MultipleAttributeNaturesException(
|
||||||
String attributeName,
|
String attributeName,
|
||||||
EnumSet<AttributeMetadata.AttributeNature> natures) {
|
EnumSet<AttributeNature> natures) {
|
||||||
super( craftMessage( attributeName, natures ) );
|
super( craftMessage( attributeName, natures ) );
|
||||||
this.attributeName = attributeName;
|
this.attributeName = attributeName;
|
||||||
}
|
}
|
||||||
|
@ -28,12 +29,12 @@ public class MultipleAttributeNaturesException extends MappingException {
|
||||||
return attributeName;
|
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 `" )
|
final StringBuilder buffer = new StringBuilder( "Attribute `" )
|
||||||
.append( attributeName )
|
.append( attributeName )
|
||||||
.append( "` expressed multiple natures [" );
|
.append( "` expressed multiple natures [" );
|
||||||
String separator = "";
|
String separator = "";
|
||||||
for ( AttributeMetadata.AttributeNature nature : natures ) {
|
for ( AttributeNature nature : natures ) {
|
||||||
buffer.append( separator );
|
buffer.append( separator );
|
||||||
buffer.append( nature.name() );
|
buffer.append( nature.name() );
|
||||||
separator = ",";
|
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
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
* Copyright Red Hat Inc. and Hibernate Authors
|
* 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.net.URL;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
|
@ -4,38 +4,30 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.boot.models.internal;
|
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.AttributeConverter;
|
||||||
import jakarta.persistence.Converter;
|
import jakarta.persistence.Converter;
|
||||||
import jakarta.persistence.Embeddable;
|
import jakarta.persistence.Embeddable;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.MappedSuperclass;
|
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
|
* In-flight holder for various things as we process metadata sources
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class DomainModelCategorizationCollector {
|
public class DomainModelCategorizationCollector {
|
||||||
private final boolean areIdGeneratorsGlobal;
|
private final boolean areIdGeneratorsGlobal;
|
||||||
|
|
||||||
|
@ -162,20 +154,4 @@ public class DomainModelCategorizationCollector {
|
||||||
// if we hit no opt-outs we have a root
|
// if we hit no opt-outs we have a root
|
||||||
return true;
|
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.JavaTypeRegistration;
|
||||||
import org.hibernate.boot.models.spi.JdbcTypeRegistration;
|
import org.hibernate.boot.models.spi.JdbcTypeRegistration;
|
||||||
import org.hibernate.boot.models.spi.JpaEventListener;
|
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.NamedNativeQueryRegistration;
|
||||||
import org.hibernate.boot.models.spi.NamedQueryRegistration;
|
import org.hibernate.boot.models.spi.NamedQueryRegistration;
|
||||||
import org.hibernate.boot.models.spi.NamedStoredProcedureQueryRegistration;
|
import org.hibernate.boot.models.spi.NamedStoredProcedureQueryRegistration;
|
||||||
|
|
|
@ -4,11 +4,8 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.boot.models.spi;
|
package org.hibernate.boot.models.spi;
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
import com.fasterxml.classmate.ResolvedType;
|
||||||
import java.util.List;
|
import jakarta.persistence.AttributeConverter;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import org.hibernate.boot.spi.ClassmateContext;
|
|
||||||
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorBypassedImpl;
|
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorBypassedImpl;
|
||||||
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorStandardImpl;
|
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorStandardImpl;
|
||||||
import org.hibernate.boot.model.convert.internal.ConverterHelper;
|
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.ConverterDescriptor;
|
||||||
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
|
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
|
||||||
import org.hibernate.boot.model.convert.spi.RegisteredConversion;
|
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.models.spi.AnnotationDescriptor;
|
||||||
import org.hibernate.resource.beans.spi.ManagedBean;
|
import org.hibernate.resource.beans.spi.ManagedBean;
|
||||||
import org.hibernate.type.descriptor.converter.internal.JpaAttributeConverterImpl;
|
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.descriptor.java.spi.JavaTypeRegistry;
|
||||||
import org.hibernate.type.spi.TypeConfiguration;
|
import org.hibernate.type.spi.TypeConfiguration;
|
||||||
|
|
||||||
import com.fasterxml.classmate.ResolvedType;
|
import java.lang.annotation.Annotation;
|
||||||
import jakarta.persistence.AttributeConverter;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A registered conversion.
|
* A registered conversion.
|
||||||
*
|
*
|
||||||
* @see org.hibernate.annotations.ConverterRegistration
|
* @see org.hibernate.annotations.ConverterRegistration
|
||||||
*
|
*
|
||||||
* @todo copied from RegisteredConversion because of the "early" creation of `ConverterDescriptor`
|
* @apiNote Largely a copy of {@linkplain RegisteredConversion} to avoid early creation of
|
||||||
* upstream. Technically the conversion from ClassDetails to Class should be fine since
|
* {@linkplain ConverterDescriptor}. Technically the conversion from ClassDetails to Class
|
||||||
* conversions are only valid for basic types which we will never enhance.
|
* should be fine since conversions are only valid for basic types which we will never enhance.
|
||||||
*
|
*
|
||||||
* @author Steve Ebersole
|
* @author Steve Ebersole
|
||||||
*/
|
*/
|
||||||
@Copied(RegisteredConversion.class)
|
|
||||||
public class ConversionRegistration {
|
public class ConversionRegistration {
|
||||||
private final Class<?> explicitDomainType;
|
private final Class<?> explicitDomainType;
|
||||||
private final Class<? extends AttributeConverter<?,?>> converterType;
|
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.JaxbEntityListenerImpl;
|
||||||
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitDefaultsImpl;
|
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.internal.util.MutableObject;
|
||||||
import org.hibernate.models.ModelsException;
|
import org.hibernate.models.ModelsException;
|
||||||
import org.hibernate.models.spi.ClassDetails;
|
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.internal.UuidGeneratorAnnotation;
|
||||||
import org.hibernate.boot.models.annotations.spi.CustomSqlDetails;
|
import org.hibernate.boot.models.annotations.spi.CustomSqlDetails;
|
||||||
import org.hibernate.boot.models.annotations.spi.DatabaseObjectDetails;
|
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.spi.JpaEventListener;
|
||||||
import org.hibernate.boot.models.xml.internal.db.ForeignKeyProcessing;
|
import org.hibernate.boot.models.xml.internal.db.ForeignKeyProcessing;
|
||||||
import org.hibernate.boot.models.xml.internal.db.JoinColumnProcessing;
|
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.internal.ManagedResourcesImpl;
|
||||||
import org.hibernate.boot.model.process.spi.ManagedResources;
|
import org.hibernate.boot.model.process.spi.ManagedResources;
|
||||||
import org.hibernate.boot.model.process.spi.MetadataBuildingProcess;
|
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.DomainModelCategorizationCollector;
|
||||||
import org.hibernate.boot.models.internal.GlobalRegistrationsImpl;
|
import org.hibernate.boot.models.internal.GlobalRegistrationsImpl;
|
||||||
import org.hibernate.boot.models.internal.ModelsHelper;
|
import org.hibernate.boot.models.internal.ModelsHelper;
|
||||||
|
|
|
@ -4,23 +4,27 @@
|
||||||
*/
|
*/
|
||||||
package org.hibernate.orm.test.boot.models.xml.globals;
|
package org.hibernate.orm.test.boot.models.xml.globals;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.hibernate.boot.internal.BootstrapContextImpl;
|
import org.hibernate.boot.internal.BootstrapContextImpl;
|
||||||
|
import org.hibernate.boot.internal.InFlightMetadataCollectorImpl;
|
||||||
import org.hibernate.boot.internal.MetadataBuilderImpl;
|
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.ManagedResources;
|
||||||
|
import org.hibernate.boot.model.process.spi.MetadataBuildingProcess;
|
||||||
import org.hibernate.boot.model.source.internal.annotations.AdditionalManagedResourcesImpl;
|
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.models.spi.JpaEventListener;
|
||||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
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.models.spi.MethodDetails;
|
||||||
|
|
||||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||||
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
|
import org.hibernate.testing.orm.junit.ServiceRegistryScope;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
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;
|
import static org.hibernate.models.spi.ClassDetails.VOID_CLASS_DETAILS;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,25 +39,61 @@ public class JpaEventListenerTests {
|
||||||
.addXmlMappings( "mappings/models/globals.xml" )
|
.addXmlMappings( "mappings/models/globals.xml" )
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final StandardServiceRegistry serviceRegistry = registryScope.getRegistry();
|
final InFlightMetadataCollector metadataCollector = buildMetadataCollector( managedResources, registryScope );
|
||||||
final BootstrapContextImpl bootstrapContext = new BootstrapContextImpl(
|
final List<JpaEventListener> registrations = metadataCollector.getGlobalRegistrations().getEntityListenerRegistrations();
|
||||||
serviceRegistry,
|
|
||||||
new MetadataBuilderImpl.MetadataBuildingOptionsImpl( serviceRegistry )
|
|
||||||
);
|
|
||||||
final CategorizedDomainModel categorizedDomainModel = processManagedResources(
|
|
||||||
managedResources,
|
|
||||||
bootstrapContext
|
|
||||||
);
|
|
||||||
final List<JpaEventListener> registrations = categorizedDomainModel
|
|
||||||
.getGlobalRegistrations()
|
|
||||||
.getEntityListenerRegistrations();
|
|
||||||
assertThat( registrations ).hasSize( 1 );
|
assertThat( registrations ).hasSize( 1 );
|
||||||
final JpaEventListener registration = registrations.get( 0 );
|
final JpaEventListener registration = registrations.get( 0 );
|
||||||
final MethodDetails postPersistMethod = registration.getPostPersistMethod();
|
final MethodDetails postPersistMethod = registration.getPostPersistMethod();
|
||||||
assertThat( postPersistMethod ).isNotNull();
|
assertThat( postPersistMethod ).isNotNull();
|
||||||
assertThat( postPersistMethod.getReturnType() ).isEqualTo( VOID_CLASS_DETAILS );
|
assertThat( postPersistMethod.getReturnType() ).isEqualTo( VOID_CLASS_DETAILS );
|
||||||
assertThat( postPersistMethod.getArgumentTypes() ).hasSize( 1 );
|
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