HHH-17504 - Ongoing JPA 32 work

HHH-17350 - Work on hibernate-models, XSD and JAXB
HHH-16114 - Improve boot metamodel binding
HHH-15996 - Develop an abstraction for Annotation in annotation processing
HHH-16012 - Develop an abstraction for domain model Class refs
HHH-15997 - Support for dynamic models in orm.xml
HHH-15698 - Support for entity-name in mapping.xsd
This commit is contained in:
Steve Ebersole 2023-12-04 11:04:25 -06:00
parent bcd927b21b
commit b3241be1f0
227 changed files with 21445 additions and 3 deletions

View File

@ -27,10 +27,11 @@ dependencies {
api jakartaLibs.jpa
api jakartaLibs.jta
implementation libs.hcann
implementation libs.hibernateModels
implementation libs.jandex
implementation libs.classmate
implementation libs.byteBuddy
implementation libs.hcann
implementation jakartaLibs.jaxbApi
implementation jakartaLibs.jaxb

View File

@ -0,0 +1,30 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
import java.util.Locale;
import org.hibernate.MappingException;
import org.hibernate.models.spi.ClassDetails;
/**
* Indicates that {@link jakarta.persistence.AccessType} could not be
* determined
*
* @author Steve Ebersole
*/
public class AccessTypeDeterminationException extends MappingException {
public AccessTypeDeterminationException(ClassDetails managedClass) {
super(
String.format(
Locale.ROOT,
"Unable to determine default `AccessType` for class `%s`",
managedClass.getName()
)
);
}
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
import java.util.Locale;
import org.hibernate.MappingException;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Access;
/**
* Indicates a problem with the placement of the {@link Access} annotation; either<ul>
* <li>{@linkplain jakarta.persistence.AccessType#FIELD FIELD} on a getter</li>
* <li>{@linkplain jakarta.persistence.AccessType#PROPERTY PROPERTY} on a field
* <li>{@linkplain jakarta.persistence.AccessType#PROPERTY PROPERTY} on a setter</li></li>
* </ul>
*
* @author Steve Ebersole
*/
public class AccessTypePlacementException extends MappingException {
public AccessTypePlacementException(ClassDetails classDetails, MemberDetails memberDetails) {
super( craftMessage( classDetails, memberDetails ) );
}
private static String craftMessage(ClassDetails classDetails, MemberDetails memberDetails) {
if ( memberDetails.isField() ) {
return String.format(
Locale.ROOT,
"Field `%s.%s` defined `@Access(PROPERTY) - see section 2.3.2 of the specification",
classDetails.getName(),
memberDetails.getName()
);
}
else {
return String.format(
Locale.ROOT,
"Method `%s.%s` defined `@Access(FIELD) - see section 2.3.2 of the specification",
classDetails.getName(),
memberDetails.getName()
);
}
}
}

View File

@ -0,0 +1,20 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
/**
* @author Steve Ebersole
*/
public class AnnotationPlacementException extends RuntimeException {
public AnnotationPlacementException(String message) {
super( message );
}
public AnnotationPlacementException(String message, Throwable cause) {
super( message, cause );
}
}

View File

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

View File

@ -0,0 +1,205 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
import java.lang.annotation.Annotation;
import java.util.function.Consumer;
import org.hibernate.annotations.*;
import org.hibernate.boot.internal.Abstract;
import org.hibernate.boot.internal.CollectionClassification;
import org.hibernate.boot.internal.Extends;
import org.hibernate.boot.internal.Target;
import org.hibernate.boot.models.categorize.internal.OrmAnnotationHelper;
import org.hibernate.models.spi.AnnotationDescriptor;
import static org.hibernate.models.internal.AnnotationHelper.createOrmDescriptor;
/**
* Details about Hibernate annotations
*
* @implNote Suppressed for deprecation because we refer to many deprecated annotations
*
* @author Steve Ebersole
*/
@SuppressWarnings("deprecation")
public interface HibernateAnnotations {
AnnotationDescriptor<Any> ANY = createOrmDescriptor( Any.class );
AnnotationDescriptor<AnyDiscriminator> ANY_DISCRIMINATOR = createOrmDescriptor( AnyDiscriminator.class );
AnnotationDescriptor<AnyDiscriminatorValues> ANY_DISCRIMINATOR_VALUES = createOrmDescriptor( AnyDiscriminatorValues.class );
AnnotationDescriptor<AnyDiscriminatorValue> ANY_DISCRIMINATOR_VALUE = createOrmDescriptor( AnyDiscriminatorValue.class, ANY_DISCRIMINATOR_VALUES );
AnnotationDescriptor<AnyKeyJavaClass> ANY_KEY_JAVA_CLASS = createOrmDescriptor( AnyKeyJavaClass.class );
AnnotationDescriptor<AnyKeyJavaType> ANY_KEY_JAVA_TYPE = createOrmDescriptor( AnyKeyJavaType.class );
AnnotationDescriptor<AnyKeyJdbcType> ANY_KEY_JDBC_TYPE = createOrmDescriptor( AnyKeyJdbcType.class );
AnnotationDescriptor<AnyKeyJdbcTypeCode> ANY_KEY_JDBC_TYPE_CODE = createOrmDescriptor( AnyKeyJdbcTypeCode.class );
AnnotationDescriptor<AttributeAccessor> ATTRIBUTE_ACCESSOR = createOrmDescriptor( AttributeAccessor.class );
AnnotationDescriptor<AttributeBinderType> ATTRIBUTE_BINDER_TYPE = createOrmDescriptor( AttributeBinderType.class );
AnnotationDescriptor<Bag> BAG = createOrmDescriptor( Bag.class );
AnnotationDescriptor<BatchSize> BATCH_SIZE = createOrmDescriptor( BatchSize.class );
AnnotationDescriptor<Cache> CACHE = createOrmDescriptor( Cache.class );
AnnotationDescriptor<Cascade> CASCADE = createOrmDescriptor( Cascade.class );
AnnotationDescriptor<Checks> CHECKS = createOrmDescriptor( Checks.class );
AnnotationDescriptor<Check> CHECK = createOrmDescriptor( Check.class, CHECKS );
AnnotationDescriptor<CollectionId> COLLECTION_ID = createOrmDescriptor( CollectionId.class );
AnnotationDescriptor<CollectionIdJavaType> COLLECTION_ID_JAVA_TYPE = createOrmDescriptor( CollectionIdJavaType.class );
AnnotationDescriptor<CollectionIdJdbcType> COLLECTION_ID_JDBC_TYPE = createOrmDescriptor( CollectionIdJdbcType.class );
AnnotationDescriptor<CollectionIdJdbcTypeCode> COLLECTION_ID_JDBC_TYPE_CODE = createOrmDescriptor( CollectionIdJdbcTypeCode.class );
AnnotationDescriptor<CollectionIdMutability> COLLECTION_ID_MUTABILITY = createOrmDescriptor( CollectionIdMutability.class );
AnnotationDescriptor<CollectionIdType> COLLECTION_ID_TYPE = createOrmDescriptor( CollectionIdType.class );
AnnotationDescriptor<CollectionType> COLLECTION_TYPE = createOrmDescriptor( CollectionType.class );
AnnotationDescriptor<CollectionTypeRegistrations> COLLECTION_TYPE_REGS = createOrmDescriptor( CollectionTypeRegistrations.class );
AnnotationDescriptor<CollectionTypeRegistration> COLLECTION_TYPE_REG = createOrmDescriptor( CollectionTypeRegistration.class, COLLECTION_TYPE_REGS );
AnnotationDescriptor<ColumnDefault> COLUMN_DEFAULT = createOrmDescriptor( ColumnDefault.class );
AnnotationDescriptor<ColumnTransformers> COLUMN_TRANSFORMERS = createOrmDescriptor( ColumnTransformers.class );
AnnotationDescriptor<ColumnTransformer> COLUMN_TRANSFORMER = createOrmDescriptor( ColumnTransformer.class, COLUMN_TRANSFORMERS );
AnnotationDescriptor<Comment> COMMENT = createOrmDescriptor( Comment.class );
AnnotationDescriptor<CompositeType> COMPOSITE_TYPE = createOrmDescriptor( CompositeType.class );
AnnotationDescriptor<CompositeTypeRegistrations> COMPOSITE_TYPE_REGS = createOrmDescriptor( CompositeTypeRegistrations.class );
AnnotationDescriptor<CompositeTypeRegistration> COMPOSITE_TYPE_REG = createOrmDescriptor( CompositeTypeRegistration.class, COMPOSITE_TYPE_REGS );
AnnotationDescriptor<ConverterRegistrations> CONVERTER_REGS = createOrmDescriptor( ConverterRegistrations.class );
AnnotationDescriptor<ConverterRegistration> CONVERTER_REG = createOrmDescriptor( ConverterRegistration.class, CONVERTER_REGS );
AnnotationDescriptor<CreationTimestamp> CREATION_TIMESTAMP = createOrmDescriptor( CreationTimestamp.class );
AnnotationDescriptor<CurrentTimestamp> CURRENT_TIMESTAMP = createOrmDescriptor( CurrentTimestamp.class );
AnnotationDescriptor<DiscriminatorFormula> DISCRIMINATOR_FORMULA = createOrmDescriptor( DiscriminatorFormula.class );
AnnotationDescriptor<DiscriminatorOptions> DISCRIMINATOR_OPTIONS = createOrmDescriptor( DiscriminatorOptions.class );
AnnotationDescriptor<DynamicInsert> DYNAMIC_INSERT = createOrmDescriptor( DynamicInsert.class );
AnnotationDescriptor<DynamicUpdate> DYNAMIC_UPDATE = createOrmDescriptor( DynamicUpdate.class );
AnnotationDescriptor<EmbeddableInstantiator> EMBEDDABLE_INSTANTIATOR = createOrmDescriptor( EmbeddableInstantiator.class );
AnnotationDescriptor<EmbeddableInstantiatorRegistrations> EMBEDDABLE_INSTANTIATOR_REGS = createOrmDescriptor( EmbeddableInstantiatorRegistrations.class );
AnnotationDescriptor<EmbeddableInstantiatorRegistration> EMBEDDABLE_INSTANTIATOR_REG = createOrmDescriptor( EmbeddableInstantiatorRegistration.class, EMBEDDABLE_INSTANTIATOR_REGS );
AnnotationDescriptor<Fetch> FETCH = createOrmDescriptor( Fetch.class );
AnnotationDescriptor<FetchProfiles> FETCH_PROFILES = createOrmDescriptor( FetchProfiles.class );
AnnotationDescriptor<FetchProfile> FETCH_PROFILE = createOrmDescriptor( FetchProfile.class, FETCH_PROFILES );
AnnotationDescriptor<Filters> FILTERS = createOrmDescriptor( Filters.class );
AnnotationDescriptor<Filter> FILTER = createOrmDescriptor( Filter.class, FILTERS );
AnnotationDescriptor<FilterDefs> FILTER_DEFS = createOrmDescriptor( FilterDefs.class );
AnnotationDescriptor<FilterDef> FILTER_DEF = createOrmDescriptor( FilterDef.class, FILTER_DEFS );
AnnotationDescriptor<FilterJoinTables> FILTER_JOIN_TABLES = createOrmDescriptor( FilterJoinTables.class );
AnnotationDescriptor<FilterJoinTable> FILTER_JOIN_TABLE = createOrmDescriptor( FilterJoinTable.class, FILTER_JOIN_TABLES );
AnnotationDescriptor<ForeignKey> FOREIGN_KEY = createOrmDescriptor( ForeignKey.class );
AnnotationDescriptor<Formula> FORMULA = createOrmDescriptor( Formula.class );
AnnotationDescriptor<Generated> GENERATED = createOrmDescriptor( Generated.class );
AnnotationDescriptor<GeneratedColumn> GENERATED_COLUMN = createOrmDescriptor( GeneratedColumn.class );
AnnotationDescriptor<GeneratorType> GENERATOR_TYPE = createOrmDescriptor( GeneratorType.class );
AnnotationDescriptor<GenericGenerators> GENERIC_GENERATORS = createOrmDescriptor( GenericGenerators.class );
AnnotationDescriptor<GenericGenerator> GENERIC_GENERATOR = createOrmDescriptor( GenericGenerator.class, GENERIC_GENERATORS );
AnnotationDescriptor<IdGeneratorType> ID_GENERATOR_TYPE = createOrmDescriptor( IdGeneratorType.class );
AnnotationDescriptor<Immutable> IMMUTABLE = createOrmDescriptor( Immutable.class );
AnnotationDescriptor<Imported> IMPORTED = createOrmDescriptor( Imported.class );
AnnotationDescriptor<Index> INDEX = createOrmDescriptor( Index.class );
AnnotationDescriptor<IndexColumn> INDEX_COLUMN = createOrmDescriptor( IndexColumn.class );
AnnotationDescriptor<Instantiator> INSTANTIATOR = createOrmDescriptor( Instantiator.class );
AnnotationDescriptor<JavaType> JAVA_TYPE = createOrmDescriptor( JavaType.class );
AnnotationDescriptor<JavaTypeRegistrations> JAVA_TYPE_REGS = createOrmDescriptor( JavaTypeRegistrations.class );
AnnotationDescriptor<JavaTypeRegistration> JAVA_TYPE_REG = createOrmDescriptor( JavaTypeRegistration.class, JAVA_TYPE_REGS );
AnnotationDescriptor<JdbcType> JDBC_TYPE = createOrmDescriptor( JdbcType.class );
AnnotationDescriptor<JdbcTypeCode> JDBC_TYPE_CODE = createOrmDescriptor( JdbcTypeCode.class );
AnnotationDescriptor<JdbcTypeRegistrations> JDBC_TYPE_REGS = createOrmDescriptor( JdbcTypeRegistrations.class );
AnnotationDescriptor<JdbcTypeRegistration> JDBC_TYPE_REG = createOrmDescriptor( JdbcTypeRegistration.class, JDBC_TYPE_REGS );
AnnotationDescriptor<JoinColumnsOrFormulas> JOIN_COLUMNS_OR_FORMULAS = createOrmDescriptor( JoinColumnsOrFormulas.class );
AnnotationDescriptor<JoinColumnOrFormula> JOIN_COLUMN_OR_FORMULA = createOrmDescriptor( JoinColumnOrFormula.class, JOIN_COLUMNS_OR_FORMULAS );
AnnotationDescriptor<JoinFormula> JOIN_FORMULA = createOrmDescriptor( JoinFormula.class );
AnnotationDescriptor<LazyCollection> LAZY_COLLECTION = createOrmDescriptor( LazyCollection.class );
AnnotationDescriptor<LazyGroup> LAZY_GROUP = createOrmDescriptor( LazyGroup.class );
AnnotationDescriptor<LazyToOne> LAZY_TO_ONE = createOrmDescriptor( LazyToOne.class );
AnnotationDescriptor<ListIndexBase> LIST_INDEX_BASE = createOrmDescriptor( ListIndexBase.class );
AnnotationDescriptor<ListIndexJavaType> LIST_INDEX_JAVA_TYPE = createOrmDescriptor( ListIndexJavaType.class );
AnnotationDescriptor<ListIndexJdbcType> LIST_INDEX_JDBC_TYPE = createOrmDescriptor( ListIndexJdbcType.class );
AnnotationDescriptor<ListIndexJdbcTypeCode> LIST_INDEX_JDBC_TYPE_CODE = createOrmDescriptor( ListIndexJdbcTypeCode.class );
AnnotationDescriptor<Loader> LOADER = createOrmDescriptor( Loader.class );
AnnotationDescriptor<ManyToAny> MANY_TO_ANY = createOrmDescriptor( ManyToAny.class );
AnnotationDescriptor<MapKeyCompositeType> MAP_KEY_COMPOSITE_TYPE = createOrmDescriptor( MapKeyCompositeType.class );
AnnotationDescriptor<MapKeyJavaType> MAP_KEY_JAVA_TYPE = createOrmDescriptor( MapKeyJavaType.class );
AnnotationDescriptor<MapKeyJdbcType> MAP_KEY_JDBC_TYPE = createOrmDescriptor( MapKeyJdbcType.class );
AnnotationDescriptor<MapKeyJdbcTypeCode> MAP_KEY_JDBC_TYPE_CODE = createOrmDescriptor( MapKeyJdbcTypeCode.class );
AnnotationDescriptor<MapKeyMutability> MAP_KEY_MUTABILITY = createOrmDescriptor( MapKeyMutability.class );
AnnotationDescriptor<MapKeyType> MAP_KEY_TYPE = createOrmDescriptor( MapKeyType.class );
AnnotationDescriptor<Mutability> MUTABILITY = createOrmDescriptor( Mutability.class );
AnnotationDescriptor<NamedNativeQueries> NAMED_NATIVE_QUERIES = createOrmDescriptor( NamedNativeQueries.class );
AnnotationDescriptor<NamedNativeQuery> NAMED_NATIVE_QUERY = createOrmDescriptor( NamedNativeQuery.class, NAMED_NATIVE_QUERIES );
AnnotationDescriptor<NamedQueries> NAMED_QUERIES = createOrmDescriptor( NamedQueries.class );
AnnotationDescriptor<NamedQuery> NAMED_QUERY = createOrmDescriptor( NamedQuery.class, NAMED_QUERIES );
AnnotationDescriptor<Nationalized> NATIONALIZED = createOrmDescriptor( Nationalized.class );
AnnotationDescriptor<NaturalId> NATURAL_ID = createOrmDescriptor( NaturalId.class );
AnnotationDescriptor<NaturalIdCache> NATURAL_ID_CACHE = createOrmDescriptor( NaturalIdCache.class );
AnnotationDescriptor<NotFound> NOT_FOUND = createOrmDescriptor( NotFound.class );
AnnotationDescriptor<OnDelete> ON_DELETE = createOrmDescriptor( OnDelete.class );
AnnotationDescriptor<OptimisticLock> OPTIMISTIC_LOCK = createOrmDescriptor( OptimisticLock.class );
AnnotationDescriptor<OptimisticLocking> OPTIMISTIC_LOCKING = createOrmDescriptor( OptimisticLocking.class );
AnnotationDescriptor<OrderBy> ORDER_BY = createOrmDescriptor( OrderBy.class );
AnnotationDescriptor<ParamDef> PARAM_DEF = createOrmDescriptor( ParamDef.class );
AnnotationDescriptor<Parameter> PARAMETER = createOrmDescriptor( Parameter.class );
AnnotationDescriptor<Parent> PARENT = createOrmDescriptor( Parent.class );
AnnotationDescriptor<PartitionKey> PARTITION_KEY = createOrmDescriptor( PartitionKey.class );
AnnotationDescriptor<Persister> PERSISTER = createOrmDescriptor( Persister.class );
AnnotationDescriptor<Polymorphism> POLYMORPHISM = createOrmDescriptor( Polymorphism.class );
AnnotationDescriptor<Proxy> PROXY = createOrmDescriptor( Proxy.class );
AnnotationDescriptor<RowId> ROW_ID = createOrmDescriptor( RowId.class );
AnnotationDescriptor<SecondaryRows> SECONDARY_ROWS = createOrmDescriptor( SecondaryRows.class );
AnnotationDescriptor<SecondaryRow> SECONDARY_ROW = createOrmDescriptor( SecondaryRow.class, SECONDARY_ROWS );
AnnotationDescriptor<SelectBeforeUpdate> SELECT_BEFORE_UPDATE = createOrmDescriptor( SelectBeforeUpdate.class );
AnnotationDescriptor<SortComparator> SORT_COMPARATOR = createOrmDescriptor( SortComparator.class );
AnnotationDescriptor<SortNatural> SORT_NATURAL = createOrmDescriptor( SortNatural.class );
AnnotationDescriptor<Source> SOURCE = createOrmDescriptor( Source.class );
AnnotationDescriptor<SQLDeletes> SQL_DELETES = createOrmDescriptor( SQLDeletes.class );
AnnotationDescriptor<SQLDelete> SQL_DELETE = createOrmDescriptor( SQLDelete.class, SQL_DELETES );
AnnotationDescriptor<SQLDeleteAll> SQL_DELETE_ALL = createOrmDescriptor( SQLDeleteAll.class );
AnnotationDescriptor<SqlFragmentAlias> SQL_FRAGMENT_ALIAS = createOrmDescriptor( SqlFragmentAlias.class );
AnnotationDescriptor<SQLInserts> SQL_INSERTS = createOrmDescriptor( SQLInserts.class );
AnnotationDescriptor<SQLInsert> SQL_INSERT = createOrmDescriptor( SQLInsert.class, SQL_INSERTS );
AnnotationDescriptor<SQLUpdates> SQL_UPDATES = createOrmDescriptor( SQLUpdates.class );
AnnotationDescriptor<SQLUpdate> SQL_UPDATE = createOrmDescriptor( SQLUpdate.class, SQL_UPDATES );
AnnotationDescriptor<Struct> STRUCT = createOrmDescriptor( Struct.class );
AnnotationDescriptor<Subselect> SUBSELECT = createOrmDescriptor( Subselect.class );
AnnotationDescriptor<Synchronize> SYNCHRONIZE = createOrmDescriptor( Synchronize.class );
AnnotationDescriptor<Tables> TABLES = createOrmDescriptor( Tables.class );
AnnotationDescriptor<Table> TABLE = createOrmDescriptor( Table.class, TABLES );
AnnotationDescriptor<TenantId> TENANT_ID = createOrmDescriptor( TenantId.class );
AnnotationDescriptor<TimeZoneColumn> TZ_COLUMN = createOrmDescriptor( TimeZoneColumn.class );
AnnotationDescriptor<TimeZoneStorage> TZ_STORAGE = createOrmDescriptor( TimeZoneStorage.class );
AnnotationDescriptor<Type> TYPE = createOrmDescriptor( Type.class );
AnnotationDescriptor<TypeBinderType> TYPE_BINDER_TYPE = createOrmDescriptor( TypeBinderType.class );
AnnotationDescriptor<TypeRegistrations> TYPE_REGS = createOrmDescriptor( TypeRegistrations.class );
AnnotationDescriptor<TypeRegistration> TYPE_REG = createOrmDescriptor( TypeRegistration.class, TYPE_REGS );
AnnotationDescriptor<UpdateTimestamp> UPDATE_TIMESTAMP = createOrmDescriptor( UpdateTimestamp.class );
AnnotationDescriptor<UuidGenerator> UUID_GENERATOR = createOrmDescriptor( UuidGenerator.class );
AnnotationDescriptor<ValueGenerationType> VALUE_GENERATION_TYPE = createOrmDescriptor( ValueGenerationType.class );
AnnotationDescriptor<Where> WHERE = createOrmDescriptor( Where.class );
AnnotationDescriptor<WhereJoinTable> WHERE_JOIN_TABLE = createOrmDescriptor( WhereJoinTable.class );
AnnotationDescriptor<Abstract> ABSTRACT = createOrmDescriptor( Abstract.class );
AnnotationDescriptor<CollectionClassification> COLLECTION_CLASSIFICATION = createOrmDescriptor( CollectionClassification.class );
AnnotationDescriptor<Extends> EXTENDS = createOrmDescriptor( Extends.class );
AnnotationDescriptor<Target> TARGET = createOrmDescriptor( Target.class );
AnnotationDescriptor<DialectOverride.Checks> DIALECT_OVERRIDE_CHECKS = createOrmDescriptor( DialectOverride.Checks.class );
AnnotationDescriptor<DialectOverride.Check> DIALECT_OVERRIDE_CHECK = createOrmDescriptor( DialectOverride.Check.class, DIALECT_OVERRIDE_CHECKS );
AnnotationDescriptor<DialectOverride.OrderBys> DIALECT_OVERRIDE_ORDER_BYS = createOrmDescriptor( DialectOverride.OrderBys.class );
AnnotationDescriptor<DialectOverride.OrderBy> DIALECT_OVERRIDE_ORDER_BY = createOrmDescriptor( DialectOverride.OrderBy.class, DIALECT_OVERRIDE_ORDER_BYS );
AnnotationDescriptor<DialectOverride.ColumnDefaults> DIALECT_OVERRIDE_COLUMN_DEFAULTS = createOrmDescriptor( DialectOverride.ColumnDefaults.class );
AnnotationDescriptor<DialectOverride.ColumnDefault> DIALECT_OVERRIDE_COLUMN_DEFAULT = createOrmDescriptor( DialectOverride.ColumnDefault.class, DIALECT_OVERRIDE_COLUMN_DEFAULTS );
AnnotationDescriptor<DialectOverride.GeneratedColumns> DIALECT_OVERRIDE_GENERATED_COLUMNS = createOrmDescriptor( DialectOverride.GeneratedColumns.class );
AnnotationDescriptor<DialectOverride.GeneratedColumn> DIALECT_OVERRIDE_GENERATED_COLUMN = createOrmDescriptor( DialectOverride.GeneratedColumn.class, DIALECT_OVERRIDE_GENERATED_COLUMNS );
AnnotationDescriptor<DialectOverride.DiscriminatorFormulas> DIALECT_OVERRIDE_DISCRIMINATOR_FORMULAS = createOrmDescriptor( DialectOverride.DiscriminatorFormulas.class );
AnnotationDescriptor<DialectOverride.DiscriminatorFormula> DIALECT_OVERRIDE_DISCRIMINATOR_FORMULA = createOrmDescriptor( DialectOverride.DiscriminatorFormula.class, DIALECT_OVERRIDE_DISCRIMINATOR_FORMULAS );
AnnotationDescriptor<DialectOverride.Formulas> DIALECT_OVERRIDE_FORMULAS = createOrmDescriptor( DialectOverride.Formulas.class );
AnnotationDescriptor<DialectOverride.Formula> DIALECT_OVERRIDE_FORMULA = createOrmDescriptor( DialectOverride.Formula.class, DIALECT_OVERRIDE_FORMULAS );
AnnotationDescriptor<DialectOverride.JoinFormulas> DIALECT_OVERRIDE_JOIN_FORMULAS = createOrmDescriptor( DialectOverride.JoinFormulas.class );
AnnotationDescriptor<DialectOverride.JoinFormula> DIALECT_OVERRIDE_JOIN_FORMULA = createOrmDescriptor( DialectOverride.JoinFormula.class, DIALECT_OVERRIDE_JOIN_FORMULAS );
AnnotationDescriptor<DialectOverride.Wheres> DIALECT_OVERRIDE_WHERES = createOrmDescriptor( DialectOverride.Wheres.class );
AnnotationDescriptor<DialectOverride.Where> DIALECT_OVERRIDE_WHERE = createOrmDescriptor( DialectOverride.Where.class, DIALECT_OVERRIDE_WHERES );
AnnotationDescriptor<DialectOverride.FilterOverrides> DIALECT_OVERRIDE_FILTER_OVERRIDES = createOrmDescriptor( DialectOverride.FilterOverrides.class );
AnnotationDescriptor<DialectOverride.Filters> DIALECT_OVERRIDE_FILTERS = createOrmDescriptor( DialectOverride.Filters.class, DIALECT_OVERRIDE_FILTER_OVERRIDES );
AnnotationDescriptor<DialectOverride.FilterDefOverrides> DIALECT_OVERRIDE_FILTER_DEF_OVERRIDES = createOrmDescriptor( DialectOverride.FilterDefOverrides.class );
AnnotationDescriptor<DialectOverride.FilterDefs> DIALECT_OVERRIDE_FILTER_DEFS = createOrmDescriptor( DialectOverride.FilterDefs.class, DIALECT_OVERRIDE_FILTER_DEF_OVERRIDES );
AnnotationDescriptor<DialectOverride.Version> DIALECT_OVERRIDE_VERSION = createOrmDescriptor( DialectOverride.Version.class );
static void forEachAnnotation(Consumer<AnnotationDescriptor<? extends Annotation>> consumer) {
OrmAnnotationHelper.forEachOrmAnnotation( HibernateAnnotations.class, consumer );
}
}

View File

@ -0,0 +1,198 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
import java.lang.annotation.Annotation;
import java.util.function.Consumer;
import org.hibernate.boot.models.categorize.internal.OrmAnnotationHelper;
import org.hibernate.models.spi.AnnotationDescriptor;
import jakarta.persistence.Access;
import jakarta.persistence.AssociationOverride;
import jakarta.persistence.AssociationOverrides;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.Basic;
import jakarta.persistence.Cacheable;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ColumnResult;
import jakarta.persistence.Convert;
import jakarta.persistence.Converter;
import jakarta.persistence.Converts;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Embedded;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.EntityResult;
import jakarta.persistence.Enumerated;
import jakarta.persistence.ExcludeDefaultListeners;
import jakarta.persistence.ExcludeSuperclassListeners;
import jakarta.persistence.FieldResult;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.IdClass;
import jakarta.persistence.Index;
import jakarta.persistence.Inheritance;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
import jakarta.persistence.JoinTable;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapKey;
import jakarta.persistence.MapKeyClass;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.MapKeyEnumerated;
import jakarta.persistence.MapKeyJoinColumn;
import jakarta.persistence.MapKeyJoinColumns;
import jakarta.persistence.MapKeyTemporal;
import jakarta.persistence.MappedSuperclass;
import jakarta.persistence.MapsId;
import jakarta.persistence.NamedAttributeNode;
import jakarta.persistence.NamedEntityGraph;
import jakarta.persistence.NamedEntityGraphs;
import jakarta.persistence.NamedNativeQueries;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.NamedStoredProcedureQueries;
import jakarta.persistence.NamedStoredProcedureQuery;
import jakarta.persistence.NamedSubgraph;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.OrderBy;
import jakarta.persistence.OrderColumn;
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 jakarta.persistence.PrimaryKeyJoinColumn;
import jakarta.persistence.PrimaryKeyJoinColumns;
import jakarta.persistence.QueryHint;
import jakarta.persistence.SecondaryTable;
import jakarta.persistence.SecondaryTables;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.SequenceGenerators;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.SqlResultSetMappings;
import jakarta.persistence.StoredProcedureParameter;
import jakarta.persistence.Table;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.TableGenerators;
import jakarta.persistence.Temporal;
import jakarta.persistence.Transient;
import jakarta.persistence.UniqueConstraint;
import jakarta.persistence.Version;
import static org.hibernate.models.internal.AnnotationHelper.createOrmDescriptor;
/**
* Descriptors for JPA annotations
*
* @author Steve Ebersole
*/
public interface JpaAnnotations {
AnnotationDescriptor<Access> ACCESS = createOrmDescriptor( Access.class );
AnnotationDescriptor<AssociationOverrides> ASSOCIATION_OVERRIDES = createOrmDescriptor( AssociationOverrides.class );
AnnotationDescriptor<AssociationOverride> ASSOCIATION_OVERRIDE = createOrmDescriptor( AssociationOverride.class, ASSOCIATION_OVERRIDES );
AnnotationDescriptor<AttributeOverrides> ATTRIBUTE_OVERRIDES = createOrmDescriptor( AttributeOverrides.class );
AnnotationDescriptor<AttributeOverride> ATTRIBUTE_OVERRIDE = createOrmDescriptor( AttributeOverride.class, ATTRIBUTE_OVERRIDES );
AnnotationDescriptor<Basic> BASIC = createOrmDescriptor( Basic.class );
AnnotationDescriptor<Cacheable> CACHEABLE = createOrmDescriptor( Cacheable.class );
AnnotationDescriptor<CollectionTable> COLLECTION_TABLE = createOrmDescriptor( CollectionTable.class );
AnnotationDescriptor<Column> COLUMN = createOrmDescriptor( Column.class );
AnnotationDescriptor<ColumnResult> COLUMN_RESULT = createOrmDescriptor( ColumnResult.class );
AnnotationDescriptor<Converts> CONVERTS = createOrmDescriptor( Converts.class );
AnnotationDescriptor<Convert> CONVERT = createOrmDescriptor( Convert.class, CONVERTS );
AnnotationDescriptor<Converter> CONVERTER = createOrmDescriptor( Converter.class );
AnnotationDescriptor<DiscriminatorColumn> DISCRIMINATOR_COLUMN = createOrmDescriptor( DiscriminatorColumn.class );
AnnotationDescriptor<DiscriminatorValue> DISCRIMINATOR_VALUE = createOrmDescriptor( DiscriminatorValue.class );
AnnotationDescriptor<ElementCollection> ELEMENT_COLLECTION = createOrmDescriptor( ElementCollection.class );
AnnotationDescriptor<Embeddable> EMBEDDABLE = createOrmDescriptor( Embeddable.class );
AnnotationDescriptor<Embedded> EMBEDDED = createOrmDescriptor( Embedded.class );
AnnotationDescriptor<EmbeddedId> EMBEDDED_ID = createOrmDescriptor( EmbeddedId.class );
AnnotationDescriptor<Entity> ENTITY = createOrmDescriptor( Entity.class );
AnnotationDescriptor<EntityListeners> ENTITY_LISTENERS = createOrmDescriptor( EntityListeners.class );
AnnotationDescriptor<EntityResult> ENTITY_RESULT = createOrmDescriptor( EntityResult.class );
AnnotationDescriptor<Enumerated> ENUMERATED = createOrmDescriptor( Enumerated.class );
AnnotationDescriptor<ExcludeDefaultListeners> EXCLUDE_DEFAULT_LISTENERS = createOrmDescriptor( ExcludeDefaultListeners.class );
AnnotationDescriptor<ExcludeSuperclassListeners> EXCLUDE_SUPERCLASS_LISTENERS = createOrmDescriptor( ExcludeSuperclassListeners.class );
AnnotationDescriptor<FieldResult> FIELD_RESULT = createOrmDescriptor( FieldResult.class );
AnnotationDescriptor<ForeignKey> FOREIGN_KEY = createOrmDescriptor( ForeignKey.class );
AnnotationDescriptor<GeneratedValue> GENERATED_VALUE = createOrmDescriptor( GeneratedValue.class );
AnnotationDescriptor<Id> ID = createOrmDescriptor( Id.class );
AnnotationDescriptor<IdClass> ID_CLASS = createOrmDescriptor( IdClass.class );
AnnotationDescriptor<Index> INDEX = createOrmDescriptor( Index.class );
AnnotationDescriptor<Inheritance> INHERITANCE = createOrmDescriptor( Inheritance.class );
AnnotationDescriptor<JoinColumns> JOIN_COLUMNS = createOrmDescriptor( JoinColumns.class );
AnnotationDescriptor<JoinColumn> JOIN_COLUMN = createOrmDescriptor( JoinColumn.class, JOIN_COLUMNS );
AnnotationDescriptor<JoinTable> JOIN_TABLE = createOrmDescriptor( JoinTable.class );
AnnotationDescriptor<Lob> LOB = createOrmDescriptor( Lob.class );
AnnotationDescriptor<ManyToMany> MANY_TO_MANY = createOrmDescriptor( ManyToMany.class );
AnnotationDescriptor<ManyToOne> MANY_TO_ONE = createOrmDescriptor( ManyToOne.class );
AnnotationDescriptor<MapKey> MAP_KEY = createOrmDescriptor( MapKey.class );
AnnotationDescriptor<MapKeyClass> MAP_KEY_CLASS = createOrmDescriptor( MapKeyClass.class );
AnnotationDescriptor<MapKeyColumn> MAP_KEY_COLUMN = createOrmDescriptor( MapKeyColumn.class );
AnnotationDescriptor<MapKeyEnumerated> MAP_KEY_ENUMERATED = createOrmDescriptor( MapKeyEnumerated.class );
AnnotationDescriptor<MapKeyJoinColumns> MAP_KEY_JOIN_COLUMNS = createOrmDescriptor( MapKeyJoinColumns.class );
AnnotationDescriptor<MapKeyJoinColumn> MAP_KEY_JOIN_COLUMN = createOrmDescriptor( MapKeyJoinColumn.class, MAP_KEY_JOIN_COLUMNS );
AnnotationDescriptor<MapKeyTemporal> MAP_KEY_TEMPORAL = createOrmDescriptor( MapKeyTemporal.class );
AnnotationDescriptor<MappedSuperclass> MAPPED_SUPERCLASS = createOrmDescriptor( MappedSuperclass.class );
AnnotationDescriptor<MapsId> MAPS_ID = createOrmDescriptor( MapsId.class );
AnnotationDescriptor<NamedAttributeNode> NAMED_ATTRIBUTE_NODE = createOrmDescriptor( NamedAttributeNode.class );
AnnotationDescriptor<NamedEntityGraphs> NAMED_ENTITY_GRAPHS = createOrmDescriptor( NamedEntityGraphs.class );
AnnotationDescriptor<NamedEntityGraph> NAMED_ENTITY_GRAPH = createOrmDescriptor( NamedEntityGraph.class, NAMED_ENTITY_GRAPHS );
AnnotationDescriptor<NamedNativeQueries> NAMED_NATIVE_QUERIES = createOrmDescriptor( NamedNativeQueries.class );
AnnotationDescriptor<NamedNativeQuery> NAMED_NATIVE_QUERY = createOrmDescriptor( NamedNativeQuery.class, NAMED_NATIVE_QUERIES );
AnnotationDescriptor<NamedQueries> NAMED_QUERIES = createOrmDescriptor( NamedQueries.class );
AnnotationDescriptor<NamedQuery> NAMED_QUERY = createOrmDescriptor( NamedQuery.class, NAMED_QUERIES );
AnnotationDescriptor<NamedStoredProcedureQueries> NAMED_STORED_PROCEDURE_QUERIES = createOrmDescriptor( NamedStoredProcedureQueries.class );
AnnotationDescriptor<NamedStoredProcedureQuery> NAMED_STORED_PROCEDURE_QUERY = createOrmDescriptor( NamedStoredProcedureQuery.class, NAMED_STORED_PROCEDURE_QUERIES );
AnnotationDescriptor<NamedSubgraph> NAMED_SUB_GRAPH = createOrmDescriptor( NamedSubgraph.class );
AnnotationDescriptor<OneToMany> ONE_TO_MANY = createOrmDescriptor( OneToMany.class );
AnnotationDescriptor<OneToOne> ONE_TO_ONE = createOrmDescriptor( OneToOne.class );
AnnotationDescriptor<OrderBy> ORDER_BY = createOrmDescriptor( OrderBy.class );
AnnotationDescriptor<OrderColumn> ORDER_COLUMN = createOrmDescriptor( OrderColumn.class );
AnnotationDescriptor<PostLoad> POST_LOAD = createOrmDescriptor( PostLoad.class );
AnnotationDescriptor<PostPersist> POST_PERSIST = createOrmDescriptor( PostPersist.class );
AnnotationDescriptor<PostRemove> POST_REMOVE = createOrmDescriptor( PostRemove.class );
AnnotationDescriptor<PostUpdate> POST_UPDATE = createOrmDescriptor( PostUpdate.class );
AnnotationDescriptor<PrePersist> PRE_PERSIST = createOrmDescriptor( PrePersist.class );
AnnotationDescriptor<PreRemove> PRE_REMOVE = createOrmDescriptor( PreRemove.class );
AnnotationDescriptor<PreUpdate> PRE_UPDATE = createOrmDescriptor( PreUpdate.class );
AnnotationDescriptor<PrimaryKeyJoinColumns> PRIMARY_KEY_JOIN_COLUMNS = createOrmDescriptor( PrimaryKeyJoinColumns.class );
AnnotationDescriptor<PrimaryKeyJoinColumn> PRIMARY_KEY_JOIN_COLUMN = createOrmDescriptor( PrimaryKeyJoinColumn.class, PRIMARY_KEY_JOIN_COLUMNS );
AnnotationDescriptor<QueryHint> QUERY_HINT = createOrmDescriptor( QueryHint.class );
AnnotationDescriptor<SecondaryTables> SECONDARY_TABLES = createOrmDescriptor( SecondaryTables.class );
AnnotationDescriptor<SecondaryTable> SECONDARY_TABLE = createOrmDescriptor( SecondaryTable.class, SECONDARY_TABLES );
AnnotationDescriptor<SequenceGenerators> SEQUENCE_GENERATORS = createOrmDescriptor( SequenceGenerators.class );
AnnotationDescriptor<SequenceGenerator> SEQUENCE_GENERATOR = createOrmDescriptor( SequenceGenerator.class, SEQUENCE_GENERATORS );
AnnotationDescriptor<SqlResultSetMappings> SQL_RESULT_SET_MAPPINGS = createOrmDescriptor( SqlResultSetMappings.class );
AnnotationDescriptor<SqlResultSetMapping> SQL_RESULT_SET_MAPPING = createOrmDescriptor( SqlResultSetMapping.class, SQL_RESULT_SET_MAPPINGS );
AnnotationDescriptor<StoredProcedureParameter> STORED_PROCEDURE_PARAMETER = createOrmDescriptor( StoredProcedureParameter.class );
AnnotationDescriptor<Table> TABLE = createOrmDescriptor( Table.class );
AnnotationDescriptor<TableGenerators> TABLE_GENERATORS = createOrmDescriptor( TableGenerators.class );
AnnotationDescriptor<TableGenerator> TABLE_GENERATOR = createOrmDescriptor( TableGenerator.class, TABLE_GENERATORS );
AnnotationDescriptor<Temporal> TEMPORAL = createOrmDescriptor( Temporal.class );
AnnotationDescriptor<Transient> TRANSIENT = createOrmDescriptor( Transient.class );
AnnotationDescriptor<UniqueConstraint> UNIQUE_CONSTRAINT = createOrmDescriptor( UniqueConstraint.class );
AnnotationDescriptor<Version> VERSION = createOrmDescriptor( Version.class );
static void forEachAnnotation(Consumer<AnnotationDescriptor<? extends Annotation>> consumer) {
OrmAnnotationHelper.forEachOrmAnnotation( JpaAnnotations.class, consumer );
}
}

View File

@ -0,0 +1,22 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
/**
* Indicates a problem resolving a member from {@linkplain org.hibernate.models.spi.ClassDetails}
*
* @author Steve Ebersole
*/
public class MemberResolutionException extends RuntimeException {
public MemberResolutionException(String message) {
super( message );
}
public MemberResolutionException(String message, Throwable cause) {
super( message, cause );
}
}

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models;
import java.util.EnumSet;
import org.hibernate.MappingException;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
/**
* Condition where an attribute indicates multiple {@linkplain AttributeMetadata.AttributeNature natures}
*
* @author Steve Ebersole
*/
public class MultipleAttributeNaturesException extends MappingException {
private final String attributeName;
public MultipleAttributeNaturesException(
String attributeName,
EnumSet<AttributeMetadata.AttributeNature> natures) {
super( craftMessage( attributeName, natures ) );
this.attributeName = attributeName;
}
public String getAttributeName() {
return attributeName;
}
private static String craftMessage(String attributeName, EnumSet<AttributeMetadata.AttributeNature> natures) {
final StringBuilder buffer = new StringBuilder( "Attribute `" )
.append( attributeName )
.append( "` expressed multiple natures [" );
natures.forEach( buffer::append );
return buffer.append( "]" ).toString();
}
}

View File

@ -0,0 +1,24 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind;
import org.hibernate.Internal;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
/**
* todo : find the proper min/max id range
*
* @author Steve Ebersole
*/
@Internal
public interface ModelBindingLogging extends BasicLogger {
String NAME = "org.hibernate.models.orm";
Logger MODEL_BINDING_LOGGER = Logger.getLogger( NAME );
}

View File

@ -0,0 +1,23 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* A means to pass along the String type name from the mapping xml.
*
* @author Steve Ebersole
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnyKeyType {
String value();
}

View File

@ -0,0 +1,264 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Mutability;
import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.OptimisticLock;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.AnnotationPlacementException;
import org.hibernate.boot.models.bind.internal.binders.BasicValueBinder;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.type.descriptor.java.MutabilityPlan;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.BASIC;
/**
* Binding for an attribute
*
* @author Steve Ebersole
*/
public class AttributeBinding extends Binding {
private final AttributeMetadata attributeMetadata;
private final Property property;
private final Table attributeTable;
private final Value mappingValue;
public AttributeBinding(
AttributeMetadata attributeMetadata,
PersistentClass owner,
Table primaryTable,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
super( bindingOptions, bindingState, bindingContext );
this.attributeMetadata = attributeMetadata;
this.property = new Property();
this.property.setName( attributeMetadata.getName() );
if ( attributeMetadata.getNature() == BASIC ) {
final var basicValue = createBasicValue( primaryTable );
property.setValue( basicValue );
attributeTable = basicValue.getTable();
mappingValue = basicValue;
}
else {
throw new UnsupportedOperationException( "Not yet implemented" );
}
applyNaturalId( attributeMetadata, property );
}
public AttributeBinding(
AttributeMetadata attributeMetadata,
Component owner,
Table primaryTable,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
super( bindingOptions, bindingState, bindingContext );
throw new UnsupportedOperationException( "Not yet implemented" );
}
private BasicValue createBasicValue(Table primaryTable) {
final BasicValue basicValue = new BasicValue( bindingState.getMetadataBuildingContext() );
final MemberDetails member = attributeMetadata.getMember();
bindImplicitJavaType( member, property, basicValue );
bindMutability( member, property, basicValue );
bindOptimisticLocking( member, property, basicValue );
bindConversion( member, property, basicValue );
processColumn( member, property, basicValue, primaryTable, Column.class );
BasicValueBinder.bindJavaType( member, property, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueBinder.bindJdbcType( member, property, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueBinder.bindLob( member, property, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueBinder.bindNationalized(
member,
property,
basicValue,
bindingOptions,
bindingState,
bindingContext
);
BasicValueBinder.bindEnumerated(
member,
property,
basicValue,
bindingOptions,
bindingState,
bindingContext
);
BasicValueBinder.bindTemporalPrecision(
member,
property,
basicValue,
bindingOptions,
bindingState,
bindingContext
);
BasicValueBinder.bindTimeZoneStorage(
member,
property,
basicValue,
bindingOptions,
bindingState,
bindingContext
);
return basicValue;
}
public Property getProperty() {
return property;
}
public Table getAttributeTable() {
return attributeTable;
}
public Value getMappingValue() {
return mappingValue;
}
@Override
public Property getBinding() {
return getProperty();
}
public static void bindImplicitJavaType(
MemberDetails member,
@SuppressWarnings("unused") Property property,
BasicValue basicValue) {
basicValue.setImplicitJavaTypeAccess( (typeConfiguration) -> member.getType().toJavaClass() );
}
private void bindMutability(MemberDetails member, Property property, BasicValue basicValue) {
final var mutabilityAnn = member.getAnnotationUsage( Mutability.class );
final var immutableAnn = member.getAnnotationUsage( Immutable.class );
if ( immutableAnn != null ) {
if ( mutabilityAnn != null ) {
throw new AnnotationPlacementException(
"Illegal combination of @Mutability and @Immutable - " + member.getName()
);
}
property.setUpdateable( false );
}
else if ( mutabilityAnn != null ) {
basicValue.setExplicitMutabilityPlanAccess( (typeConfiguration) -> {
final ClassDetails classDetails = mutabilityAnn.getClassDetails( "value" );
final Class<MutabilityPlan<?>> javaClass = classDetails.toJavaClass();
try {
return javaClass.getConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
final ModelsException modelsException = new ModelsException( "Error instantiating local @MutabilityPlan - " + member.getName() );
modelsException.addSuppressed( e );
throw modelsException;
}
} );
}
}
private void bindOptimisticLocking(
MemberDetails member,
Property property,
@SuppressWarnings("unused") BasicValue basicValue) {
final var annotationUsage = member.getAnnotationUsage( OptimisticLock.class );
if ( annotationUsage != null ) {
if ( annotationUsage.getBoolean( "excluded" ) ) {
property.setOptimisticLocked( false );
return;
}
}
property.setOptimisticLocked( true );
}
private void bindConversion(MemberDetails member, @SuppressWarnings("unused") Property property, BasicValue basicValue) {
// todo : do we need to account for auto-applied converters here?
final var convertAnn = member.getAnnotationUsage( Convert.class );
if ( convertAnn == null ) {
return;
}
if ( convertAnn.getBoolean( "disableConversion" ) ) {
return;
}
if ( convertAnn.getString( "attributeName" ) != null ) {
throw new ModelsException( "@Convert#attributeName should not be specified on basic mappings - " + member.getName() );
}
final ClassDetails converterClassDetails = convertAnn.getClassDetails( "converter" );
final Class<AttributeConverter<?, ?>> javaClass = converterClassDetails.toJavaClass();
basicValue.setJpaAttributeConverterDescriptor( new ClassBasedConverterDescriptor(
javaClass,
bindingContext.getClassmateContext()
) );
}
public <A extends Annotation> void processColumn(
MemberDetails member,
Property property,
BasicValue basicValue,
Table primaryTable,
Class<A> annotation) {
// todo : implicit column
final var columnAnn = member.getAnnotationUsage( annotation );
final var column = ColumnBinder.bindColumn( columnAnn, property::getName );
var tableName = BindingHelper.getValue( columnAnn, "table", "" );
if ( "".equals( tableName ) || tableName == null ) {
basicValue.setTable( primaryTable );
}
else {
final Identifier identifier = Identifier.toIdentifier( tableName );
final TableReference tableByName = bindingState.getTableByName( identifier.getCanonicalName() );
basicValue.setTable( tableByName.table() );
}
basicValue.addColumn( column );
}
private void applyNaturalId(AttributeMetadata attributeMetadata, Property property) {
final var naturalIdAnn = attributeMetadata.getMember().getAnnotationUsage( NaturalId.class );
if ( naturalIdAnn == null ) {
return;
}
property.setNaturalIdentifier( true );
property.setUpdateable( naturalIdAnn.getBoolean( "mutable" ) );
}
}

View File

@ -0,0 +1,28 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
/**
* @author Steve Ebersole
*/
public abstract class Binding {
protected final BindingState bindingState;
protected final BindingOptions bindingOptions;
protected final BindingContext bindingContext;
public Binding(BindingOptions bindingOptions, BindingState bindingState, BindingContext bindingContext) {
this.bindingOptions = bindingOptions;
this.bindingState = bindingState;
this.bindingContext = bindingContext;
}
public abstract Object getBinding();
}

View File

@ -0,0 +1,106 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.categorize.spi.CategorizedDomainModel;
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetailsRegistry;
import jakarta.persistence.SharedCacheMode;
/**
* @author Steve Ebersole
*/
public class BindingContextImpl implements BindingContext {
private final ClassDetailsRegistry classDetailsRegistry;
private final AnnotationDescriptorRegistry annotationDescriptorRegistry;
private final GlobalRegistrations globalRegistrations;
private final ImplicitNamingStrategy implicitNamingStrategy;
private final PhysicalNamingStrategy physicalNamingStrategy;
private final SharedCacheMode sharedCacheMode;
private final ClassmateContext classmateContext;
private final BootstrapContext bootstrapContext;
public BindingContextImpl(CategorizedDomainModel categorizedDomainModel, BootstrapContext bootstrapContext) {
this(
categorizedDomainModel.getClassDetailsRegistry(),
categorizedDomainModel.getAnnotationDescriptorRegistry(),
categorizedDomainModel.getGlobalRegistrations(),
bootstrapContext.getMetadataBuildingOptions().getImplicitNamingStrategy(),
bootstrapContext.getMetadataBuildingOptions().getPhysicalNamingStrategy(),
bootstrapContext.getMetadataBuildingOptions().getSharedCacheMode(),
bootstrapContext.getClassmateContext(),
bootstrapContext
);
}
public BindingContextImpl(
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry annotationDescriptorRegistry,
GlobalRegistrations globalRegistrations,
ImplicitNamingStrategy implicitNamingStrategy,
PhysicalNamingStrategy physicalNamingStrategy,
SharedCacheMode sharedCacheMode,
ClassmateContext classmateContext,
BootstrapContext bootstrapContext) {
this.classDetailsRegistry = classDetailsRegistry;
this.annotationDescriptorRegistry = annotationDescriptorRegistry;
this.implicitNamingStrategy = implicitNamingStrategy;
this.physicalNamingStrategy = physicalNamingStrategy;
this.bootstrapContext = bootstrapContext;
this.globalRegistrations = globalRegistrations;
this.classmateContext = classmateContext;
this.sharedCacheMode = sharedCacheMode;
}
@Override
public ClassDetailsRegistry getClassDetailsRegistry() {
return classDetailsRegistry;
}
@Override
public AnnotationDescriptorRegistry getAnnotationDescriptorRegistry() {
return annotationDescriptorRegistry;
}
@Override
public BootstrapContext getBootstrapContext() {
return bootstrapContext;
}
@Override
public GlobalRegistrations getGlobalRegistrations() {
return globalRegistrations;
}
@Override
public ClassmateContext getClassmateContext() {
return classmateContext;
}
@Override
public SharedCacheMode getSharedCacheMode() {
return sharedCacheMode;
}
@Override
public ImplicitNamingStrategy getImplicitNamingStrategy() {
return implicitNamingStrategy;
}
@Override
public PhysicalNamingStrategy getPhysicalNamingStrategy() {
return physicalNamingStrategy;
}
}

View File

@ -0,0 +1,192 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.lang.annotation.Annotation;
import java.util.Iterator;
import java.util.List;
import java.util.function.Supplier;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ObjectNameNormalizer;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.QuotedIdentifierTarget;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.AttributeDescriptor;
import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER;
/**
* @author Steve Ebersole
*/
public class BindingHelper {
public static <T, A extends Annotation> T getValue(
AnnotationUsage<A> annotationUsage,
String attributeName,
Class<A> annotationType,
BindingContext context) {
final T valueOrNull = getValueOrNull( annotationUsage, attributeName );
if ( valueOrNull != null ) {
return valueOrNull;
}
// resolve its default
return getDefaultValue( attributeName, annotationType, context );
}
public static <T, A extends Annotation> T getValueOrNull(
AnnotationUsage<A> annotationUsage,
String attributeName) {
if ( annotationUsage != null ) {
// allow to return null if missing
return annotationUsage.getAttributeValue( attributeName );
}
// there was no annotation...
return null;
}
public static <T,A extends Annotation> T getDefaultValue(
String attributeName,
Class<A> annotationType,
BindingContext context) {
final AnnotationDescriptor<A> annotationDescriptor = context.getAnnotationDescriptorRegistry().getDescriptor( annotationType );
final AttributeDescriptor<Object> attributeDescriptor = annotationDescriptor.getAttribute( attributeName );
//noinspection unchecked
return (T) attributeDescriptor.getAttributeMethod().getDefaultValue();
}
public static <A extends Annotation> String getString(
AnnotationUsage<A> annotationUsage,
String attributeName,
Class<A> annotationType,
BindingContext context) {
return getValue( annotationUsage, attributeName, annotationType, context );
}
public static <A extends Annotation> String getStringOrNull(
AnnotationUsage<A> annotationUsage,
String attributeName) {
return getValueOrNull( annotationUsage, attributeName );
}
public static <A extends Annotation> Identifier getIdentifier(
AnnotationUsage<A> annotationUsage,
String attributeName,
Class<A> annotationType,
QuotedIdentifierTarget target,
BindingOptions options,
JdbcEnvironment jdbcEnvironment,
BindingContext context) {
final String name = getString( annotationUsage, attributeName, annotationType, context );
final boolean globallyQuoted = options.getGloballyQuotedIdentifierTargets().contains( target );
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name, globallyQuoted );
}
public static <A extends Annotation> Identifier toIdentifier(
String name,
QuotedIdentifierTarget target,
BindingOptions options,
JdbcEnvironment jdbcEnvironment) {
final boolean globallyQuoted = options.getGloballyQuotedIdentifierTargets().contains( target );
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name, globallyQuoted );
}
public static <T,A extends Annotation> T getValue(AnnotationUsage<A> ann, String attributeName, T defaultValue) {
if ( ann == null ) {
return defaultValue;
}
return ann.getAttributeValue( attributeName, defaultValue );
}
public static <T,A extends Annotation> T getValue(AnnotationUsage<A> ann, String attributeName, AnnotationDescriptor<A> descriptor) {
if ( ann == null ) {
//noinspection unchecked
return (T) descriptor.getAttribute( attributeName ).getAttributeMethod().getDefaultValue();
}
//noinspection unchecked
return getValue(
ann,
attributeName,
() -> (T) descriptor.getAttribute( attributeName ).getAttributeMethod().getDefaultValue()
);
}
public static <T,A extends Annotation> T getValue(AnnotationUsage<A> ann, String attributeName, Supplier<T> defaultValueSupplier) {
if ( ann == null ) {
return (T) defaultValueSupplier.get();
}
return ann.getAttributeValue( attributeName, defaultValueSupplier );
}
public static <A extends Annotation> String getGloballyQuotedValue(
AnnotationUsage<A> ann,
String attributeName,
Supplier<String> defaultValueSupplier,
BindingOptions bindingOptions,
BindingState bindingState) {
final String value = getValue( ann, attributeName, defaultValueSupplier );
return applyGlobalQuoting( value, QuotedIdentifierTarget.COLUMN_DEFINITION, bindingOptions, bindingState );
}
public static String applyGlobalQuoting(
String text,
QuotedIdentifierTarget target,
BindingOptions options,
BindingState bindingState) {
final boolean globallyQuoted = options.getGloballyQuotedIdentifierTargets().contains( target );
if ( !globallyQuoted ) {
return text;
}
final ObjectNameNormalizer objectNameNormalizer = bindingState
.getMetadataBuildingContext()
.getObjectNameNormalizer();
return objectNameNormalizer.applyGlobalQuoting( text );
}
public static void processSecondPassQueue(List<? extends SecondPass> secondPasses) {
if ( secondPasses == null ) {
return;
}
int processedCount = 0;
final Iterator<? extends SecondPass> secondPassItr = secondPasses.iterator();
while ( secondPassItr.hasNext() ) {
final SecondPass secondPass = secondPassItr.next();
try {
final boolean success = secondPass.process();
if ( success ) {
processedCount++;
secondPassItr.remove();
}
}
catch (Exception e) {
MODEL_BINDING_LOGGER.debug( "Error processing second pass", e );
}
}
if ( !secondPasses.isEmpty() ) {
if ( processedCount == 0 ) {
// there are second-passes in the queue, but we were not able to
// successfully process any of them. this is a non-changing
// error condition - just throw an exception
throw new ModelsException( "Unable to process second-pass list" );
}
processSecondPassQueue( secondPasses );
}
}
}

View File

@ -0,0 +1,105 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.lang.annotation.Annotation;
import java.util.EnumSet;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.QuotedIdentifierTarget;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
/**
* @author Steve Ebersole
*/
public class BindingOptionsImpl implements BindingOptions {
private final Identifier defaultCatalogName;
private final Identifier defaultSchemaName;
private final EnumSet<QuotedIdentifierTarget> globallyQuotedIdentifierTargets;
public BindingOptionsImpl(MetadataBuildingContext metadataBuildingContext) {
final boolean globallyQuote = metadataBuildingContext.getMappingDefaults().shouldImplicitlyQuoteIdentifiers();
final boolean skipColumnDefinitions = metadataBuildingContext
.getBootstrapContext()
.getServiceRegistry()
.getService( ConfigurationService.class )
.getSetting(
AvailableSettings.GLOBALLY_QUOTED_IDENTIFIERS_SKIP_COLUMN_DEFINITIONS,
StandardConverters.BOOLEAN,
false
);
if ( !globallyQuote ) {
globallyQuotedIdentifierTargets = EnumSet.noneOf( QuotedIdentifierTarget.class );
}
else {
globallyQuotedIdentifierTargets = EnumSet.allOf( QuotedIdentifierTarget.class );
if ( skipColumnDefinitions ) {
globallyQuotedIdentifierTargets.remove( QuotedIdentifierTarget.COLUMN_DEFINITION );
}
}
final JdbcEnvironment jdbcEnvironment = metadataBuildingContext.getMetadataCollector()
.getDatabase()
.getJdbcEnvironment();
defaultCatalogName = toIdentifier(
metadataBuildingContext.getMappingDefaults().getImplicitCatalogName(),
QuotedIdentifierTarget.CATALOG_NAME,
globallyQuotedIdentifierTargets,
jdbcEnvironment
);
defaultSchemaName = toIdentifier(
metadataBuildingContext.getMappingDefaults().getImplicitSchemaName(),
QuotedIdentifierTarget.SCHEMA_NAME,
globallyQuotedIdentifierTargets,
jdbcEnvironment
);
}
public static <A extends Annotation> Identifier toIdentifier(
String name,
QuotedIdentifierTarget target,
EnumSet<QuotedIdentifierTarget> globallyQuotedIdentifierTargets,
JdbcEnvironment jdbcEnvironment) {
final boolean globallyQuoted = globallyQuotedIdentifierTargets.contains( target );
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name, globallyQuoted );
}
public BindingOptionsImpl(Identifier defaultCatalogName, Identifier defaultSchemaName) {
this( defaultCatalogName, defaultSchemaName, EnumSet.noneOf( QuotedIdentifierTarget.class ) );
}
public BindingOptionsImpl(
Identifier defaultCatalogName,
Identifier defaultSchemaName,
EnumSet<QuotedIdentifierTarget> globallyQuotedIdentifierTargets) {
this.defaultCatalogName = defaultCatalogName;
this.defaultSchemaName = defaultSchemaName;
this.globallyQuotedIdentifierTargets = globallyQuotedIdentifierTargets;
}
@Override
public Identifier getDefaultCatalogName() {
return defaultCatalogName;
}
@Override
public Identifier getDefaultSchemaName() {
return defaultSchemaName;
}
@Override
public EnumSet<QuotedIdentifierTarget> getGloballyQuotedIdentifierTargets() {
return globallyQuotedIdentifierTargets;
}
}

View File

@ -0,0 +1,195 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.boot.models.categorize.spi.FilterDefRegistration;
import org.hibernate.boot.models.categorize.spi.IdentifiableTypeMetadata;
import org.hibernate.boot.models.categorize.spi.ManagedTypeMetadata;
import org.hibernate.boot.models.categorize.spi.TableOwner;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.util.KeyedConsumer;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.type.spi.TypeConfiguration;
/**
* @author Steve Ebersole
*/
public class BindingStateImpl implements BindingState {
private final MetadataBuildingContext metadataBuildingContext;
private final Database database;
private final JdbcServices jdbcServices;
private final Map<String, TableReference> tableMap = new HashMap<>();
private final Map<TableOwner, TableReference> tableByOwnerMap = new HashMap<>();
private final Map<ClassDetails, ManagedTypeBinding> typeBindings = new HashMap<>();
private final Map<ClassDetails, IdentifiableTypeBinding> typeBindersBySuper = new HashMap<>();
public BindingStateImpl(MetadataBuildingContext metadataBuildingContext) {
this.metadataBuildingContext = metadataBuildingContext;
this.database = metadataBuildingContext.getMetadataCollector().getDatabase();
this.jdbcServices = metadataBuildingContext.getBootstrapContext().getServiceRegistry().getService( JdbcServices.class );
}
@Override
public MetadataBuildingContext getMetadataBuildingContext() {
return metadataBuildingContext;
}
@Override
public Database getDatabase() {
return database;
}
@Override
public JdbcServices getJdbcServices() {
return jdbcServices;
}
@Override
public int getTableCount() {
return tableMap.size();
}
@Override
public void forEachTable(KeyedConsumer<String,TableReference> consumer) {
//noinspection unchecked
tableMap.forEach( (BiConsumer<? super String, ? super TableReference>) consumer );
}
@Override
public <T extends TableReference> T getTableByName(String name) {
//noinspection unchecked
return (T) tableMap.get( name );
}
@Override
public <T extends TableReference> T getTableByOwner(TableOwner owner) {
//noinspection unchecked
return (T) tableByOwnerMap.get( owner );
}
@Override
public void addTable(TableOwner owner, TableReference table) {
tableMap.put( table.logicalName().getCanonicalName(), table );
tableByOwnerMap.put( owner, table );
}
@Override
public void addSecondaryTable(SecondaryTable table) {
tableMap.put( table.logicalName().getCanonicalName(), table );
}
private String resolveSchemaName(Identifier explicit) {
if ( explicit != null ) {
return explicit.getCanonicalName();
}
var defaultNamespace = metadataBuildingContext.getMetadataCollector()
.getDatabase()
.getDefaultNamespace();
if ( defaultNamespace != null ) {
final Identifier defaultSchemaName = defaultNamespace.getName().getSchema();
if ( defaultSchemaName != null ) {
return defaultSchemaName.getCanonicalName();
}
}
return null;
}
private String resolveCatalogName(Identifier explicit) {
if ( explicit != null ) {
return explicit.getCanonicalName();
}
var defaultNamespace = metadataBuildingContext.getMetadataCollector()
.getDatabase()
.getDefaultNamespace();
if ( defaultNamespace != null ) {
final Identifier defaultCatalogName = defaultNamespace.getName().getCatalog();
if ( defaultCatalogName != null ) {
return defaultCatalogName.getCanonicalName();
}
}
return null;
}
@Override
public void registerTypeBinding(ManagedTypeMetadata type, ManagedTypeBinding binding) {
typeBindings.put( type.getClassDetails(), binding );
if ( type instanceof IdentifiableTypeMetadata identifiableType ) {
if ( identifiableType.getSuperType() != null ) {
typeBindersBySuper.put(
identifiableType.getSuperType().getClassDetails(),
(IdentifiableTypeBinding) binding
);
}
}
if ( binding instanceof EntityBinding entityTypeBinding ) {
metadataBuildingContext.getMetadataCollector().addEntityBinding( entityTypeBinding.getPersistentClass() );
}
else if ( binding instanceof MappedSuperclassBinding mappedSuperBinding ) {
metadataBuildingContext.getMetadataCollector().addMappedSuperclass(
mappedSuperBinding.typeMetadata.getClassDetails().toJavaClass(),
mappedSuperBinding.getMappedSuperclass()
);
}
}
@Override
public ManagedTypeBinding getTypeBinding(ClassDetails type) {
return typeBindings.get( type );
}
@Override
public IdentifiableTypeBinding getSuperTypeBinding(ClassDetails type) {
return typeBindersBySuper.get( type );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Filter def
@Override
public void apply(FilterDefRegistration registration) {
metadataBuildingContext.getMetadataCollector().addFilterDefinition( new FilterDefinition(
registration.getName(),
registration.getDefaultCondition(),
extractParameterMap( registration )
) );
}
private Map<String, JdbcMapping> extractParameterMap(FilterDefRegistration registration) {
final Map<String, ClassDetails> parameters = registration.getParameters();
if ( CollectionHelper.isEmpty( parameters ) ) {
return Collections.emptyMap();
}
final TypeConfiguration typeConfiguration = metadataBuildingContext.getBootstrapContext().getTypeConfiguration();
final Map<String, JdbcMapping> result = new HashMap<>();
parameters.forEach( (name, typeDetails) -> {
result.put( name, typeConfiguration.getBasicTypeForJavaType( typeDetails.toJavaClass() ) );
} );
return result;
}
}

View File

@ -0,0 +1,418 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.hibernate.annotations.Filter;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
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.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.categorize.spi.MappedSuperclassTypeMetadata;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.event.internal.EntityCallback;
import org.hibernate.jpa.event.internal.ListenerCallback;
import org.hibernate.jpa.event.spi.CallbackDefinition;
import org.hibernate.jpa.event.spi.CallbackType;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MethodDetails;
import jakarta.persistence.Cacheable;
import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity;
import jakarta.persistence.SharedCacheMode;
/**
* @author Steve Ebersole
*/
public abstract class EntityBinding extends IdentifiableTypeBinding {
public EntityBinding(
EntityTypeMetadata entityTypeMetadata,
IdentifiableTypeBinding superTypeBinding,
EntityHierarchy.HierarchyRelation hierarchyRelation,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
super( entityTypeMetadata, superTypeBinding, hierarchyRelation, bindingOptions, bindingState, bindingContext );
}
public abstract PersistentClass getPersistentClass();
@Override
public EntityTypeMetadata getTypeMetadata() {
return (EntityTypeMetadata) super.getTypeMetadata();
}
protected static void applyNaming(EntityTypeMetadata source, PersistentClass persistentClass, BindingState bindingState) {
final ClassDetails classDetails = source.getClassDetails();
final AnnotationUsage<Entity> entityAnn = classDetails.getAnnotationUsage( Entity.class );
final String jpaEntityName = BindingHelper.getValue( entityAnn, "name", (String) null );
final String entityName;
final String importName;
if ( classDetails.getName() != null
&& !classDetails.getName().equals( classDetails.getClassName() ) ) {
// should indicate a dynamic model
entityName = classDetails.getName();
}
else {
entityName = classDetails.getClassName();
}
if ( StringHelper.isNotEmpty( jpaEntityName ) ) {
importName = jpaEntityName;
}
else {
importName = StringHelper.unqualifyEntityName( entityName );
}
persistentClass.setClassName( classDetails.getClassName() );
persistentClass.setEntityName( entityName );
persistentClass.setJpaEntityName( importName );
bindingState.getMetadataBuildingContext().getMetadataCollector().addImport( importName, entityName );
}
protected static void applyDiscriminatorValue(
EntityTypeMetadata typeMetadata,
PersistentClass persistentClass) {
final BasicValue discriminatorMapping = (BasicValue) persistentClass.getRootClass().getDiscriminator();
if ( discriminatorMapping == null ) {
return;
}
final AnnotationUsage<DiscriminatorValue> ann = typeMetadata.getClassDetails().getAnnotationUsage( DiscriminatorValue.class );
if ( ann == null ) {
final Type resolvedJavaType = discriminatorMapping.resolve().getRelationalJavaType().getJavaType();
if ( resolvedJavaType == String.class ) {
persistentClass.setDiscriminatorValue( persistentClass.getEntityName() );
}
else {
persistentClass.setDiscriminatorValue( Integer.toString( persistentClass.getSubclassId() ) );
}
}
else {
persistentClass.setDiscriminatorValue( ann.getString( "value" ) );
}
}
protected static void applyCaching(EntityTypeMetadata source, PersistentClass persistentClass, BindingState bindingState) {
final ClassDetails classDetails = source.getClassDetails();
final var cacheableAnn = classDetails.getAnnotationUsage( Cacheable.class );
if ( cacheableAnn == null ) {
return;
}
final SharedCacheMode sharedCacheMode = bindingState.getMetadataBuildingContext()
.getBuildingOptions()
.getSharedCacheMode();
persistentClass.setCached( isCacheable( sharedCacheMode, cacheableAnn ) );
}
private static boolean isCacheable(SharedCacheMode sharedCacheMode, AnnotationUsage<Cacheable> explicitCacheableAnn) {
return switch ( sharedCacheMode ) {
// all entities should be cached
case ALL -> true;
// Hibernate defaults to ENABLE_SELECTIVE, the only sensible setting
// only entities with @Cacheable(true) should be cached
case ENABLE_SELECTIVE, UNSPECIFIED -> explicitCacheableAnn != null && explicitCacheableAnn.getBoolean( "value" );
// only entities with @Cacheable(false) should not be cached
case DISABLE_SELECTIVE -> explicitCacheableAnn == null || explicitCacheableAnn.getBoolean( "value" );
// treat both NONE and UNSPECIFIED the same
default -> false;
};
}
protected static void applyFilters(EntityTypeMetadata source, PersistentClass persistentClass) {
final ClassDetails classDetails = source.getClassDetails();
final List<AnnotationUsage<Filter>> filters = classDetails.getRepeatedAnnotationUsages( Filter.class );
if ( CollectionHelper.isEmpty( filters ) ) {
return;
}
filters.forEach( (filter) -> {
persistentClass.addFilter(
filter.getString( "name" ),
filter.getString( "condition", (String) null ),
filter.getAttributeValue( "deduceAliasInjectionPoints", true ),
extractFilterAliasTableMap( filter ),
extractFilterAliasEntityMap( filter )
);
} );
}
private static Map<String, String> extractFilterAliasTableMap(AnnotationUsage<Filter> filter) {
// todo : implement
return null;
}
private static Map<String, String> extractFilterAliasEntityMap(AnnotationUsage<Filter> filter) {
// todo : implement
return null;
}
protected static void applyJpaEventListeners(EntityTypeMetadata typeMetadata, PersistentClass persistentClass) {
final List<JpaEventListener> listeners = typeMetadata.getCompleteJpaEventListeners();
if ( CollectionHelper.isEmpty( listeners ) ) {
return;
}
listeners.forEach( (listener) -> {
if ( listener.getStyle() == JpaEventListenerStyle.CALLBACK ) {
processEntityCallbacks( listener, typeMetadata, persistentClass );
}
else {
assert listener.getStyle() == JpaEventListenerStyle.LISTENER;
processListenerCallbacks( listener, typeMetadata, persistentClass );
}
} );
}
private static void processEntityCallbacks(
JpaEventListener listener,
EntityTypeMetadata typeMetadata,
PersistentClass persistentClass) {
final Class<?> entityClass = listener.getCallbackClass().toJavaClass();
processJpaEventCallbacks(
entityClass,
listener,
JpaEventListenerStyle.CALLBACK,
null,
typeMetadata,
persistentClass
);
}
private static void processJpaEventCallbacks(
Class<?> listenerClass,
JpaEventListener listener,
JpaEventListenerStyle style,
Class<?> methodArgumentType,
EntityTypeMetadata typeMetadata,
PersistentClass persistentClass) {
assert style == JpaEventListenerStyle.CALLBACK || methodArgumentType != null;
// todo : would be nicer to allow injecting them one at a time.
// upstream is defined currently to accept a List
final List<CallbackDefinition> callbackDefinitions = new ArrayList<>();
final MethodDetails prePersistMethod = listener.getPrePersistMethod();
if ( prePersistMethod != null ) {
final Method callbackMethod = findCallbackMethod( listenerClass, prePersistMethod );
callbackDefinitions.add( createCallbackDefinition(
listenerClass,
callbackMethod,
style,
CallbackType.PRE_PERSIST
) );
}
final MethodDetails postPersistMethod = listener.getPostPersistMethod();
if ( postPersistMethod != null ) {
final Method callbackMethod = findCallbackMethod( listenerClass, postPersistMethod );
callbackDefinitions.add( createCallbackDefinition(
listenerClass,
callbackMethod,
style,
CallbackType.POST_PERSIST
) );
}
final MethodDetails preUpdateMethod = listener.getPreUpdateMethod();
if ( preUpdateMethod != null ) {
final Method callbackMethod = findCallbackMethod( listenerClass, preUpdateMethod );
callbackDefinitions.add( createCallbackDefinition(
listenerClass,
callbackMethod,
style,
CallbackType.PRE_UPDATE
) );
}
final MethodDetails postUpdateMethod = listener.getPostUpdateMethod();
if ( postUpdateMethod != null ) {
final Method callbackMethod = findCallbackMethod( listenerClass, postUpdateMethod );
callbackDefinitions.add( createCallbackDefinition(
listenerClass,
callbackMethod,
style,
CallbackType.POST_UPDATE
) );
}
final MethodDetails preRemoveMethod = listener.getPreRemoveMethod();
if ( preRemoveMethod != null ) {
final Method callbackMethod = findCallbackMethod( listenerClass, preRemoveMethod );
callbackDefinitions.add( createCallbackDefinition(
listenerClass,
callbackMethod,
style,
CallbackType.PRE_REMOVE
) );
}
final MethodDetails postRemoveMethod = listener.getPostRemoveMethod();
if ( postRemoveMethod != null ) {
final Method callbackMethod = findCallbackMethod( listenerClass, postRemoveMethod );
callbackDefinitions.add( createCallbackDefinition(
listenerClass,
callbackMethod,
style,
CallbackType.POST_REMOVE
) );
}
final MethodDetails postLoadMethod = listener.getPostLoadMethod();
if ( postLoadMethod != null ) {
final Method callbackMethod = findCallbackMethod( listenerClass, postLoadMethod );
callbackDefinitions.add( createCallbackDefinition(
listenerClass,
callbackMethod,
style,
CallbackType.POST_LOAD
) );
}
persistentClass.addCallbackDefinitions( callbackDefinitions );
}
private static CallbackDefinition createCallbackDefinition(
Class<?> listenerClass,
Method callbackMethod,
JpaEventListenerStyle style,
CallbackType callbackType) {
final CallbackDefinition callback;
if ( style == JpaEventListenerStyle.CALLBACK ) {
callback = new EntityCallback.Definition( callbackMethod, callbackType );
}
else {
callback = new ListenerCallback.Definition( listenerClass, callbackMethod, callbackType );
}
return callback;
}
private static void processListenerCallbacks(
JpaEventListener listener,
EntityTypeMetadata typeMetadata,
PersistentClass persistentClass) {
final Class<?> listenerClass = listener.getCallbackClass().toJavaClass();
processJpaEventCallbacks(
listenerClass,
listener,
JpaEventListenerStyle.LISTENER,
typeMetadata.getClassDetails().toJavaClass(),
typeMetadata,
persistentClass
);
}
private static Method findCallbackMethod(
Class<?> callbackTarget,
MethodDetails callbackMethod) {
try {
if ( callbackMethod.getArgumentTypes().isEmpty() ) {
return callbackTarget.getDeclaredMethod( callbackMethod.getName() );
}
else {
final ClassDetails argClassDetails = callbackMethod.getArgumentTypes().get( 0 );
// we don't
return callbackTarget.getMethod( callbackMethod.getName(), argClassDetails.toJavaClass() );
}
}
catch (NoSuchMethodException e) {
final ModelsException modelsException = new ModelsException(
String.format(
Locale.ROOT,
"Unable to locate callback method - %s.%s",
callbackTarget.getName(),
callbackMethod.getName()
)
);
modelsException.addSuppressed( e );
throw modelsException;
}
}
protected void processSecondaryTables() {
final List<SecondaryTable> secondaryTables = TableHelper.bindSecondaryTables(
this,
bindingOptions,
bindingState,
bindingContext
);
secondaryTables.forEach( (secondaryTable) -> {
final Join join = new Join();
join.setTable( secondaryTable.table() );
join.setPersistentClass( getPersistentClass() );
join.setOptional( secondaryTable.optional() );
join.setInverse( !secondaryTable.owned() );
} );
}
protected void prepareSubclassBindings() {
getTypeMetadata().forEachSubType( (subType) -> {
if ( subType instanceof EntityTypeMetadata entityTypeMetadata ) {
new SubclassEntityBinding(
entityTypeMetadata,
this,
EntityHierarchy.HierarchyRelation.SUB,
bindingOptions,
bindingState,
bindingContext
);
}
else {
new MappedSuperclassBinding(
(MappedSuperclassTypeMetadata) subType,
this,
EntityHierarchy.HierarchyRelation.SUB,
bindingOptions,
bindingState,
bindingContext
);
}
} );
}
@Override
protected boolean excludeAttributeFromPreparation(AttributeMetadata attributeMetadata) {
// skip "special" attributes
final EntityHierarchy hierarchy = getTypeMetadata().getHierarchy();
if ( hierarchy.getIdMapping().contains( attributeMetadata )
|| hierarchy.getVersionAttribute() == attributeMetadata
|| hierarchy.getTenantIdAttribute() == attributeMetadata ) {
return true;
}
return super.excludeAttributeFromPreparation( attributeMetadata );
}
@Override
protected AttributeBinding createAttributeBinding(AttributeMetadata attributeMetadata, Table primaryTable) {
return new AttributeBinding( attributeMetadata, getPersistentClass(), primaryTable, bindingOptions, bindingState, bindingContext );
}
}

View File

@ -0,0 +1,126 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.util.Map;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
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.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.IdentifiableTypeClass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.ModelsException;
/**
* Binding for an {@linkplain jakarta.persistence.metamodel.IdentifiableType identifiable type}
*
* @author Steve Ebersole
*/
public abstract class IdentifiableTypeBinding extends ManagedTypeBinding {
protected final IdentifiableTypeBinding superTypeBinding;
protected final IdentifiableTypeMetadata superTypeMetadata;
private final Map<String, AttributeBinding> attributeBindings;
public IdentifiableTypeBinding(
IdentifiableTypeMetadata typeMetadata,
IdentifiableTypeBinding superTypeBinding,
EntityHierarchy.HierarchyRelation hierarchyRelation,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
super( typeMetadata, bindingOptions, bindingState, bindingContext );
this.superTypeBinding = superTypeBinding;
this.superTypeMetadata = superTypeBinding == null ? null : superTypeBinding.getTypeMetadata();
// NOTE: slightly over-sized (id, version, ...), but that's ok
this.attributeBindings = CollectionHelper.linkedMapOfSize( typeMetadata.getNumberOfAttributes() );
}
@Override
public IdentifiableTypeMetadata getTypeMetadata() {
return (IdentifiableTypeMetadata) super.getTypeMetadata();
}
public IdentifiableTypeMetadata getSuperTypeMetadata() {
return superTypeMetadata;
}
public IdentifiableTypeBinding getSuperTypeBinding() {
return superTypeBinding;
}
@Override
public abstract IdentifiableTypeClass getBinding();
@Override
public Map<String, AttributeBinding> getAttributeBindings() {
return attributeBindings;
}
public EntityTypeMetadata findSuperEntityMetadata() {
final EntityBinding superEntityBinder = getSuperEntityBinding();
if ( superEntityBinder == null ) {
return null;
}
return superEntityBinder.getTypeMetadata();
}
public PersistentClass resolveSuperEntityPersistentClass(IdentifiableTypeClass superTypeBinding) {
if ( superTypeBinding instanceof PersistentClass ) {
return (PersistentClass) superTypeBinding;
}
if ( superTypeBinding.getSuperType() != null ) {
return resolveSuperEntityPersistentClass( superTypeBinding.getSuperType() );
}
throw new ModelsException( "Unable to resolve super PersistentClass for " + superTypeBinding );
}
public EntityBinding getSuperEntityBinding() {
IdentifiableTypeBinding check = superTypeBinding;
if ( check == null ) {
return null;
}
do {
if ( check.getBinding() instanceof PersistentClass ) {
return (EntityBinding) check;
}
check = check.getSuperTypeBinding();
} while ( check != null );
return null;
}
protected void prepareAttributeBindings(Table primaryTable) {
typeMetadata.forEachAttribute( (index, attributeMetadata) -> {
if ( excludeAttributeFromPreparation( attributeMetadata ) ) {
return;
}
final var attributeBinding = createAttributeBinding( attributeMetadata, primaryTable );
attributeBindings.put( attributeMetadata.getName(), attributeBinding );
getBinding().applyProperty( attributeBinding.getProperty() );
} );
}
protected boolean excludeAttributeFromPreparation(AttributeMetadata attributeMetadata) {
return false;
}
protected abstract AttributeBinding createAttributeBinding(AttributeMetadata attributeMetadata, Table primaryTable);
}

View File

@ -0,0 +1,155 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.models.bind.internal.binders.BasicValueBinder;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.categorize.spi.AggregatedKeyMapping;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.BasicKeyMapping;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.models.categorize.spi.KeyMapping;
import org.hibernate.boot.models.categorize.spi.NonAggregatedKeyMapping;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Column;
/**
* Binding for an entity identifier
*
* @author Steve Ebersole
*/
public class IdentifierBinding extends Binding {
private final KeyValue keyValue;
public IdentifierBinding(
RootEntityBinding rootEntityBinding,
EntityTypeMetadata entityTypeMetadata,
BindingOptions options,
BindingState state,
BindingContext context) {
super( options, state, context );
this.keyValue = prepareMappingValue( rootEntityBinding, entityTypeMetadata, options, state, context );
}
public KeyValue getValue() {
return keyValue;
}
@Override
public KeyValue getBinding() {
return getValue();
}
private static KeyValue prepareMappingValue(
RootEntityBinding rootEntityBinding,
EntityTypeMetadata entityTypeMetadata,
BindingOptions options,
BindingState state,
BindingContext context) {
final RootClass rootClass = rootEntityBinding.getPersistentClass();
final EntityHierarchy hierarchy = entityTypeMetadata.getHierarchy();
final KeyMapping idMapping = hierarchy.getIdMapping();
final Table table = rootClass.getTable();
if ( idMapping instanceof BasicKeyMapping basicKeyMapping ) {
return prepareBasicIdentifier( basicKeyMapping, table, rootClass, options, state, context );
}
else if ( idMapping instanceof AggregatedKeyMapping aggregatedKeyMapping ) {
return bindAggregatedIdentifier( aggregatedKeyMapping, table, rootClass, options, state, context );
}
else {
return bindNonAggregatedIdentifier( (NonAggregatedKeyMapping) idMapping, table, rootClass, options, state, context );
}
}
private static KeyValue prepareBasicIdentifier(
BasicKeyMapping basicKeyMapping,
Table table,
RootClass rootClass,
BindingOptions options,
BindingState state,
BindingContext context) {
final AttributeMetadata idAttribute = basicKeyMapping.getAttribute();
final MemberDetails idAttributeMember = idAttribute.getMember();
final BasicValue idValue = new BasicValue( state.getMetadataBuildingContext(), table );
rootClass.setIdentifier( idValue );
idValue.setTable( table );
final Property idProperty = new Property();
idProperty.setName( idAttribute.getName() );
idProperty.setValue( idValue );
idProperty.setPersistentClass( rootClass );
rootClass.addProperty( idProperty );
rootClass.setIdentifierProperty( idProperty );
final PrimaryKey primaryKey = table.getPrimaryKey();
final AnnotationUsage<Column> idColumnAnn = idAttributeMember.getAnnotationUsage( Column.class );
final org.hibernate.mapping.Column column = ColumnBinder.bindColumn(
idColumnAnn,
() -> "id",
true,
false
);
idValue.addColumn( column, true, false );
primaryKey.addColumn( column );
AttributeBinding.bindImplicitJavaType( idAttributeMember, idProperty, idValue );
BasicValueBinder.bindJavaType( idAttributeMember, idProperty, idValue, options, state, context );
BasicValueBinder.bindJdbcType( idAttributeMember, idProperty, idValue, options, state, context );
BasicValueBinder.bindNationalized( idAttributeMember, idProperty, idValue, options, state, context );
return idValue;
}
private static KeyValue bindAggregatedIdentifier(
AggregatedKeyMapping aggregatedKeyMapping,
Table table,
RootClass rootClass,
BindingOptions options,
BindingState state,
BindingContext context) {
final Component idValue = new Component( state.getMetadataBuildingContext(), rootClass );
rootClass.setIdentifier( idValue );
idValue.setTable( table );
final Property idProperty = new Property();
idProperty.setValue( idValue );
rootClass.setIdentifierProperty( idProperty );
// todo : need an EmbeddableBinding
return idValue;
}
private static KeyValue bindNonAggregatedIdentifier(
NonAggregatedKeyMapping idMapping,
Table table,
RootClass rootClass,
BindingOptions options,
BindingState state,
BindingContext context) {
throw new UnsupportedOperationException( "Not yet implemented" );
}
}

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.mapping.Table;
/**
* Models a from-clause sub-query.
*
* @see org.hibernate.annotations.Subselect
*
* @author Steve Ebersole
*/
public record InLineView(Identifier logicalName, Table table) implements TableReference {
@Override
public Identifier logicalName() {
return logicalName;
}
public String getQuery() {
return table.getSubselect();
}
@Override
public boolean exportable() {
return false;
}
}

View File

@ -0,0 +1,38 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.util.Map;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.categorize.spi.ManagedTypeMetadata;
/**
* Binding for an {@linkplain jakarta.persistence.metamodel.ManagedType managed type}
*
* @author Steve Ebersole
*/
public abstract class ManagedTypeBinding extends Binding {
protected final ManagedTypeMetadata typeMetadata;
public ManagedTypeBinding(
ManagedTypeMetadata typeMetadata,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
super( bindingOptions, bindingState, bindingContext );
this.typeMetadata = typeMetadata;
}
public ManagedTypeMetadata getTypeMetadata() {
return typeMetadata;
}
public abstract Map<String, AttributeBinding> getAttributeBindings();
}

View File

@ -0,0 +1,86 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.MappedSuperclassTypeMetadata;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
/**
* @author Steve Ebersole
*/
public class MappedSuperclassBinding extends IdentifiableTypeBinding {
private final MappedSuperclass mappedSuperclass;
public MappedSuperclassBinding(
MappedSuperclassTypeMetadata type,
IdentifiableTypeBinding superTypeBinding,
EntityHierarchy.HierarchyRelation hierarchyRelation,
BindingOptions options,
BindingState state,
BindingContext bindingContext) {
super( type, superTypeBinding, hierarchyRelation, options, state, bindingContext );
final EntityBinding superEntityBinding = getSuperEntityBinding();
final MappedSuperclass superMappedSuper;
final PersistentClass superEntity;
if ( superTypeBinding == superEntityBinding && superTypeBinding != null ) {
superMappedSuper = null;
superEntity = superEntityBinding.getPersistentClass();
}
else if ( superTypeBinding != null ) {
superMappedSuper = (MappedSuperclass) superTypeBinding.getBinding();
superEntity = null;
}
else if ( superEntityBinding != null ) {
superMappedSuper = null;
superEntity = superEntityBinding.getPersistentClass();
}
else {
superMappedSuper = null;
superEntity = null;
}
this.mappedSuperclass = new MappedSuperclass( superMappedSuper, superEntity, getTable() );
state.registerTypeBinding( type, this );
state.getMetadataBuildingContext().getMetadataCollector().addImport(
StringHelper.unqualify( type.getClassDetails().getClassName() ),
type.getClassDetails().getClassName()
);
}
public MappedSuperclass getMappedSuperclass() {
return mappedSuperclass;
}
@Override
public MappedSuperclass getBinding() {
return getMappedSuperclass();
}
@Override
protected AttributeBinding createAttributeBinding(AttributeMetadata attributeMetadata, Table primaryTable) {
throw new UnsupportedOperationException( "Not yet implemented" );
}
Table getTable() {
final var superEntityBinder = getSuperEntityBinding();
if ( superEntityBinder == null ) {
return null;
}
return superEntityBinder.getPersistentClass().getTable();
}
}

View File

@ -0,0 +1,65 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.spi.PhysicalTableReference;
import org.hibernate.mapping.Table;
/**
* Models a physical table from the underlying database schema
*
* @see jakarta.persistence.Table
* @see jakarta.persistence.CollectionTable
* @see jakarta.persistence.JoinTable
*
* @author Steve Ebersole
*/
public record PhysicalTable(
Identifier logicalName,
Identifier logicalCatalogName,
Identifier logicalSchemaName,
Identifier physicalTableName,
Identifier physicalCatalogName,
Identifier physicalSchemaName,
Table table) implements PhysicalTableReference {
@Override
public Identifier logicalName() {
return logicalName;
}
@Override
public Identifier getLogicalSchemaName() {
return logicalSchemaName;
}
@Override
public Identifier getLogicalCatalogName() {
return logicalCatalogName;
}
@Override
public Identifier getPhysicalTableName() {
return physicalTableName;
}
@Override
public Identifier getPhysicalSchemaName() {
return physicalSchemaName;
}
@Override
public Identifier getPhysicalCatalogName() {
return physicalCatalogName;
}
@Override
public boolean exportable() {
return !table.isAbstract() && table.getExportIdentifier() != null;
}
}

View File

@ -0,0 +1,55 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.spi.PersistentTableReference;
import org.hibernate.mapping.Table;
/**
* @see org.hibernate.annotations.View
*
* @author Steve Ebersole
*/
public record PhysicalView(
Identifier logicalName,
Identifier logicalCatalogName,
Identifier logicalSchemaName,
Identifier physicalName,
Identifier physicalCatalogName,
Identifier physicalSchemaName,
Table table) implements PersistentTableReference {
@Override
public Identifier logicalName() {
return logicalName;
}
@Override
public Identifier getPhysicalSchemaName() {
return physicalSchemaName;
}
@Override
public Identifier getLogicalSchemaName() {
return logicalSchemaName;
}
@Override
public Identifier getPhysicalCatalogName() {
return physicalCatalogName;
}
@Override
public Identifier getLogicalCatalogName() {
return logicalCatalogName;
}
@Override
public boolean exportable() {
return true;
}
}

View File

@ -0,0 +1,326 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.MappingException;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.OptimisticLockType;
import org.hibernate.annotations.OptimisticLocking;
import org.hibernate.annotations.SoftDelete;
import org.hibernate.annotations.SoftDeleteType;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.TableReference;
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.MappedSuperclassTypeMetadata;
import org.hibernate.boot.models.categorize.spi.NaturalIdCacheRegion;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.engine.OptimisticLockStyle;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.InheritanceType;
import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER;
import static org.hibernate.boot.models.bind.internal.binders.TenantIdBinder.bindTenantId;
import static org.hibernate.boot.models.bind.internal.binders.VersionBinder.bindVersion;
import static org.hibernate.internal.util.StringHelper.coalesce;
/**
* Binding for a root {@linkplain jakarta.persistence.metamodel.EntityType entity type}
*
* @author Steve Ebersole
*/
public class RootEntityBinding extends EntityBinding {
private final RootClass rootClass;
private final TableReference tableReference;
private final IdentifierBinding identifierBinding;
public RootEntityBinding(
EntityTypeMetadata typeMetadata,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
super(
typeMetadata,
resolveSuperTypeBinding(
(MappedSuperclassTypeMetadata) typeMetadata.getSuperType(),
bindingOptions,
bindingState,
bindingContext
),
EntityHierarchy.HierarchyRelation.ROOT,
bindingOptions,
bindingState,
bindingContext
);
this.rootClass = new RootClass( bindingState.getMetadataBuildingContext() );
applyNaming( typeMetadata, rootClass, bindingState );
this.tableReference = TableHelper.bindPrimaryTable(
typeMetadata,
EntityHierarchy.HierarchyRelation.ROOT,
bindingOptions,
bindingState,
bindingContext
);
processSecondaryTables();
bindingState.registerTypeBinding( typeMetadata, this );
this.rootClass.setTable( tableReference.table() );
if ( getSuperTypeBinding() != null ) {
rootClass.setSuperMappedSuperclass( (MappedSuperclass) getSuperTypeBinding().getBinding() );
}
this.identifierBinding = new IdentifierBinding( this, typeMetadata, bindingOptions, bindingState, bindingContext );
this.rootClass.setIdentifier( identifierBinding.getValue() );
if ( typeMetadata.hasSubTypes() ) {
bindDiscriminator( typeMetadata, rootClass, bindingOptions, bindingState, bindingContext );
applyDiscriminatorValue( typeMetadata, rootClass );
}
bindVersion( typeMetadata, rootClass, bindingOptions, bindingState, bindingContext );
bindTenantId( typeMetadata, rootClass, bindingOptions, bindingState, bindingContext );
applyOptimisticLocking( typeMetadata, rootClass );
applyCacheRegions( typeMetadata, rootClass );
applySoftDelete( typeMetadata, rootClass, tableReference.table() );
applyCaching( typeMetadata, rootClass, bindingState );
applyFilters( typeMetadata, rootClass );
applyJpaEventListeners( typeMetadata, rootClass );
// todo : handle any super mapped-superclasses
prepareAttributeBindings( tableReference.table() );
prepareSubclassBindings();
}
private static IdentifiableTypeBinding resolveSuperTypeBinding(
MappedSuperclassTypeMetadata superTypeMetadata,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
if ( superTypeMetadata == null ) {
return null;
}
final IdentifiableTypeBinding superTypeSuperTypeBinding;
if ( superTypeMetadata.getSuperType() == null ) {
superTypeSuperTypeBinding = null;
}
else {
superTypeSuperTypeBinding = resolveSuperTypeBinding(
(MappedSuperclassTypeMetadata) superTypeMetadata.getSuperType(),
bindingOptions,
bindingState,
bindingContext
);
}
return new MappedSuperclassBinding(
superTypeMetadata,
superTypeSuperTypeBinding,
EntityHierarchy.HierarchyRelation.SUPER,
bindingOptions,
bindingState,
bindingContext
);
}
@Override
public RootClass getPersistentClass() {
return rootClass;
}
@Override
public RootClass getBinding() {
return getPersistentClass();
}
public TableReference getTableReference() {
return tableReference;
}
public IdentifierBinding getIdentifierBinding() {
return identifierBinding;
}
private void applyCacheRegions(EntityTypeMetadata source, RootClass rootClass) {
final EntityHierarchy hierarchy = source.getHierarchy();
final CacheRegion cacheRegion = hierarchy.getCacheRegion();
final NaturalIdCacheRegion naturalIdCacheRegion = hierarchy.getNaturalIdCacheRegion();
if ( cacheRegion != null ) {
rootClass.setCacheRegionName( cacheRegion.getRegionName() );
rootClass.setCacheConcurrencyStrategy( cacheRegion.getAccessType().getExternalName() );
rootClass.setLazyPropertiesCacheable( cacheRegion.isCacheLazyProperties() );
}
if ( naturalIdCacheRegion != null ) {
rootClass.setNaturalIdCacheRegionName( naturalIdCacheRegion.getRegionName() );
}
}
private void applyOptimisticLocking(EntityTypeMetadata source, RootClass rootClass) {
final var classDetails = source.getClassDetails();
final var optimisticLocking = classDetails.getAnnotationUsage( OptimisticLocking.class );
if ( optimisticLocking != null ) {
final var optimisticLockingType = optimisticLocking.getEnum( "type", OptimisticLockType.VERSION );
rootClass.setOptimisticLockStyle( OptimisticLockStyle.valueOf( optimisticLockingType.name() ) );
}
}
private void applySoftDelete(EntityTypeMetadata source, RootClass rootClass, Table primaryTable) {
final var classDetails = source.getClassDetails();
final AnnotationUsage<SoftDelete> softDeleteConfig = classDetails.getAnnotationUsage( SoftDelete.class );
if ( softDeleteConfig == null ) {
return;
}
final BasicValue softDeleteIndicatorValue = createSoftDeleteIndicatorValue( softDeleteConfig, primaryTable );
final Column softDeleteIndicatorColumn = createSoftDeleteIndicatorColumn( softDeleteConfig, softDeleteIndicatorValue );
primaryTable.addColumn( softDeleteIndicatorColumn );
rootClass.enableSoftDelete( softDeleteIndicatorColumn );
}
private BasicValue createSoftDeleteIndicatorValue(
AnnotationUsage<SoftDelete> softDeleteAnn,
Table table) {
assert softDeleteAnn != null;
final var converterClassDetails = softDeleteAnn.getClassDetails( "converter" );
final ClassBasedConverterDescriptor converterDescriptor = new ClassBasedConverterDescriptor(
converterClassDetails.toJavaClass(),
bindingContext.getBootstrapContext().getClassmateContext()
);
final BasicValue softDeleteIndicatorValue = new BasicValue( bindingState.getMetadataBuildingContext(), table );
softDeleteIndicatorValue.makeSoftDelete( softDeleteAnn.getEnum( "strategy" ) );
softDeleteIndicatorValue.setJpaAttributeConverterDescriptor( converterDescriptor );
softDeleteIndicatorValue.setImplicitJavaTypeAccess( (typeConfiguration) -> converterDescriptor.getRelationalValueResolvedType().getErasedType() );
return softDeleteIndicatorValue;
}
private Column createSoftDeleteIndicatorColumn(
AnnotationUsage<SoftDelete> softDeleteConfig,
BasicValue softDeleteIndicatorValue) {
final Column softDeleteColumn = new Column();
applyColumnName( softDeleteColumn, softDeleteConfig );
softDeleteColumn.setLength( 1 );
softDeleteColumn.setNullable( false );
softDeleteColumn.setUnique( false );
softDeleteColumn.setComment( "Soft-delete indicator" );
softDeleteColumn.setValue( softDeleteIndicatorValue );
softDeleteIndicatorValue.addColumn( softDeleteColumn );
return softDeleteColumn;
}
private void applyColumnName(Column softDeleteColumn, AnnotationUsage<SoftDelete> softDeleteConfig) {
final Database database = bindingState.getMetadataBuildingContext().getMetadataCollector().getDatabase();
final PhysicalNamingStrategy namingStrategy = bindingState.getMetadataBuildingContext().getBuildingOptions().getPhysicalNamingStrategy();
final SoftDeleteType strategy = softDeleteConfig.getEnum( "strategy" );
final String logicalColumnName = coalesce(
strategy.getDefaultColumnName(),
softDeleteConfig.getString( "columnName" )
);
final Identifier physicalColumnName = namingStrategy.toPhysicalColumnName(
database.toIdentifier( logicalColumnName ),
database.getJdbcEnvironment()
);
softDeleteColumn.setName( physicalColumnName.render( database.getDialect() ) );
}
public static void bindDiscriminator(
EntityTypeMetadata typeMetadata,
RootClass rootClass,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final InheritanceType inheritanceType = typeMetadata.getHierarchy().getInheritanceType();
final AnnotationUsage<DiscriminatorColumn> columnAnn = typeMetadata.getClassDetails().getAnnotationUsage( DiscriminatorColumn.class );
final AnnotationUsage<DiscriminatorFormula> formulaAnn = typeMetadata.getClassDetails().getAnnotationUsage( DiscriminatorFormula.class );
if ( columnAnn != null && formulaAnn != null ) {
throw new MappingException( "Entity defined both @DiscriminatorColumn and @DiscriminatorFormula - " + typeMetadata.getEntityName() );
}
if ( inheritanceType == InheritanceType.TABLE_PER_CLASS ) {
// UnionSubclass cannot define a discriminator
return;
}
if ( inheritanceType == InheritanceType.JOINED ) {
// JoinedSubclass can define a discriminator in certain circumstances
final MetadataBuildingOptions buildingOptions = bindingState.getMetadataBuildingContext().getBuildingOptions();
if ( buildingOptions.ignoreExplicitDiscriminatorsForJoinedInheritance() ) {
if ( columnAnn != null || formulaAnn != null ) {
MODEL_BINDING_LOGGER.debugf( "Skipping explicit discriminator for JOINED hierarchy due to configuration - " + rootClass.getEntityName() );
}
return;
}
if ( !buildingOptions.createImplicitDiscriminatorsForJoinedInheritance() ) {
if ( columnAnn == null && formulaAnn == null ) {
return;
}
}
}
if ( inheritanceType == InheritanceType.SINGLE_TABLE ) {
if ( !typeMetadata.hasSubTypes() ) {
return;
}
}
final BasicValue value = new BasicValue( bindingState.getMetadataBuildingContext(), rootClass.getIdentityTable() );
rootClass.setDiscriminator( value );
final DiscriminatorType discriminatorType = ColumnBinder.bindDiscriminatorColumn(
bindingContext,
formulaAnn,
value,
columnAnn,
bindingOptions,
bindingState
);
final Class<?> discriminatorJavaType;
switch ( discriminatorType ) {
case STRING -> discriminatorJavaType = String.class;
case CHAR -> discriminatorJavaType = char.class;
case INTEGER -> discriminatorJavaType = int.class;
default -> throw new IllegalStateException( "Unexpected DiscriminatorType - " + discriminatorType );
}
value.setImplicitJavaTypeAccess( typeConfiguration -> discriminatorJavaType );
}
}

View File

@ -0,0 +1,15 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface SecondPass {
boolean process();
}

View File

@ -0,0 +1,62 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.spi.PhysicalTableReference;
import org.hibernate.mapping.Table;
/**
* @see jakarta.persistence.SecondaryTable
*
* @author Steve Ebersole
*/
public record SecondaryTable(
Identifier logicalName,
Identifier logicalCatalogName,
Identifier logicalSchemaName,
Identifier physicalName,
Identifier physicalCatalogName,
Identifier physicalSchemaName,
boolean optional,
boolean owned,
Table table) implements PhysicalTableReference {
@Override
public Identifier logicalName() {
return logicalName;
}
@Override
public Identifier getLogicalSchemaName() {
return logicalSchemaName;
}
@Override
public Identifier getLogicalCatalogName() {
return logicalCatalogName;
}
@Override
public Identifier getPhysicalTableName() {
return physicalName;
}
@Override
public Identifier getPhysicalSchemaName() {
return physicalSchemaName;
}
@Override
public Identifier getPhysicalCatalogName() {
return physicalCatalogName;
}
@Override
public boolean exportable() {
return !table.isAbstract();
}
}

View File

@ -0,0 +1,248 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.util.Collections;
import java.util.List;
import org.hibernate.MappingException;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
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.mapping.Column;
import org.hibernate.mapping.IdentifiableTypeClass;
import org.hibernate.mapping.JoinedSubclass;
import org.hibernate.mapping.MappedSuperclass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.mapping.Subclass;
import org.hibernate.mapping.TableOwner;
import org.hibernate.mapping.UnionSubclass;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
/**
* @author Steve Ebersole
*/
public class SubclassEntityBinding extends EntityBinding {
private final Subclass subclass;
public SubclassEntityBinding(
EntityTypeMetadata entityTypeMetadata,
IdentifiableTypeBinding superTypeBinding,
EntityHierarchy.HierarchyRelation hierarchyRelation,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
super( entityTypeMetadata, superTypeBinding, hierarchyRelation, bindingOptions, bindingState, bindingContext );
this.subclass = createSubclass();
applyNaming( entityTypeMetadata, subclass, bindingState );
bindingState.registerTypeBinding( getTypeMetadata(), this );
if ( subclass instanceof TableOwner ) {
final var primaryTable = TableHelper.bindPrimaryTable(
entityTypeMetadata,
EntityHierarchy.HierarchyRelation.SUB,
bindingOptions,
bindingState,
bindingContext
);
final var table = primaryTable.table();
( (TableOwner) subclass ).setTable( table );
}
applyDiscriminatorValue( getTypeMetadata(), subclass );
}
@Override
public Subclass getPersistentClass() {
return subclass;
}
@Override
public Subclass getBinding() {
return getPersistentClass();
}
private Subclass createSubclass() {
final IdentifiableTypeMetadata superType = getSuperTypeMetadata();
// we have a few cases to handle here, complicated by how Hibernate has historically modeled
// mapped-superclass in its PersistentClass model (aka, not well) which manifests in some
// craziness over how these PersistentClass instantiations happen
final InheritanceType inheritanceType = superType.getHierarchy().getInheritanceType();
if ( inheritanceType == InheritanceType.JOINED ) {
return createJoinedSubclass( superTypeBinding.getBinding() );
}
if ( inheritanceType == InheritanceType.TABLE_PER_CLASS ) {
return createUnionSubclass( superTypeBinding.getBinding() );
}
assert inheritanceType == null || inheritanceType == InheritanceType.SINGLE_TABLE;
return createSingleTableSubclass( superTypeBinding.getBinding() );
}
private UnionSubclass createUnionSubclass(IdentifiableTypeClass superTypeMapping) {
if ( superTypeMapping instanceof PersistentClass superEntity ) {
return new UnionSubclass( superEntity, bindingState.getMetadataBuildingContext() );
}
else {
assert superTypeMapping instanceof MappedSuperclass;
final var superEntity = resolveSuperEntityPersistentClass( superTypeMapping );
final var binding = new UnionSubclass(
superEntity,
bindingState.getMetadataBuildingContext()
);
binding.setSuperMappedSuperclass( (MappedSuperclass) superTypeMapping );
return binding;
}
}
private JoinedSubclass createJoinedSubclass(IdentifiableTypeClass superTypeMapping) {
final JoinedSubclass joinedSubclass;
final var superTypePersistentClass = getSuperEntityBinding().getPersistentClass();
if ( superTypeMapping instanceof PersistentClass superEntity ) {
joinedSubclass = new JoinedSubclass(
superEntity,
bindingState.getMetadataBuildingContext()
);
}
else {
assert superTypeMapping instanceof MappedSuperclass;
joinedSubclass = new JoinedSubclass(
superTypePersistentClass,
bindingState.getMetadataBuildingContext()
);
joinedSubclass.setSuperMappedSuperclass( (MappedSuperclass) superTypeMapping );
}
final var joinTableReference = TableHelper.bindPrimaryTable(
getTypeMetadata(),
EntityHierarchy.HierarchyRelation.SUB,
bindingOptions,
bindingState,
bindingContext
);
joinedSubclass.setTable( joinTableReference.table() );
final PrimaryKey primaryKey = new PrimaryKey( joinTableReference.table() );
joinTableReference.table().setPrimaryKey( primaryKey );
final var targetTable = superTypePersistentClass.getIdentityTable();
if ( targetTable.getPrimaryKey() != null && targetTable.getPrimaryKey().getColumnSpan() > 0 ) {
// we can create the foreign key immediately
final var joinTableAnn = typeMetadata.getClassDetails().getAnnotationUsage( JoinTable.class );
final List<AnnotationUsage<JoinColumn>> joinColumnAnns = BindingHelper.getValue(
joinTableAnn,
"joinColumns",
Collections.emptyList()
);
final List<AnnotationUsage<JoinColumn>> inverseJoinColumnAnns = BindingHelper.getValue(
joinTableAnn,
"inverseJoinColumns",
Collections.emptyList()
);
for ( int i = 0; i < targetTable.getPrimaryKey().getColumnSpan(); i++ ) {
final Column targetColumn = targetTable.getPrimaryKey().getColumns().get( i );
final Column pkColumn;
if ( !inverseJoinColumnAnns.isEmpty() ) {
final var joinColumnAnn = resolveMatchingJoinColumnAnn(
inverseJoinColumnAnns,
targetColumn,
joinColumnAnns
);
pkColumn = ColumnBinder.bindColumn( joinColumnAnn, targetColumn::getName, true, false );
}
else {
pkColumn = ColumnBinder.bindColumn( null, targetColumn::getName, true, false );
}
primaryKey.addColumn( pkColumn );
}
final AnnotationUsage<ForeignKey> foreignKeyAnn = BindingHelper.getValue( joinTableAnn, "foreignKey", (AnnotationUsage<ForeignKey>) null );
final String foreignKeyName = foreignKeyAnn == null
? ""
: BindingHelper.getString( foreignKeyAnn, "name", ForeignKey.class, bindingContext );
final String foreignKeyDefinition = foreignKeyAnn == null
? ""
: BindingHelper.getString( foreignKeyAnn, "foreignKeyDefinition", ForeignKey.class, bindingContext );
final org.hibernate.mapping.ForeignKey foreignKey = targetTable.createForeignKey(
foreignKeyName,
primaryKey.getColumns(),
findSuperEntityMetadata().getEntityName(),
foreignKeyDefinition,
targetTable.getPrimaryKey().getColumns()
);
foreignKey.setReferencedTable( targetTable );
}
else {
throw new UnsupportedOperationException( "Delayed foreign key creation not yet implemented" );
}
// todo : bind foreign-key
// todo : do same for secondary tables
// - in both cases we can immediately process the fk if
return joinedSubclass;
}
private AnnotationUsage<JoinColumn> resolveMatchingJoinColumnAnn(
List<AnnotationUsage<JoinColumn>> inverseJoinColumnAnns,
Column pkColumn,
List<AnnotationUsage<JoinColumn>> joinColumnAnns) {
int matchPosition = -1;
for ( int j = 0; j < inverseJoinColumnAnns.size(); j++ ) {
final var inverseJoinColumnAnn = inverseJoinColumnAnns.get( j );
final String name = inverseJoinColumnAnn.getString( "name" );
if ( pkColumn.getName().equals( name ) ) {
matchPosition = j;
break;
}
}
if ( matchPosition == -1 ) {
throw new MappingException( "Unable to match primary key column [" + pkColumn.getName() + "] to any inverseJoinColumn - " + getTypeMetadata().getEntityName() );
}
return joinColumnAnns.get( matchPosition );
}
private SingleTableSubclass createSingleTableSubclass(IdentifiableTypeClass superTypeBinding) {
if ( superTypeBinding instanceof PersistentClass superEntity ) {
return new SingleTableSubclass( superEntity, bindingState.getMetadataBuildingContext() );
}
else {
assert superTypeBinding instanceof MappedSuperclass;
final PersistentClass superEntity = resolveSuperEntityPersistentClass( superTypeBinding );
final SingleTableSubclass binding = new SingleTableSubclass(
superEntity,
bindingState.getMetadataBuildingContext()
);
binding.setSuperMappedSuperclass( (MappedSuperclass) superTypeBinding );
return binding;
}
}
}

View File

@ -0,0 +1,575 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.SecondaryRow;
import org.hibernate.annotations.Subselect;
import org.hibernate.boot.model.naming.EntityNaming;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitEntityNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.models.AnnotationPlacementException;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.PhysicalTableReference;
import org.hibernate.boot.models.bind.spi.QuotedIdentifierTarget;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.DenormalizedTable;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.JoinTable;
import jakarta.persistence.Table;
/**
* @author Steve Ebersole
*/
public class TableHelper {
public static TableReference bindPrimaryTable(
EntityTypeMetadata entityTypeMetadata,
EntityHierarchy.HierarchyRelation hierarchyRelation,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final ClassDetails typeClassDetails = entityTypeMetadata.getClassDetails();
final AnnotationUsage<Table> tableAnn = typeClassDetails.getAnnotationUsage( Table.class );
final AnnotationUsage<JoinTable> joinTableAnn = typeClassDetails.getAnnotationUsage( JoinTable.class );
final AnnotationUsage<Subselect> subselectAnn = typeClassDetails.getAnnotationUsage( Subselect.class );
if ( tableAnn != null && joinTableAnn != null ) {
throw new AnnotationPlacementException( "Illegal combination of @Table and @JoinTable on " + typeClassDetails.getName() );
}
if ( tableAnn != null && subselectAnn != null ) {
throw new AnnotationPlacementException( "Illegal combination of @Table and @Subselect on " + typeClassDetails.getName() );
}
if ( joinTableAnn != null && subselectAnn != null ) {
throw new AnnotationPlacementException( "Illegal combination of @JoinTable and @Subselect on " + typeClassDetails.getName() );
}
final TableReference tableReference;
if ( entityTypeMetadata.getHierarchy().getInheritanceType() == InheritanceType.TABLE_PER_CLASS ) {
assert subselectAnn == null;
if ( hierarchyRelation == EntityHierarchy.HierarchyRelation.ROOT ) {
tableReference = bindPhysicalTable(
entityTypeMetadata,
tableAnn,
Table.class,
true,
bindingOptions,
bindingState,
bindingContext
);
}
else {
tableReference = bindUnionTable(
entityTypeMetadata,
tableAnn,
bindingOptions,
bindingState,
bindingContext
);
}
}
else if ( entityTypeMetadata.getHierarchy().getInheritanceType() == InheritanceType.SINGLE_TABLE ) {
if ( hierarchyRelation == EntityHierarchy.HierarchyRelation.ROOT ) {
tableReference = normalTableDetermination(
entityTypeMetadata,
subselectAnn,
tableAnn,
Table.class,
typeClassDetails,
bindingOptions,
bindingState,
bindingContext
);
}
else {
tableReference = null;
}
}
else {
tableReference = normalTableDetermination(
entityTypeMetadata,
subselectAnn,
joinTableAnn,
JoinTable.class,
typeClassDetails,
bindingOptions,
bindingState,
bindingContext
);
}
if ( tableReference != null ) {
bindingState.addTable( entityTypeMetadata, tableReference );
final PrimaryKey primaryKey = new PrimaryKey( tableReference.table() );
tableReference.table().setPrimaryKey( primaryKey );
}
return tableReference;
}
public static List<SecondaryTable> bindSecondaryTables(
EntityBinding entityBinding,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final ClassDetails typeClassDetails = entityBinding.getTypeMetadata().getClassDetails();
final List<AnnotationUsage<jakarta.persistence.SecondaryTable>> secondaryTableAnns = typeClassDetails.getRepeatedAnnotationUsages( jakarta.persistence.SecondaryTable.class );
final List<SecondaryTable> result = new ArrayList<>( secondaryTableAnns.size() );
secondaryTableAnns.forEach( (secondaryTableAnn) -> {
final AnnotationUsage<SecondaryRow> secondaryRowAnn = typeClassDetails.getNamedAnnotationUsage(
SecondaryRow.class,
secondaryTableAnn.getString( "name" ),
"table"
);
final SecondaryTable binding = bindSecondaryTable(
entityBinding,
secondaryTableAnn,
secondaryRowAnn,
bindingOptions,
bindingState,
bindingContext
);
result.add( binding );
bindingState.addSecondaryTable( binding );
} );
return result;
}
public static SecondaryTable bindSecondaryTable(
EntityBinding entityBinding,
AnnotationUsage<jakarta.persistence.SecondaryTable> secondaryTableAnn,
AnnotationUsage<SecondaryRow> secondaryRowAnn,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final Identifier logicalName = determineLogicalName(
entityBinding.getTypeMetadata(),
secondaryTableAnn,
bindingOptions,
bindingState,
bindingContext
);
final Identifier schemaName = resolveDatabaseIdentifier(
secondaryTableAnn,
"schema",
jakarta.persistence.SecondaryTable.class,
bindingOptions.getDefaultSchemaName(),
QuotedIdentifierTarget.SCHEMA_NAME,
bindingOptions,
bindingState,
bindingContext
);
final Identifier catalogName = resolveDatabaseIdentifier(
secondaryTableAnn,
"catalog",
jakarta.persistence.SecondaryTable.class,
bindingOptions.getDefaultCatalogName(),
QuotedIdentifierTarget.CATALOG_NAME,
bindingOptions,
bindingState,
bindingContext
);
final var binding = bindingState.getMetadataBuildingContext().getMetadataCollector().addTable(
toCanonicalName( schemaName ),
toCanonicalName( catalogName ),
logicalName.getCanonicalName(),
null,
false,
bindingState.getMetadataBuildingContext()
);
applyComment( binding, secondaryTableAnn, findCommentAnnotation(
entityBinding.getTypeMetadata(),
logicalName,
false
) );
applyOptions( binding, secondaryTableAnn );
final Join join = new Join();
join.setTable( binding );
join.setOptional( BindingHelper.getValue( secondaryRowAnn, "optional", true ) );
join.setInverse( !BindingHelper.getValue( secondaryRowAnn, "owned", true ) );
join.setPersistentClass( entityBinding.getPersistentClass() );
entityBinding.getPersistentClass().addJoin( join );
final JdbcEnvironment jdbcEnvironment = jdbcEnvironment( bindingContext );
final PhysicalNamingStrategy physicalNamingStrategy = physicalNamingStrategy( bindingContext );
return new SecondaryTable(
logicalName,
catalogName,
schemaName,
physicalNamingStrategy.toPhysicalTableName( logicalName, jdbcEnvironment ),
physicalNamingStrategy.toPhysicalCatalogName( catalogName, jdbcEnvironment ),
physicalNamingStrategy.toPhysicalSchemaName( schemaName, jdbcEnvironment ),
BindingHelper.getValue( secondaryRowAnn, "optional", true ),
BindingHelper.getValue( secondaryRowAnn, "owned", true ),
binding
);
}
private static <A extends Annotation> TableReference normalTableDetermination(
EntityTypeMetadata type,
AnnotationUsage<Subselect> subselectAnn,
AnnotationUsage<A> tableAnn,
Class<A> annotationType,
ClassDetails typeClassDetails,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final TableReference tableReference;
if ( subselectAnn != null ) {
tableReference = bindVirtualTable( type, subselectAnn, bindingOptions, bindingState, bindingContext );
}
else {
// either an explicit or implicit @Table
tableReference = bindPhysicalTable( type, tableAnn, annotationType, true, bindingOptions, bindingState, bindingContext );
}
return tableReference;
}
private static InLineView bindVirtualTable(
EntityTypeMetadata type,
AnnotationUsage<Subselect> subselectAnn,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final Identifier logicalName = implicitNamingStrategy( bindingContext ).determinePrimaryTableName(
new ImplicitEntityNameSource() {
@Override
public EntityNaming getEntityNaming() {
return type;
}
@Override
public MetadataBuildingContext getBuildingContext() {
return bindingState.getMetadataBuildingContext();
}
}
);
// todo : get rid of metadata-collector handling of tables and handle via table-state
return new InLineView(
logicalName,
bindingState.getMetadataBuildingContext().getMetadataCollector().addTable(
null,
null,
logicalName.getCanonicalName(),
BindingHelper.getString( subselectAnn, "value", Subselect.class, bindingContext ),
true,
bindingState.getMetadataBuildingContext()
)
);
}
private static <A extends Annotation> PhysicalTableReference bindPhysicalTable(
EntityTypeMetadata type,
AnnotationUsage<A> tableAnn,
Class<A> annotationType,
boolean isPrimary,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
if ( tableAnn != null ) {
return bindExplicitPhysicalTable( type, tableAnn, annotationType, isPrimary, bindingOptions, bindingState, bindingContext );
}
else {
return bindImplicitPhysicalTable( type, isPrimary, bindingOptions, bindingState, bindingContext );
}
}
private static <A extends Annotation> PhysicalTable bindExplicitPhysicalTable(
EntityTypeMetadata type,
AnnotationUsage<A> tableAnn,
Class<A> annotationType,
boolean isPrimary,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final Identifier logicalName = determineLogicalName( type, tableAnn, bindingOptions, bindingState, bindingContext );
final Identifier logicalSchemaName = resolveDatabaseIdentifier(
tableAnn,
"schema",
annotationType,
bindingOptions.getDefaultSchemaName(),
QuotedIdentifierTarget.SCHEMA_NAME,
bindingOptions,
bindingState,
bindingContext
);
final Identifier logicalCatalogName = resolveDatabaseIdentifier(
tableAnn,
"catalog",
annotationType,
bindingOptions.getDefaultCatalogName(),
QuotedIdentifierTarget.CATALOG_NAME,
bindingOptions,
bindingState,
bindingContext
);
final var binding = bindingState.getMetadataBuildingContext().getMetadataCollector().addTable(
logicalSchemaName == null ? null : logicalSchemaName.getCanonicalName(),
logicalCatalogName == null ? null : logicalCatalogName.getCanonicalName(),
logicalName.getCanonicalName(),
null,
type.isAbstract(),
bindingState.getMetadataBuildingContext()
);
applyComment( binding, tableAnn, findCommentAnnotation( type, logicalName, isPrimary ) );
applyOptions( binding, tableAnn );
final JdbcEnvironment jdbcEnvironment = jdbcEnvironment( bindingContext );
final PhysicalNamingStrategy physicalNamingStrategy = physicalNamingStrategy( bindingContext );
return new PhysicalTable(
logicalName,
logicalCatalogName,
logicalSchemaName,
physicalNamingStrategy.toPhysicalTableName( logicalName, jdbcEnvironment ),
logicalCatalogName == null ? null : physicalNamingStrategy.toPhysicalCatalogName( logicalCatalogName, jdbcEnvironment ),
logicalSchemaName == null ? null : physicalNamingStrategy.toPhysicalSchemaName( logicalSchemaName, jdbcEnvironment ),
binding
);
}
private static PhysicalTable bindImplicitPhysicalTable(
EntityTypeMetadata type,
boolean isPrimary,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final Identifier logicalName = determineLogicalName( type, null, bindingOptions, bindingState, bindingContext );
final org.hibernate.mapping.Table binding = bindingState.getMetadataBuildingContext().getMetadataCollector().addTable(
bindingOptions.getDefaultSchemaName() == null ? null : bindingOptions.getDefaultSchemaName().getCanonicalName(),
bindingOptions.getDefaultCatalogName() == null ? null : bindingOptions.getDefaultCatalogName().getCanonicalName(),
logicalName.getCanonicalName(),
null,
type.isAbstract(),
bindingState.getMetadataBuildingContext()
);
applyComment(
binding,
null,
findCommentAnnotation( type, logicalName, isPrimary )
);
final JdbcEnvironment jdbcEnvironment = jdbcEnvironment( bindingContext );
final PhysicalNamingStrategy physicalNamingStrategy = physicalNamingStrategy( bindingContext );
return new PhysicalTable(
logicalName,
bindingOptions.getDefaultCatalogName(),
bindingOptions.getDefaultSchemaName(),
physicalNamingStrategy.toPhysicalTableName( logicalName, jdbcEnvironment ),
physicalNamingStrategy.toPhysicalCatalogName( bindingOptions.getDefaultCatalogName(), jdbcEnvironment ),
physicalNamingStrategy.toPhysicalSchemaName( bindingOptions.getDefaultSchemaName(), jdbcEnvironment ),
binding
);
}
private static TableReference bindUnionTable(
EntityTypeMetadata type,
AnnotationUsage<Table> tableAnn,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
assert type.getSuperType() != null;
final TableReference superTypeTable = bindingState.getTableByOwner( type.getSuperType() );
final org.hibernate.mapping.Table unionBaseTable = superTypeTable.table();
final Identifier logicalName = determineLogicalName( type, tableAnn, bindingOptions, bindingState, bindingContext );
final Identifier logicalSchemaName = resolveDatabaseIdentifier(
tableAnn,
"schema",
Table.class,
bindingOptions.getDefaultSchemaName(),
QuotedIdentifierTarget.SCHEMA_NAME,
bindingOptions,
bindingState,
bindingContext
);
final Identifier logicalCatalogName = resolveDatabaseIdentifier(
tableAnn,
"catalog",
Table.class,
bindingOptions.getDefaultCatalogName(),
QuotedIdentifierTarget.CATALOG_NAME,
bindingOptions,
bindingState,
bindingContext
);
final DenormalizedTable binding = (DenormalizedTable) bindingState.getMetadataBuildingContext().getMetadataCollector().addDenormalizedTable(
logicalSchemaName == null ? null : logicalSchemaName.getCanonicalName(),
logicalCatalogName == null ? null : logicalCatalogName.getCanonicalName(),
logicalName.getCanonicalName(),
type.isAbstract(),
null,
unionBaseTable,
bindingState.getMetadataBuildingContext()
);
return new UnionTable( logicalName, superTypeTable, binding, !type.hasSubTypes() );
}
private static Identifier determineLogicalName(
EntityTypeMetadata type,
AnnotationUsage<?> tableAnn,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
if ( tableAnn != null ) {
final String name = StringHelper.nullIfEmpty( tableAnn.getString( "name" ) );
if ( name != null ) {
return BindingHelper.toIdentifier( name, QuotedIdentifierTarget.TABLE_NAME, bindingOptions, jdbcEnvironment( bindingContext ) );
}
}
return implicitNamingStrategy( bindingContext ).determinePrimaryTableName(
new ImplicitEntityNameSource() {
@Override
public EntityNaming getEntityNaming() {
return type;
}
@Override
public MetadataBuildingContext getBuildingContext() {
return bindingState.getMetadataBuildingContext();
}
}
);
}
private static String toCanonicalName(Identifier name) {
if ( name == null ) {
return null;
}
return name.getCanonicalName();
}
private static <A extends Annotation> Identifier resolveDatabaseIdentifier(
AnnotationUsage<A> annotationUsage,
String attributeName,
Class<A> annotationType,
Identifier fallback,
QuotedIdentifierTarget target,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final String explicit = BindingHelper.getStringOrNull( annotationUsage, attributeName );
if ( StringHelper.isNotEmpty( explicit ) ) {
return BindingHelper.toIdentifier( explicit, target, bindingOptions, jdbcEnvironment( bindingContext ) );
}
if ( fallback != null ) {
return fallback;
}
final String defaultValue = BindingHelper.getDefaultValue( attributeName, annotationType, bindingContext );
return BindingHelper.toIdentifier(defaultValue, target, bindingOptions, jdbcEnvironment( bindingContext ) );
}
private static AnnotationUsage<Comment> findCommentAnnotation(
EntityTypeMetadata type,
Identifier logicalTableName,
boolean isPrimary) {
if ( isPrimary ) {
final AnnotationUsage<Comment> unnamed = type.getClassDetails().getNamedAnnotationUsage(
Comment.class,
"",
"on"
);
if ( unnamed != null ) {
return unnamed;
}
}
return type.getClassDetails().getNamedAnnotationUsage(
Comment.class,
logicalTableName.getCanonicalName(),
"on"
);
}
private static void applyComment(
org.hibernate.mapping.Table table,
AnnotationUsage<?> tableAnn,
AnnotationUsage<Comment> commentAnn) {
if ( commentAnn != null ) {
table.setComment( commentAnn.getString( "value" ) );
}
else if ( tableAnn != null ) {
final String comment = tableAnn.getString( "comment" );
if ( StringHelper.isNotEmpty( comment ) ) {
table.setComment( comment );
}
}
}
private static void applyOptions(org.hibernate.mapping.Table table, AnnotationUsage<?> tableAnn) {
if ( tableAnn != null ) {
final String options = tableAnn.getString( "options" );
if ( StringHelper.isNotEmpty( options ) ) {
// todo : add this to Table
// table.setOptions( options );
throw new UnsupportedOperationException( "Not yet implemented" );
}
}
}
private static ImplicitNamingStrategy implicitNamingStrategy(BindingContext bindingContext) {
return bindingContext
.getBootstrapContext()
.getMetadataBuildingOptions()
.getImplicitNamingStrategy();
}
private static PhysicalNamingStrategy physicalNamingStrategy(BindingContext bindingContext) {
return bindingContext
.getBootstrapContext()
.getMetadataBuildingOptions()
.getPhysicalNamingStrategy();
}
private static JdbcEnvironment jdbcEnvironment(BindingContext bindingContext) {
return bindingContext.getServiceRegistry().getService( JdbcEnvironment.class );
}
}

View File

@ -0,0 +1,21 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.mapping.DenormalizedTable;
/**
* @author Steve Ebersole
*/
public record UnionTable(
Identifier logicalName,
TableReference base,
DenormalizedTable table,
boolean exportable) implements TableReference {
}

View File

@ -0,0 +1,98 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal.binders;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.internal.BindingHelper;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
/**
* @author Steve Ebersole
*/
public class AttributeBinder {
public static void bindImplicitJavaType(
MemberDetails member,
@SuppressWarnings("unused") Property property,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
basicValue.setImplicitJavaTypeAccess( (typeConfiguration) -> member.getType().toJavaClass() );
}
public static org.hibernate.mapping.Column processColumn(
MemberDetails member,
Property property,
BasicValue basicValue,
Table primaryTable,
BindingOptions bindingOptions,
BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
// todo : implicit column
final var columnAnn = member.getAnnotationUsage( Column.class );
final var column = ColumnBinder.bindColumn( columnAnn, property::getName );
var tableName = BindingHelper.getValue( columnAnn, "table", "" );
if ( "".equals( tableName ) || tableName == null ) {
basicValue.setTable( primaryTable );
}
else {
final Identifier identifier = Identifier.toIdentifier( tableName );
final TableReference tableByName = bindingState.getTableByName( identifier.getCanonicalName() );
basicValue.setTable( tableByName.table() );
}
basicValue.addColumn( column );
return column;
}
public static void bindConversion(
MemberDetails member,
@SuppressWarnings("unused") Property property,
@SuppressWarnings("unused") BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
// todo : do we need to account for auto-applied converters here?
final var convertAnn = member.getAnnotationUsage( Convert.class );
if ( convertAnn == null ) {
return;
}
if ( convertAnn.getBoolean( "disableConversion" ) ) {
return;
}
if ( convertAnn.getString( "attributeName" ) != null ) {
throw new ModelsException( "@Convert#attributeName should not be specified on basic mappings - " + member.getName() );
}
final ClassDetails converterClassDetails = convertAnn.getClassDetails( "converter" );
final Class<AttributeConverter<?, ?>> javaClass = converterClassDetails.toJavaClass();
basicValue.setJpaAttributeConverterDescriptor( new ClassBasedConverterDescriptor(
javaClass,
bindingContext.getClassmateContext()
) );
}
}

View File

@ -0,0 +1,202 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal.binders;
import java.lang.reflect.InvocationTargetException;
import org.hibernate.annotations.JavaType;
import org.hibernate.annotations.JdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Nationalized;
import org.hibernate.annotations.TimeZoneColumn;
import org.hibernate.annotations.TimeZoneStorage;
import org.hibernate.annotations.TimeZoneStorageType;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.AnnotationPlacementException;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Property;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.type.descriptor.java.BasicJavaType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Lob;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import static jakarta.persistence.EnumType.ORDINAL;
import static org.hibernate.annotations.TimeZoneStorageType.AUTO;
import static org.hibernate.annotations.TimeZoneStorageType.COLUMN;
/**
* @author Steve Ebersole
*/
public class BasicValueBinder {
public static void bindJavaType(
MemberDetails member,
Property property,
BasicValue basicValue,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
// todo : do we need to account for JavaTypeRegistration here?
final var javaTypeAnn = member.getAnnotationUsage( JavaType.class );
if ( javaTypeAnn == null ) {
return;
}
basicValue.setExplicitJavaTypeAccess( (typeConfiguration) -> {
final var classDetails = javaTypeAnn.getClassDetails( "value" );
final Class<BasicJavaType<?>> javaClass = classDetails.toJavaClass();
try {
return javaClass.getConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
final ModelsException modelsException = new ModelsException( "Error instantiating local @JavaType - " + member.getName() );
modelsException.addSuppressed( e );
throw modelsException;
}
} );
}
public static void bindJdbcType(
MemberDetails member,
Property property,
BasicValue basicValue,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
// todo : do we need to account for JdbcTypeRegistration here?
final var jdbcTypeAnn = member.getAnnotationUsage( JdbcType.class );
final var jdbcTypeCodeAnn = member.getAnnotationUsage( JdbcTypeCode.class );
if ( jdbcTypeAnn != null ) {
if ( jdbcTypeCodeAnn != null ) {
throw new AnnotationPlacementException(
"Illegal combination of @JdbcType and @JdbcTypeCode - " + member.getName()
);
}
basicValue.setExplicitJdbcTypeAccess( (typeConfiguration) -> {
final var classDetails = jdbcTypeAnn.getClassDetails( "value" );
final Class<org.hibernate.type.descriptor.jdbc.JdbcType> javaClass = classDetails.toJavaClass();
try {
return javaClass.getConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
final ModelsException modelsException = new ModelsException( "Error instantiating local @JdbcType - " + member.getName() );
modelsException.addSuppressed( e );
throw modelsException;
}
} );
}
else if ( jdbcTypeCodeAnn != null ) {
final Integer typeCode = jdbcTypeCodeAnn.getInteger( "value" );
basicValue.setExplicitJdbcTypeCode( typeCode );
}
}
public static void bindNationalized(
MemberDetails member,
Property property,
BasicValue basicValue,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
if ( member.getAnnotationUsage( Nationalized.class ) != null ) {
basicValue.makeNationalized();
}
}
public static void bindLob(
MemberDetails member,
Property property,
BasicValue basicValue,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
if ( member.getAnnotationUsage( Lob.class ) != null ) {
basicValue.makeLob();
}
}
public static void bindEnumerated(
MemberDetails member,
Property property,
BasicValue basicValue,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final AnnotationUsage<Enumerated> enumerated = member.getAnnotationUsage( Enumerated.class );
if ( enumerated == null ) {
return;
}
basicValue.setEnumerationStyle( enumerated.getEnum( "value", ORDINAL ) );
}
public static void bindTemporalPrecision(
MemberDetails member,
Property property,
BasicValue basicValue,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final AnnotationUsage<Temporal> temporalAnn = member.getAnnotationUsage( Temporal.class );
if ( temporalAnn == null ) {
return;
}
//noinspection deprecation
final TemporalType precision = temporalAnn.getEnum( "value" );
basicValue.setTemporalPrecision( precision );
}
public static void bindTimeZoneStorage(
MemberDetails member,
Property property,
BasicValue basicValue,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final AnnotationUsage<TimeZoneStorage> storageAnn = member.getAnnotationUsage( TimeZoneStorage.class );
final AnnotationUsage<TimeZoneColumn> columnAnn = member.getAnnotationUsage( TimeZoneColumn.class );
if ( storageAnn != null ) {
final TimeZoneStorageType strategy = storageAnn.getEnum( "value", AUTO );
if ( strategy != COLUMN && columnAnn != null ) {
throw new AnnotationPlacementException(
"Illegal combination of @TimeZoneStorage(" + strategy.name() + ") and @TimeZoneColumn"
);
}
basicValue.setTimeZoneStorageType( strategy );
}
if ( columnAnn != null ) {
final org.hibernate.mapping.Column column = (org.hibernate.mapping.Column) basicValue.getColumn();
column.setName( columnAnn.getString( "name", property.getName() + "_tz" ) );
column.setSqlType( columnAnn.getString( "columnDefinition", (String) null ) );
final var tableName = columnAnn.getString( "table", (String) null );
TableReference tableByName = null;
if ( tableName != null ) {
final Identifier identifier = Identifier.toIdentifier( tableName );
tableByName = bindingState.getTableByName( identifier.getCanonicalName() );
basicValue.setTable( tableByName.table() );
}
property.setInsertable( columnAnn.getBoolean( "insertable", true ) );
property.setUpdateable( columnAnn.getBoolean( "updatable", true ) );
}
}
}

View File

@ -0,0 +1,161 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal.binders;
import java.util.function.Supplier;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.boot.models.bind.internal.BindingHelper;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
import static org.hibernate.internal.util.NullnessHelper.nullif;
/**
* @author Steve Ebersole
*/
public class ColumnBinder {
public static Column bindColumn(
AnnotationUsage<?> annotationUsage,
Supplier<String> defaultNameSupplier) {
return bindColumn(
annotationUsage,
defaultNameSupplier,
false,
true,
255,
0,
0
);
}
public static Column bindColumn(
AnnotationUsage<?> annotationUsage,
Supplier<String> defaultNameSupplier,
boolean uniqueByDefault,
boolean nullableByDefault) {
return bindColumn(
annotationUsage,
defaultNameSupplier,
uniqueByDefault,
nullableByDefault,
255,
0,
0
);
}
public static Column bindColumn(
AnnotationUsage<?> annotationUsage,
Supplier<String> defaultNameSupplier,
boolean uniqueByDefault,
boolean nullableByDefault,
int lengthByDefault,
int precisionByDefault,
int scaleByDefault) {
final Column result = new Column();
result.setName( columnName( annotationUsage, defaultNameSupplier ) );
result.setUnique( BindingHelper.getValue( annotationUsage, "unique", uniqueByDefault ) );
result.setNullable( BindingHelper.getValue( annotationUsage, "nullable", nullableByDefault ) );
result.setSqlType( BindingHelper.getValue( annotationUsage, "columnDefinition", (String) null ) );
result.setLength( BindingHelper.getValue( annotationUsage, "length", lengthByDefault ) );
result.setPrecision( BindingHelper.getValue( annotationUsage, "precision", precisionByDefault ) );
result.setScale( BindingHelper.getValue( annotationUsage, "scale", scaleByDefault ) );
return result;
}
public static String columnName(
AnnotationUsage<?> columnAnnotation,
Supplier<String> defaultNameSupplier) {
if ( columnAnnotation == null ) {
return defaultNameSupplier.get();
}
return nullif( columnAnnotation.getAttributeValue( "name" ), defaultNameSupplier );
}
private ColumnBinder() {
}
public static DiscriminatorType bindDiscriminatorColumn(
BindingContext bindingContext,
AnnotationUsage<DiscriminatorFormula> formulaAnn,
BasicValue value,
AnnotationUsage<DiscriminatorColumn> columnAnn,
BindingOptions bindingOptions,
BindingState bindingState) {
final DiscriminatorType discriminatorType;
if ( formulaAnn != null ) {
final Formula formula = new Formula( formulaAnn.getString( "value" ) );
value.addFormula( formula );
discriminatorType = formulaAnn.getEnum( "discriminatorType", DiscriminatorType.STRING );
}
else {
final Column column = new Column();
value.addColumn( column, true, false );
discriminatorType = BindingHelper.getValue( columnAnn, "discriminatorType", DiscriminatorType.STRING );
column.setName( columnName( columnAnn, () -> "dtype" ) );
column.setLength( (Integer) BindingHelper.getValue(
columnAnn,
"length",
() -> {
final AnnotationDescriptor<DiscriminatorColumn> descriptor;
if ( columnAnn != null ) {
descriptor = columnAnn.getAnnotationDescriptor();
}
else {
descriptor = bindingContext.getAnnotationDescriptorRegistry().getDescriptor( DiscriminatorColumn.class );
}
return descriptor.getAttribute( "length" ).getAttributeMethod().getDefaultValue();
}
) );
column.setSqlType( BindingHelper.getGloballyQuotedValue(
columnAnn,
"columnDefinition",
() -> {
final AnnotationDescriptor<DiscriminatorColumn> descriptor;
if ( columnAnn != null ) {
descriptor = columnAnn.getAnnotationDescriptor();
}
else {
descriptor = bindingContext.getAnnotationDescriptorRegistry().getDescriptor( DiscriminatorColumn.class );
}
return (String) descriptor.getAttribute( "columnDefinition" ).getAttributeMethod().getDefaultValue();
},
bindingOptions,
bindingState
) );
applyOptions( column, columnAnn );
}
return discriminatorType;
}
private static void applyOptions(Column column, AnnotationUsage<?> columnAnn) {
if ( columnAnn != null ) {
final String options = columnAnn.getString( "options" );
if ( StringHelper.isNotEmpty( options ) ) {
// todo : see https://hibernate.atlassian.net/browse/HHH-17449
// table.setOptions( options );
throw new UnsupportedOperationException( "Not yet implemented" );
}
}
}
}

View File

@ -0,0 +1,129 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal.binders;
import org.hibernate.MappingException;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.type.spi.TypeConfiguration;
import static java.util.Collections.singletonMap;
import static org.hibernate.boot.models.bind.internal.binders.AttributeBinder.bindConversion;
import static org.hibernate.boot.models.bind.internal.binders.AttributeBinder.bindImplicitJavaType;
import static org.hibernate.boot.models.bind.internal.binders.AttributeBinder.processColumn;
import static org.hibernate.boot.models.bind.internal.binders.BasicValueBinder.bindEnumerated;
import static org.hibernate.boot.models.bind.internal.binders.BasicValueBinder.bindJavaType;
import static org.hibernate.boot.models.bind.internal.binders.BasicValueBinder.bindJdbcType;
/**
* @author Steve Ebersole
*/
public class TenantIdBinder {
public static final String FILTER_NAME = "_tenantId";
public static final String PARAMETER_NAME = "tenantId";
public static void bindTenantId(
EntityTypeMetadata managedType,
RootClass rootClass,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final AttributeMetadata tenantIdAttribute = managedType.getHierarchy().getTenantIdAttribute();
if ( tenantIdAttribute == null ) {
return;
}
bindTenantId( tenantIdAttribute, rootClass, bindingOptions, bindingState, bindingContext );
}
public static void bindTenantId(
AttributeMetadata attributeMetadata,
RootClass rootClass,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final InFlightMetadataCollector collector = bindingState.getMetadataBuildingContext().getMetadataCollector();
final TypeConfiguration typeConfiguration = collector.getTypeConfiguration();
final MemberDetails memberDetails = attributeMetadata.getMember();
final String returnedClassName = memberDetails.getType().getClassName();
final BasicType<Object> tenantIdType = typeConfiguration
.getBasicTypeRegistry()
.getRegisteredType( returnedClassName );
final FilterDefinition filterDefinition = collector.getFilterDefinition( FILTER_NAME );
if ( filterDefinition == null ) {
collector.addFilterDefinition( new FilterDefinition(
FILTER_NAME,
"",
singletonMap( PARAMETER_NAME, tenantIdType )
) );
}
else {
final JavaType<?> tenantIdTypeJtd = tenantIdType.getJavaTypeDescriptor();
final JavaType<?> parameterJtd = filterDefinition
.getParameterJdbcMapping( PARAMETER_NAME )
.getJavaTypeDescriptor();
if ( !parameterJtd.getJavaTypeClass().equals( tenantIdTypeJtd.getJavaTypeClass() ) ) {
throw new MappingException(
"all @TenantId fields must have the same type: "
+ parameterJtd.getJavaType().getTypeName()
+ " differs from "
+ tenantIdTypeJtd.getJavaType().getTypeName()
);
}
}
final Property property = new Property();
rootClass.addProperty( property );
property.setName( attributeMetadata.getName() );
final BasicValue basicValue = new BasicValue( bindingState.getMetadataBuildingContext(), rootClass.getRootTable() );
property.setValue( basicValue );
bindImplicitJavaType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindJavaType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindJdbcType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindConversion( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindEnumerated( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
processColumn(
memberDetails,
property,
basicValue,
rootClass.getRootTable(),
bindingOptions,
bindingState,
bindingContext
);
property.resetUpdateable( false );
property.resetOptional( false );
}
public static void bindTenantId(
AttributeMetadata attributeMetadata,
EntityTypeMetadata managedType,
RootClass typeBinding,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
bindTenantId( attributeMetadata, typeBinding, bindingOptions, bindingState, bindingContext );
}
}

View File

@ -0,0 +1,112 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.internal.binders;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.models.spi.MemberDetails;
import static org.hibernate.boot.models.bind.internal.binders.AttributeBinder.bindImplicitJavaType;
import static org.hibernate.boot.models.bind.internal.binders.AttributeBinder.processColumn;
import static org.hibernate.boot.models.bind.internal.binders.BasicValueBinder.bindJavaType;
import static org.hibernate.boot.models.bind.internal.binders.BasicValueBinder.bindJdbcType;
/**
* @author Steve Ebersole
*/
public class VersionBinder {
public static void bindVersion(
EntityTypeMetadata typeMetadata,
RootClass rootClass,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final AttributeMetadata versionAttribute = typeMetadata.getHierarchy().getVersionAttribute();
if ( versionAttribute == null ) {
return;
}
bindVersion( versionAttribute, rootClass, bindingOptions, bindingState, bindingContext );
}
public static void bindVersion(
AttributeMetadata versionAttribute,
RootClass typeBinding,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final Property property = new Property();
property.setName( versionAttribute.getName() );
typeBinding.setVersion( property );
typeBinding.addProperty( property );
final BasicValue basicValue = new BasicValue(
bindingState.getMetadataBuildingContext(),
typeBinding.getRootTable()
);
property.setValue( basicValue );
final MemberDetails memberDetails = versionAttribute.getMember();
bindImplicitJavaType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindJavaType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindJdbcType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
final org.hibernate.mapping.Column column = processColumn(
memberDetails,
property,
basicValue,
typeBinding.getRootTable(),
bindingOptions,
bindingState,
bindingContext
);
// force it to be non-nullable
column.setNullable( false );
}
public static void bindVersion(
AttributeMetadata attributeMetadata,
EntityTypeMetadata managedType,
RootClass typeBinding,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final Property property = new Property();
property.setName( attributeMetadata.getName() );
typeBinding.setVersion( property );
typeBinding.addProperty( property );
final BasicValue basicValue = new BasicValue(
bindingState.getMetadataBuildingContext(),
typeBinding.getRootTable()
);
property.setValue( basicValue );
final MemberDetails memberDetails = attributeMetadata.getMember();
bindImplicitJavaType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindJavaType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
bindJdbcType( memberDetails, property, basicValue, bindingOptions, bindingState, bindingContext );
final org.hibernate.mapping.Column column = processColumn(
memberDetails,
property,
basicValue,
typeBinding.getRootTable(),
bindingOptions,
bindingState,
bindingContext
);
// force it to be non-nullable
column.setNullable( false );
}
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.models.spi.SourceModelContext;
import org.hibernate.service.ServiceRegistry;
import jakarta.persistence.SharedCacheMode;
/**
* Contextual information used while {@linkplain BindingCoordinator table}
* {@linkplain org.hibernate.boot.model.process.spi.ManagedResources managed-resources} into
* into Hibernate's {@linkplain org.hibernate.mapping boot-time model}.
*
* @author Steve Ebersole
*/
public interface BindingContext extends SourceModelContext {
GlobalRegistrations getGlobalRegistrations();
ClassmateContext getClassmateContext();
SharedCacheMode getSharedCacheMode();
ImplicitNamingStrategy getImplicitNamingStrategy();
PhysicalNamingStrategy getPhysicalNamingStrategy();
BootstrapContext getBootstrapContext();
default ServiceRegistry getServiceRegistry() {
return getBootstrapContext().getServiceRegistry();
}
}

View File

@ -0,0 +1,213 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.boot.models.AnnotationPlacementException;
import org.hibernate.boot.models.bind.internal.RootEntityBinding;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.CategorizedDomainModel;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER;
/**
* Responsible for processing {@linkplain org.hibernate.boot.model.process.spi.ManagedResources managed-resources}
* and table them into Hibernate's {@linkplain org.hibernate.mapping boot-time model}.
*
* @author Steve Ebersole
*/
public class BindingCoordinator {
private final CategorizedDomainModel categorizedDomainModel;
private final BindingState bindingState;
private final BindingOptions bindingOptions;
private final BindingContext bindingContext;
public BindingCoordinator(
CategorizedDomainModel categorizedDomainModel,
BindingState bindingState,
BindingOptions bindingOptions,
BindingContext bindingContext) {
this.categorizedDomainModel = categorizedDomainModel;
this.bindingOptions = bindingOptions;
this.bindingState = bindingState;
this.bindingContext = bindingContext;
}
/**
* Main entry point into this table coordination
*
* @param categorizedDomainModel The model to be processed
* @param options Options for the table
* @param bindingContext Access to needed information and delegates
*/
public static void coordinateBinding(
CategorizedDomainModel categorizedDomainModel,
BindingState state,
BindingOptions options,
BindingContext bindingContext) {
final BindingCoordinator coordinator = new BindingCoordinator(
categorizedDomainModel,
state,
options,
bindingContext
);
coordinator.coordinateBinding();
}
private void coordinateBinding() {
// todo : to really work on these, need to changes to MetadataBuildingContext/InFlightMetadataCollector
coordinateGlobalBindings();
coordinateModelBindings();
}
private void coordinateModelBindings() {
// process hierarchy
categorizedDomainModel.forEachEntityHierarchy( this::processHierarchy );
// complete tables
// modelBinders.getTableBinder().processSecondPasses();
// bindingState.forEachType( this::processModelSecondPasses );
}
// private void processModelSecondPasses(String typeName, ManagedTypeBinding table) {
// table.processSecondPasses();
// }
private void coordinateGlobalBindings() {
processGenerators( categorizedDomainModel.getGlobalRegistrations() );
processConverters( categorizedDomainModel.getGlobalRegistrations() );
processJavaTypeRegistrations( categorizedDomainModel.getGlobalRegistrations() );
processJdbcTypeRegistrations( categorizedDomainModel.getGlobalRegistrations() );
processCustomTypes( categorizedDomainModel.getGlobalRegistrations() );
processInstantiators( categorizedDomainModel.getGlobalRegistrations() );
processEventListeners( categorizedDomainModel.getGlobalRegistrations() );
processFilterDefinitions( categorizedDomainModel.getGlobalRegistrations() );
}
private RootEntityBinding processHierarchy(int index, EntityHierarchy hierarchy) {
final EntityTypeMetadata rootTypeMetadata = hierarchy.getRoot();
MODEL_BINDING_LOGGER.tracef( "Creating root entity table - %s", rootTypeMetadata.getEntityName() );
return new RootEntityBinding( rootTypeMetadata, bindingOptions, bindingState, bindingContext );
}
private void processGenerators(GlobalRegistrations globalRegistrations) {
// todo : process these
globalRegistrations.getSequenceGeneratorRegistrations();
globalRegistrations.getTableGeneratorRegistrations();
globalRegistrations.getGenericGeneratorRegistrations();
}
private void processConverters(GlobalRegistrations globalRegistrations) {
// todo : process these
globalRegistrations.getConverterRegistrations();
}
private void processJavaTypeRegistrations(GlobalRegistrations globalRegistrations) {
// todo : process these
globalRegistrations.getJavaTypeRegistrations();
}
private void processJdbcTypeRegistrations(GlobalRegistrations globalRegistrations) {
// todo : process these
globalRegistrations.getJdbcTypeRegistrations();
}
private void processCustomTypes(GlobalRegistrations globalRegistrations) {
// todo : process these
globalRegistrations.getUserTypeRegistrations();
globalRegistrations.getCompositeUserTypeRegistrations();
globalRegistrations.getCollectionTypeRegistrations();
}
private void processInstantiators(GlobalRegistrations globalRegistrations) {
// todo : process these
globalRegistrations.getEmbeddableInstantiatorRegistrations();
}
private void processEventListeners(GlobalRegistrations globalRegistrations) {
// todo : process these
globalRegistrations.getEntityListenerRegistrations();
}
private void processFilterDefinitions(GlobalRegistrations globalRegistrations) {
globalRegistrations.getFilterDefRegistrations().forEach( (s, filterDefRegistration) -> {
bindingState.apply( filterDefRegistration );
} );
}
private void processTables(AttributeMetadata attribute) {
final AnnotationUsage<JoinTable> joinTableAnn = attribute.getMember().getAnnotationUsage( JoinTable.class );
final AnnotationUsage<CollectionTable> collectionTableAnn = attribute.getMember().getAnnotationUsage( CollectionTable.class );
final AnnotationUsage<OneToOne> oneToOneAnn = attribute.getMember().getAnnotationUsage( OneToOne.class );
final AnnotationUsage<ManyToOne> manyToOneAnn = attribute.getMember().getAnnotationUsage( ManyToOne.class );
final AnnotationUsage<ElementCollection> elementCollectionAnn = attribute.getMember().getAnnotationUsage( ElementCollection.class );
final AnnotationUsage<OneToMany> oneToManyAnn = attribute.getMember().getAnnotationUsage( OneToMany.class );
final AnnotationUsage<Any> anyAnn = attribute.getMember().getAnnotationUsage( Any.class );
final AnnotationUsage<ManyToAny> manyToAnyAnn = attribute.getMember().getAnnotationUsage( ManyToAny.class );
final boolean hasAnyTableAnnotations = joinTableAnn != null
|| collectionTableAnn != null;
final boolean hasAnyAssociationAnnotations = oneToOneAnn != null
|| manyToOneAnn != null
|| elementCollectionAnn != null
|| oneToManyAnn != null
|| anyAnn != null
|| manyToAnyAnn != null;
if ( !hasAnyAssociationAnnotations ) {
if ( hasAnyTableAnnotations ) {
throw new AnnotationPlacementException(
"@JoinTable or @CollectionTable used on non-association attribute - " + attribute.getMember()
);
}
}
if ( elementCollectionAnn != null ) {
if ( joinTableAnn != null ) {
throw new AnnotationPlacementException(
"@JoinTable should not be used with @ElementCollection; use @CollectionTable instead - " + attribute.getMember()
);
}
// an element-collection "owns" the collection table, so create it right away
}
// ^^ accounting for owning v. "inverse" side
//
// on the owning side we get/create the reference and configure it
//
// on the inverse side we just get the reference.
//
// a cool idea here for "smarter second-pass"... on the inverse side -
// TableReference mappedTable = bindingState.
//
}
}

View File

@ -0,0 +1,21 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
import java.util.EnumSet;
import org.hibernate.boot.model.naming.Identifier;
/**
* @author Steve Ebersole
*/
public interface BindingOptions {
Identifier getDefaultCatalogName();
Identifier getDefaultSchemaName();
EnumSet<QuotedIdentifierTarget> getGloballyQuotedIdentifierTargets();
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.models.bind.internal.IdentifiableTypeBinding;
import org.hibernate.boot.models.bind.internal.ManagedTypeBinding;
import org.hibernate.boot.models.bind.internal.SecondaryTable;
import org.hibernate.boot.models.categorize.spi.FilterDefRegistration;
import org.hibernate.boot.models.categorize.spi.ManagedTypeMetadata;
import org.hibernate.boot.models.categorize.spi.TableOwner;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.internal.util.KeyedConsumer;
import org.hibernate.models.spi.ClassDetails;
/**
* The idea here is mostly the role filled by InFlightMetadataCollector upstream.
* We should incorporate any improvements here into there when we move this upstream.
*
* @author Steve Ebersole
*/
public interface BindingState {
MetadataBuildingContext getMetadataBuildingContext();
default Database getDatabase() {
return getMetadataBuildingContext().getMetadataCollector().getDatabase();
}
JdbcServices getJdbcServices();
void apply(FilterDefRegistration registration);
int getTableCount();
void forEachTable(KeyedConsumer<String,TableReference> consumer);
<T extends TableReference> T getTableByName(String name);
<T extends TableReference> T getTableByOwner(TableOwner owner);
void addTable(TableOwner owner, TableReference table);
void addSecondaryTable(SecondaryTable table);
void registerTypeBinding(ManagedTypeMetadata type, ManagedTypeBinding binding);
ManagedTypeBinding getTypeBinding(ClassDetails type);
default ManagedTypeBinding getTypeBinding(ManagedTypeMetadata type) {
return getTypeBinding( type.getClassDetails() );
}
IdentifiableTypeBinding getSuperTypeBinding(ClassDetails type);
}

View File

@ -0,0 +1,13 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
/**
* @author Steve Ebersole
*/
public interface PersistentTableReference extends TableReference, SchemaAware {
}

View File

@ -0,0 +1,16 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
import org.hibernate.boot.model.naming.Identifier;
/**
* @author Steve Ebersole
*/
public interface PhysicalTableReference extends PersistentTableReference {
Identifier getPhysicalTableName();
}

View File

@ -0,0 +1,24 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
/**
* @author Steve Ebersole
*/
public enum QuotedIdentifierTarget {
CATALOG_NAME,
SCHEMA_NAME,
TABLE_NAME,
SEQUENCE_NAME,
CALLABLE_NAME,
FOREIGN_KEY,
FOREIGN_DEFINITION,
INDEX,
TYPE_NAME,
COLUMN_NAME,
COLUMN_DEFINITION
}

View File

@ -0,0 +1,20 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
import org.hibernate.boot.model.naming.Identifier;
/**
* @author Steve Ebersole
*/
public interface SchemaAware {
Identifier getPhysicalSchemaName();
Identifier getLogicalSchemaName();
Identifier getPhysicalCatalogName();
Identifier getLogicalCatalogName();
}

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.bind.spi;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.bind.internal.InLineView;
import org.hibernate.boot.models.bind.internal.PhysicalTable;
import org.hibernate.boot.models.bind.internal.PhysicalView;
import org.hibernate.boot.models.bind.internal.SecondaryTable;
import org.hibernate.mapping.Table;
/**
* Following the ANSI SQL "table reference" rule, will be one of <ul>
* <li>a {@linkplain PhysicalTable physical table}</li>
* <li>a {@linkplain SecondaryTable secondary table}</li>
* <li>a {@linkplain PhysicalView physical view}</li>
* <li>a {@linkplain InLineView in-line view}</li>
* </ul>
*
* @author Steve Ebersole
*/
public interface TableReference {
/**
* The name used across the metamodel sources (in the annotations, XML, etc...).
* In the case of physical tables and views, the logical name might not be the same
* as the table or view name (through {@linkplain org.hibernate.boot.model.naming.PhysicalNamingStrategy}, e.g.).
*/
Identifier logicalName();
/**
* Should this "table" be exposed to schema tooling?
*/
boolean exportable();
Table table();
}

View File

@ -0,0 +1,41 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize;
import org.hibernate.Internal;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.ValidIdRange;
import static org.jboss.logging.Logger.Level.INFO;
/**
* todo : find the proper min/max id range
*
* @author Steve Ebersole
*/
@Internal
@MessageLogger( projectCode = "HHH" )
@ValidIdRange( min = 999901, max = 999999 )
public interface ModelCategorizationLogging extends BasicLogger {
String NAME = "org.hibernate.models.orm";
Logger MODEL_CATEGORIZATION_LOGGER = Logger.getLogger( NAME );
ModelCategorizationLogging MODEL_CATEGORIZATION_MSG_LOGGER = Logger.getMessageLogger( ModelCategorizationLogging.class, NAME );
@LogMessage(level = INFO)
@Message( id = 999901, value = "Entity `%s` used both @DynamicInsert and @SQLInsert" )
void dynamicAndCustomInsert(String entityName);
@LogMessage(level = INFO)
@Message( id = 999902, value = "Entity `%s` used both @DynamicUpdate and @SQLUpdate" )
void dynamicAndCustomUpdate(String entityName);
}

View File

@ -0,0 +1,245 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.JpaAnnotations;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.IdentifiableTypeMetadata;
import org.hibernate.boot.models.categorize.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import jakarta.persistence.Access;
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 AbstractIdentifiableTypeMetadata superType;
private final Set<IdentifiableTypeMetadata> subTypes = new HashSet<>();
private final AccessType accessType;
/**
* Used when creating the hierarchy root-root
*
* @param accessType This is the hierarchy default
*/
public AbstractIdentifiableTypeMetadata(
ClassDetails classDetails,
EntityHierarchy hierarchy,
AccessType accessType,
ModelCategorizationContext processingContext) {
super( classDetails, processingContext );
this.hierarchy = hierarchy;
this.superType = null;
this.accessType = determineAccessType( accessType );
}
public AbstractIdentifiableTypeMetadata(
ClassDetails classDetails,
EntityHierarchy hierarchy,
AbstractIdentifiableTypeMetadata superType,
ModelCategorizationContext processingContext) {
super( classDetails, processingContext );
assert superType != null;
this.hierarchy = hierarchy;
this.superType = superType;
this.accessType = determineAccessType( superType.getAccessType() );
}
protected void postInstantiate(HierarchyTypeConsumer typeConsumer) {
typeConsumer.acceptType( this );
// now we can effectively walk subs
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 );
}
} );
}
private AccessType determineAccessType(AccessType defaultAccessType) {
final AnnotationUsage<Access> annotation = getClassDetails().getAnnotationUsage( JpaAnnotations.ACCESS );
if ( annotation != null ) {
return annotation.getAttributeValue( "value" );
}
return defaultAccessType;
}
private 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 AccessType getAccessType() {
return accessType;
}
protected void collectConversionInfo() {
// we only need to do this on root
}
protected void collectAttributeOverrides() {
// we only need to do this on root
}
protected void collectAssociationOverrides() {
// we only need to do this on root
}
protected List<JpaEventListener> collectHierarchyEventListeners(JpaEventListener localCallback) {
final ClassDetails classDetails = getClassDetails();
final List<JpaEventListener> combined = new ArrayList<>();
if ( classDetails.getAnnotationUsage( ExcludeSuperclassListeners.class ) == null ) {
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 AnnotationUsage<EntityListeners> entityListenersAnnotation = classDetails.getAnnotationUsage( EntityListeners.class );
if ( entityListenersAnnotation == null ) {
return;
}
final List<ClassDetails> entityListenerClasses = entityListenersAnnotation.getAttributeValue( "value" );
if ( CollectionHelper.isEmpty( entityListenerClasses ) ) {
return;
}
entityListenerClasses.forEach( (listenerClass) -> {
consumer.accept( JpaEventListener.from( JpaEventListenerStyle.LISTENER, listenerClass ) );
} );
}
protected List<JpaEventListener> collectCompleteEventListeners(ModelCategorizationContext modelContext) {
final ClassDetails classDetails = getClassDetails();
if ( classDetails.getAnnotationUsage( ExcludeDefaultListeners.class ) != null ) {
return getHierarchyJpaEventListeners();
}
final List<JpaEventListener> combined = new ArrayList<>();
combined.addAll( modelContext.getDefaultEventListeners() );
combined.addAll( getHierarchyJpaEventListeners() );
return combined;
}
}

View File

@ -0,0 +1,303 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.model.source.spi.AttributeRole;
import org.hibernate.boot.model.source.spi.NaturalIdMutability;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.models.MultipleAttributeNaturesException;
import org.hibernate.boot.models.categorize.ModelCategorizationLogging;
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.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Basic;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embedded;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import static org.hibernate.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, getAccessType(), memberConsumer, modelContext );
final List<AttributeMetadata> attributeList = arrayList( backingMembers.size() );
for ( MemberDetails backingMember : backingMembers ) {
final AttributeMetadata attribute = new AttributeMetadataImpl(
backingMember.resolveAttributeName(),
determineAttributeNature( backingMember ),
backingMember
);
attributeList.add( attribute );
}
return attributeList;
}
/**
* Determine the attribute's nature - is it a basic mapping, an embeddable, ...?
*
* Also performs some simple validation around multiple natures being indicated
*/
private AttributeMetadata.AttributeNature determineAttributeNature(MemberDetails backingMember) {
final EnumSet<AttributeMetadata.AttributeNature> natures = EnumSet.noneOf( AttributeMetadata.AttributeNature.class );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// first, look for explicit nature annotations
final AnnotationUsage<Any> any = backingMember.getAnnotationUsage( HibernateAnnotations.ANY );
final AnnotationUsage<Basic> basic = backingMember.getAnnotationUsage( JpaAnnotations.BASIC );
final AnnotationUsage<ElementCollection> elementCollection = backingMember.getAnnotationUsage( JpaAnnotations.ELEMENT_COLLECTION );
final AnnotationUsage<Embedded> embedded = backingMember.getAnnotationUsage( JpaAnnotations.EMBEDDED );
final AnnotationUsage<EmbeddedId> embeddedId = backingMember.getAnnotationUsage( JpaAnnotations.EMBEDDED_ID );
final AnnotationUsage<ManyToAny> manyToAny = backingMember.getAnnotationUsage( HibernateAnnotations.MANY_TO_ANY );
final AnnotationUsage<ManyToMany> manyToMany = backingMember.getAnnotationUsage( JpaAnnotations.MANY_TO_MANY );
final AnnotationUsage<ManyToOne> manyToOne = backingMember.getAnnotationUsage( JpaAnnotations.MANY_TO_ONE );
final AnnotationUsage<OneToMany> oneToMany = backingMember.getAnnotationUsage( JpaAnnotations.ONE_TO_MANY );
final AnnotationUsage<OneToOne> oneToOne = backingMember.getAnnotationUsage( JpaAnnotations.ONE_TO_ONE );
if ( basic != null ) {
natures.add( AttributeMetadata.AttributeNature.BASIC );
}
if ( embedded != null
|| embeddedId != null
|| ( backingMember.getType() != null && backingMember.getType().getAnnotationUsage( JpaAnnotations.EMBEDDABLE ) != null ) ) {
natures.add( AttributeMetadata.AttributeNature.EMBEDDED );
}
if ( any != null ) {
natures.add( AttributeMetadata.AttributeNature.ANY );
}
if ( oneToOne != null
|| manyToOne != null ) {
natures.add( AttributeMetadata.AttributeNature.TO_ONE );
}
final boolean plural = oneToMany != null
|| manyToMany != null
|| elementCollection != null
|| manyToAny != null;
if ( plural ) {
natures.add( AttributeMetadata.AttributeNature.PLURAL );
}
// look at annotations that imply a nature
// NOTE : these could apply to the element or index of collection, so
// only do these if it is not a collection
if ( !plural ) {
// first implicit basic nature
if ( backingMember.getAnnotationUsage( JpaAnnotations.TEMPORAL ) != null
|| backingMember.getAnnotationUsage( JpaAnnotations.LOB ) != null
|| backingMember.getAnnotationUsage( JpaAnnotations.ENUMERATED ) != null
|| backingMember.getAnnotationUsage( JpaAnnotations.CONVERT ) != null
|| backingMember.getAnnotationUsage( JpaAnnotations.VERSION ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.GENERATED ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.NATIONALIZED ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.TZ_COLUMN ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.TZ_STORAGE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.TYPE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.TENANT_ID ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.JAVA_TYPE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.JDBC_TYPE_CODE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.JDBC_TYPE ) != null ) {
natures.add( AttributeMetadata.AttributeNature.BASIC );
}
// then embedded
if ( backingMember.getAnnotationUsage( HibernateAnnotations.EMBEDDABLE_INSTANTIATOR ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.COMPOSITE_TYPE ) != null ) {
natures.add( AttributeMetadata.AttributeNature.EMBEDDED );
}
// and any
if ( backingMember.getAnnotationUsage( HibernateAnnotations.ANY_DISCRIMINATOR ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.ANY_DISCRIMINATOR_VALUE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.ANY_DISCRIMINATOR_VALUES ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.ANY_KEY_JAVA_TYPE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.ANY_KEY_JAVA_CLASS ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.ANY_KEY_JDBC_TYPE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.ANY_KEY_JDBC_TYPE_CODE ) != null ) {
natures.add( AttributeMetadata.AttributeNature.ANY );
}
}
int size = natures.size();
switch ( size ) {
case 0: {
ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER.debugf(
"Implicitly interpreting attribute `%s` as BASIC",
backingMember.resolveAttributeName()
);
return AttributeMetadata.AttributeNature.BASIC;
}
case 1: {
return natures.iterator().next();
}
default: {
throw new MultipleAttributeNaturesException( backingMember.resolveAttributeName(), natures );
}
}
}
// @Override
// public <A extends Annotation> List<AnnotationUsage<A>> findAnnotations(AnnotationDescriptor<A> type) {
// return classDetails.getAnnotations( type );
// }
//
// @Override
// public <A extends Annotation> void forEachAnnotation(AnnotationDescriptor<A> type, Consumer<AnnotationUsage<A>> consumer) {
// classDetails.forEachAnnotation( type, consumer );
// }
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Stuff affecting attributes built from this managed type.
public boolean canAttributesBeInsertable() {
return true;
}
public boolean canAttributesBeUpdatable() {
return true;
}
public NaturalIdMutability getContainerNaturalIdMutability() {
return NaturalIdMutability.NOT_NATURAL_ID;
}
}

View File

@ -0,0 +1,108 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.JpaAnnotations;
import org.hibernate.boot.models.categorize.spi.AllMemberConsumer;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
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.AccessType;
/**
* "Template" support for writing PersistentAttributeMemberResolver
* implementations.
*
* @author Steve Ebersole
*/
public abstract class AbstractPersistentAttributeMemberResolver implements PersistentAttributeMemberResolver {
/**
* This is the call that represents the bulk of the work needed to resolve
* the persistent attribute members. It is the strategy specific portion
* for sure.
* <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
* @param processingContext The local context
*/
protected abstract List<MemberDetails> resolveAttributesMembers(
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
AccessType classLevelAccessType,
ModelCategorizationContext processingContext);
@Override
public List<MemberDetails> resolveAttributesMembers(
ClassDetails classDetails,
AccessType classLevelAccessType,
AllMemberConsumer memberConsumer,
ModelCategorizationContext processingContext) {
final Set<FieldDetails> transientFields = new HashSet<>();
final Set<MethodDetails> transientMethods = new HashSet<>();
collectMembersMarkedTransient(
transientFields::add,
transientMethods::add,
classDetails,
memberConsumer,
processingContext
);
return resolveAttributesMembers(
transientFields::contains,
transientMethods::contains,
classDetails,
classLevelAccessType,
processingContext
);
}
protected void collectMembersMarkedTransient(
final Consumer<FieldDetails> transientFieldConsumer,
final Consumer<MethodDetails> transientMethodConsumer,
ClassDetails classDetails,
AllMemberConsumer memberConsumer,
@SuppressWarnings("unused") ModelCategorizationContext processingContext) {
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.getAnnotationUsage( JpaAnnotations.TRANSIENT ) != null ) {
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.getAnnotationUsage( JpaAnnotations.TRANSIENT ) != null ) {
transientMethodConsumer.accept( methodDetails );
}
}
}
}

View File

@ -0,0 +1,28 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.categorize.spi.AggregatedKeyMapping;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
/**
* @author Steve Ebersole
*/
public class AggregatedKeyMappingImpl implements AggregatedKeyMapping {
private final AttributeMetadata attribute;
public AggregatedKeyMappingImpl(AttributeMetadata attribute) {
this.attribute = attribute;
}
@Override
public AttributeMetadata getAttribute() {
return attribute;
}
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.models.spi.MemberDetails;
/**
* @author Steve Ebersole
*/
public class AttributeMetadataImpl implements AttributeMetadata {
private final String name;
private final AttributeNature nature;
private final MemberDetails member;
public AttributeMetadataImpl(String name, AttributeNature nature, MemberDetails member) {
this.name = name;
this.nature = nature;
this.member = member;
}
@Override
public String getName() {
return name;
}
@Override
public AttributeNature getNature() {
return nature;
}
@Override
public MemberDetails getMember() {
return member;
}
@Override
public String toString() {
return "AttributeMetadata(`" + name + "`)";
}
}

View File

@ -0,0 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.BasicKeyMapping;
/**
* @author Steve Ebersole
*/
public class BasicKeyMappingImpl implements BasicKeyMapping {
private final AttributeMetadata attribute;
public BasicKeyMappingImpl(AttributeMetadata attribute) {
this.attribute = attribute;
}
@Override
public AttributeMetadata getAttribute() {
return attribute;
}
}

View File

@ -0,0 +1,27 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.models.spi.ClassDetails;
/**
* @author Steve Ebersole
*/
public class CategorizationHelper {
public static boolean isMappedSuperclass(ClassDetails classDetails) {
return classDetails.getAnnotationUsage( JpaAnnotations.MAPPED_SUPERCLASS ) != null;
}
public static boolean isEntity(ClassDetails classDetails) {
return classDetails.getAnnotationUsage( JpaAnnotations.ENTITY ) != null;
}
public static boolean isIdentifiable(ClassDetails classDetails) {
return isEntity( classDetails ) || isMappedSuperclass( classDetails );
}
}

View File

@ -0,0 +1,73 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.categorize.spi.GlobalRegistrations;
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 ClassDetailsRegistry classDetailsRegistry;
private final AnnotationDescriptorRegistry annotationDescriptorRegistry;
private final Set<EntityHierarchy> entityHierarchies;
private final Map<String, ClassDetails> mappedSuperclasses;
private final Map<String, ClassDetails> embeddables;
private final GlobalRegistrations globalRegistrations;
public CategorizedDomainModelImpl(
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry annotationDescriptorRegistry,
Set<EntityHierarchy> entityHierarchies,
Map<String, ClassDetails> mappedSuperclasses,
Map<String, ClassDetails> embeddables,
GlobalRegistrations globalRegistrations) {
this.classDetailsRegistry = classDetailsRegistry;
this.annotationDescriptorRegistry = annotationDescriptorRegistry;
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 Set<EntityHierarchy> getEntityHierarchies() {
return entityHierarchies;
}
public Map<String, ClassDetails> getMappedSuperclasses() {
return mappedSuperclasses;
}
@Override
public Map<String, ClassDetails> getEmbeddables() {
return embeddables;
}
@Override
public GlobalRegistrations getGlobalRegistrations() {
return globalRegistrations;
}
}

View File

@ -0,0 +1,40 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.net.URL;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.models.spi.ClassLoading;
/**
* Adapts {@linkplain ClassLoaderService} to the {@linkplain ClassLoading} contract
*
* @author Steve Ebersole
*/
public class ClassLoaderServiceLoading implements ClassLoading {
private final ClassLoaderService classLoaderService;
public ClassLoaderServiceLoading(ClassLoaderService classLoaderService) {
this.classLoaderService = classLoaderService;
}
@Override
public <T> Class<T> classForName(String name) {
return classLoaderService.classForName( name );
}
@Override
public Package packageForName(String name) {
return classLoaderService.packageForNameOrNull( name );
}
@Override
public URL locateResource(String resourceName) {
return classLoaderService.locateResource( resourceName );
}
}

View File

@ -0,0 +1,152 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.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.spi.CategorizedDomainModel;
import org.hibernate.boot.models.categorize.spi.EntityHierarchy;
import org.hibernate.boot.models.categorize.spi.ManagedResourcesProcessor;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import jakarta.persistence.Embeddable;
import jakarta.persistence.Entity;
import jakarta.persistence.MappedSuperclass;
/**
* In-flight holder for various types of "global" registrations. Also acts as the
* {@linkplain #createResult builder} for {@linkplain CategorizedDomainModel} as returned
* by {@linkplain ManagedResourcesProcessor#processManagedResources}
*
* @author Steve Ebersole
*/
public class DomainModelCategorizationCollector {
private final boolean areIdGeneratorsGlobal;
private final Set<ClassDetails> rootEntities = new HashSet<>();
private final Map<String,ClassDetails> mappedSuperclasses = new HashMap<>();
private final Map<String,ClassDetails> embeddables = new HashMap<>();
private final GlobalRegistrationsImpl globalRegistrations;
public DomainModelCategorizationCollector(
boolean areIdGeneratorsGlobal,
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry descriptorRegistry) {
this.areIdGeneratorsGlobal = areIdGeneratorsGlobal;
this.globalRegistrations = new GlobalRegistrationsImpl( classDetailsRegistry, descriptorRegistry );
}
public Set<ClassDetails> getRootEntities() {
return rootEntities;
}
public Map<String, ClassDetails> getMappedSuperclasses() {
return mappedSuperclasses;
}
public Map<String, ClassDetails> getEmbeddables() {
return embeddables;
}
public GlobalRegistrationsImpl getGlobalRegistrations() {
return globalRegistrations;
}
public void apply(JaxbEntityMappingsImpl jaxbRoot) {
getGlobalRegistrations().collectJavaTypeRegistrations( jaxbRoot.getJavaTypeRegistrations() );
getGlobalRegistrations().collectJdbcTypeRegistrations( jaxbRoot.getJdbcTypeRegistrations() );
getGlobalRegistrations().collectConverterRegistrations( jaxbRoot.getConverterRegistrations() );
getGlobalRegistrations().collectUserTypeRegistrations( jaxbRoot.getUserTypeRegistrations() );
getGlobalRegistrations().collectCompositeUserTypeRegistrations( jaxbRoot.getCompositeUserTypeRegistrations() );
getGlobalRegistrations().collectCollectionTypeRegistrations( jaxbRoot.getCollectionUserTypeRegistrations() );
getGlobalRegistrations().collectEmbeddableInstantiatorRegistrations( jaxbRoot.getEmbeddableInstantiatorRegistrations() );
getGlobalRegistrations().collectFilterDefinitions( jaxbRoot.getFilterDefinitions() );
final JaxbPersistenceUnitMetadataImpl persistenceUnitMetadata = jaxbRoot.getPersistenceUnitMetadata();
if ( persistenceUnitMetadata != null ) {
final JaxbPersistenceUnitDefaultsImpl persistenceUnitDefaults = persistenceUnitMetadata.getPersistenceUnitDefaults();
final JaxbEntityListenerContainerImpl listenerContainer = persistenceUnitDefaults.getEntityListenerContainer();
if ( listenerContainer != null ) {
getGlobalRegistrations().collectEntityListenerRegistrations( listenerContainer.getEntityListeners() );
}
}
getGlobalRegistrations().collectIdGenerators( jaxbRoot );
// todo : named queries
// todo : named graphs
}
public void apply(ClassDetails classDetails) {
getGlobalRegistrations().collectJavaTypeRegistrations( classDetails );
getGlobalRegistrations().collectJdbcTypeRegistrations( classDetails );
getGlobalRegistrations().collectConverterRegistrations( classDetails );
getGlobalRegistrations().collectUserTypeRegistrations( classDetails );
getGlobalRegistrations().collectCompositeUserTypeRegistrations( classDetails );
getGlobalRegistrations().collectCollectionTypeRegistrations( classDetails );
getGlobalRegistrations().collectEmbeddableInstantiatorRegistrations( classDetails );
getGlobalRegistrations().collectFilterDefinitions( classDetails );
if ( areIdGeneratorsGlobal ) {
getGlobalRegistrations().collectIdGenerators( classDetails );
}
// todo : named queries
// todo : named graphs
if ( classDetails.getAnnotationUsage( MappedSuperclass.class ) != null ) {
if ( classDetails.getClassName() != null ) {
mappedSuperclasses.put( classDetails.getClassName(), classDetails );
}
}
else if ( classDetails.getAnnotationUsage( Entity.class ) != null ) {
if ( EntityHierarchyBuilder.isRoot( classDetails ) ) {
rootEntities.add( classDetails );
}
}
else if ( classDetails.getAnnotationUsage( Embeddable.class ) != null ) {
if ( classDetails.getClassName() != null ) {
embeddables.put( classDetails.getClassName(), classDetails );
}
}
// todo : converters? - @Converter / AttributeConverter, as opposed to @ConverterRegistration which is already collected
}
/**
* Builder for {@linkplain CategorizedDomainModel} based on our internal state plus
* the incoming set of managed types.
*
* @param entityHierarchies All entity hierarchies defined in the persistence-unit, built based
* on {@linkplain #getRootEntities()}
*
* @see ManagedResourcesProcessor#processManagedResources
*/
public CategorizedDomainModel createResult(
Set<EntityHierarchy> entityHierarchies,
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry annotationDescriptorRegistry) {
return new CategorizedDomainModelImpl(
classDetailsRegistry,
annotationDescriptorRegistry,
entityHierarchies,
mappedSuperclasses,
embeddables,
getGlobalRegistrations()
);
}
}

View File

@ -0,0 +1,209 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.AccessTypeDeterminationException;
import org.hibernate.boot.models.JpaAnnotations;
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.AnnotationUsage;
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;
/**
* 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;
ClassDetails current = rootEntityType;
while ( current != null ) {
// look for `@Access` on the class
final AnnotationUsage<Access> accessAnnotation = current.getAnnotationUsage( JpaAnnotations.ACCESS );
if ( accessAnnotation != null ) {
return accessAnnotation.getAttributeValue( "value" );
}
// look for `@Id` or `@EmbeddedId`
final AnnotationTarget idMember = determineIdMember( current );
if ( idMember != null ) {
switch ( idMember.getKind() ) {
case FIELD: {
return AccessType.FIELD;
}
case METHOD: {
return AccessType.PROPERTY;
}
default: {
throw new IllegalStateException( "@Id / @EmbeddedId found on target other than field or method : " + idMember );
}
}
}
current = current.getSuperType();
}
// 2.3.1 Default Access Type
// It is an error if a default access type cannot be determined and an access type is not explicitly specified
// by means of annotations or the XML descriptor.
throw new AccessTypeDeterminationException( rootEntityType );
}
private AnnotationTarget determineIdMember(ClassDetails current) {
final List<MethodDetails> methods = current.getMethods();
for ( int i = 0; i < methods.size(); i++ ) {
final MethodDetails methodDetails = methods.get( i );
if ( methodDetails.getAnnotationUsage( JpaAnnotations.ID ) != null
|| methodDetails.getAnnotationUsage( JpaAnnotations.EMBEDDED_ID ) != 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.getAnnotationUsage( JpaAnnotations.ID ) != null
|| fieldDetails.getAnnotationUsage( JpaAnnotations.EMBEDDED_ID ) != 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.getAnnotationUsage( JpaAnnotations.ENTITY ) != null
&& isRoot( managedType ) ) {
collectedTypes.add( managedType );
}
} );
return collectedTypes;
}
public static boolean isRoot(ClassDetails classInfo) {
// perform a series of opt-out checks against the super-type hierarchy
// an entity is considered a root of the hierarchy if:
// 1) it has no super-types
// 2) its super types contain no entities (MappedSuperclasses are allowed)
if ( classInfo.getSuperType() == null ) {
return true;
}
ClassDetails current = classInfo.getSuperType();
while ( current != null ) {
if ( current.getAnnotationUsage( JpaAnnotations.ENTITY ) != null ) {
// a super type has `@Entity`, cannot be root
return false;
}
current = current.getSuperType();
}
// if we hit no opt-outs we have a root
return true;
}
/**
* Used in tests
*/
public static Set<EntityHierarchy> createEntityHierarchies(ModelCategorizationContext processingContext) {
return new EntityHierarchyBuilder( processingContext ).process(
collectRootEntityTypes( processingContext.getClassDetailsRegistry() ),
EntityHierarchyBuilder::ignore
);
}
private static void ignore(IdentifiableTypeMetadata it) {}
}

View File

@ -0,0 +1,286 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.OptimisticLocking;
import org.hibernate.boot.models.JpaAnnotations;
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.AnnotationUsage;
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 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 modelBuildingContext) {
final ClassDetails absoluteRootClassDetails = findRootRoot( rootEntityClassDetails );
final HierarchyMetadataCollector metadataCollector = new HierarchyMetadataCollector( this, rootEntityClassDetails, typeConsumer );
if ( CategorizationHelper.isEntity( absoluteRootClassDetails ) ) {
this.absoluteRootTypeMetadata = new EntityTypeMetadataImpl(
absoluteRootClassDetails,
this,
defaultAccessType,
metadataCollector,
modelBuildingContext
);
}
else {
assert CategorizationHelper.isMappedSuperclass( absoluteRootClassDetails );
this.absoluteRootTypeMetadata = new MappedSuperclassTypeMetadataImpl(
absoluteRootClassDetails,
this,
defaultAccessType,
metadataCollector,
modelBuildingContext
);
}
this.rootEntityTypeMetadata = metadataCollector.getRootEntityMetadata();
assert rootEntityTypeMetadata != null;
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 ClassDetails findRootRoot(ClassDetails rootEntityClassDetails) {
if ( rootEntityClassDetails.getSuperType() != null ) {
final ClassDetails match = walkSupers( rootEntityClassDetails.getSuperType() );
if ( match != null ) {
return match;
}
}
return rootEntityClassDetails;
}
private ClassDetails walkSupers(ClassDetails type) {
assert type != null;
if ( type.getSuperType() != null ) {
final ClassDetails match = walkSupers( type.getSuperType() );
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 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 AnnotationUsage<Inheritance> inheritanceAnnotation = metadataCollector.getInheritanceAnnotation();
if ( inheritanceAnnotation != null ) {
return inheritanceAnnotation.getAttributeValue( "strategy" );
}
return InheritanceType.SINGLE_TABLE;
}
private OptimisticLockStyle determineOptimisticLockStyle(HierarchyMetadataCollector metadataCollector) {
final AnnotationUsage<OptimisticLocking> optimisticLockingAnnotation = metadataCollector.getOptimisticLockingAnnotation();
if ( optimisticLockingAnnotation != null ) {
optimisticLockingAnnotation.getEnum( "type", DEFAULT_LOCKING_STRATEGY );
}
return DEFAULT_LOCKING_STRATEGY;
}
private CacheRegion determineCacheRegion(
HierarchyMetadataCollector metadataCollector,
AccessType defaultCacheAccessType) {
final AnnotationUsage<Cache> cacheAnnotation = metadataCollector.getCacheAnnotation();
return new CacheRegion( cacheAnnotation, defaultCacheAccessType, rootEntityTypeMetadata.getEntityName() );
}
private NaturalIdCacheRegion determineNaturalIdCacheRegion(
HierarchyMetadataCollector metadataCollector,
CacheRegion cacheRegion) {
final AnnotationUsage<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 AnnotationUsage<Inheritance> localAnnotation = managedClass.getAnnotationUsage( JpaAnnotations.INHERITANCE );
if ( localAnnotation == null ) {
return null;
}
return localAnnotation.getAttributeValue( "strategy" );
}
private void ensureNoInheritanceAnnotationsOnSubclasses(IdentifiableTypeMetadata type) {
type.forEachSubType( (subType) -> {
if ( getLocallyDefinedInheritanceType( subType.getClassDetails() ) != null ) {
ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER.debugf(
"@javax.persistence.Inheritance was specified on non-root entity [%s]; ignoring...",
type.getClassDetails().getName()
);
}
ensureNoInheritanceAnnotationsOnSubclasses( subType );
} );
}
}

View File

@ -0,0 +1,421 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.List;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Immutable;
import org.hibernate.annotations.Proxy;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.SQLUpdate;
import org.hibernate.annotations.SelectBeforeUpdate;
import org.hibernate.annotations.Synchronize;
import org.hibernate.boot.model.CustomSql;
import org.hibernate.boot.model.naming.EntityNaming;
import org.hibernate.boot.models.JpaAnnotations;
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.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.models.spi.AnnotationUsage;
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 isSelectBeforeUpdate;
private final boolean isDynamicInsert;
private final boolean isDynamicUpdate;
private final CustomSql customInsert;
private final CustomSql customUpdate;
private final CustomSql customDelete;
private final String[] synchronizedTableNames;
private List<JpaEventListener> hierarchyEventListeners;
private List<JpaEventListener> completeEventListeners;
public EntityTypeMetadataImpl(
ClassDetails classDetails,
EntityHierarchy hierarchy,
AccessType defaultAccessType,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext modelContext) {
super( classDetails, hierarchy, 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 AnnotationUsage<Entity> entityAnnotation = classDetails.getAnnotationUsage( JpaAnnotations.ENTITY );
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.isSelectBeforeUpdate = decodeSelectBeforeUpdate();
this.isDynamicInsert = decodeDynamicInsert();
this.isDynamicUpdate = decodeDynamicUpdate();
this.customInsert = extractCustomSql( classDetails.getAnnotationUsage( SQLInsert.class ) );
this.customUpdate = extractCustomSql( classDetails.getAnnotationUsage( SQLUpdate.class ) );
this.customDelete = extractCustomSql( classDetails.getAnnotationUsage( SQLDelete.class ) );
//noinspection deprecation
final AnnotationUsage<Proxy> proxyAnnotation = classDetails.getAnnotationUsage( Proxy.class );
if ( proxyAnnotation != null ) {
final Boolean lazyValue = proxyAnnotation.getAttributeValue( "lazy" );
this.isLazy = lazyValue == null || lazyValue;
if ( this.isLazy ) {
final ClassDetails proxyClassDetails = proxyAnnotation.getAttributeValue( "proxyClass" );
if ( proxyClassDetails != null ) {
this.proxy = proxyClassDetails.getName();
}
else {
this.proxy = null;
}
}
else {
this.proxy = null;
}
}
else {
// defaults are that it is lazy and that the class itself is the proxy class
this.isLazy = true;
this.proxy = getEntityName();
}
final AnnotationUsage<DiscriminatorValue> discriminatorValueAnn = classDetails.getAnnotationUsage( DiscriminatorValue.class );
if ( discriminatorValueAnn != null ) {
this.discriminatorMatchValue = discriminatorValueAnn.getAttributeValue( "value" );
}
else {
this.discriminatorMatchValue = null;
}
postInstantiate( typeConsumer );
}
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 AnnotationUsage<Entity> entityAnnotation = classDetails.getAnnotationUsage( JpaAnnotations.ENTITY );
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.isSelectBeforeUpdate = decodeSelectBeforeUpdate();
this.isDynamicInsert = decodeDynamicInsert();
this.isDynamicUpdate = decodeDynamicUpdate();
this.customInsert = extractCustomSql( classDetails.getAnnotationUsage( SQLInsert.class ) );
this.customUpdate = extractCustomSql( classDetails.getAnnotationUsage( SQLUpdate.class ) );
this.customDelete = extractCustomSql( classDetails.getAnnotationUsage( SQLDelete.class ) );
//noinspection deprecation
final AnnotationUsage<Proxy> proxyAnnotation = classDetails.getAnnotationUsage( Proxy.class );
if ( proxyAnnotation != null ) {
final Boolean lazyValue = proxyAnnotation.getAttributeValue( "lazy" );
this.isLazy = lazyValue == null || lazyValue;
if ( this.isLazy ) {
final ClassDetails proxyClassDetails = proxyAnnotation.getAttributeValue( "proxyClass" );
if ( proxyClassDetails != null ) {
this.proxy = proxyClassDetails.getName();
}
else {
this.proxy = null;
}
}
else {
this.proxy = null;
}
}
else {
// defaults are that it is lazy and that the class itself is the proxy class
this.isLazy = true;
this.proxy = getEntityName();
}
final AnnotationUsage<DiscriminatorValue> discriminatorValueAnn = classDetails.getAnnotationUsage( DiscriminatorValue.class );
if ( discriminatorValueAnn != null ) {
this.discriminatorMatchValue = discriminatorValueAnn.getAttributeValue( "value" );
}
else {
this.discriminatorMatchValue = null;
}
postInstantiate( 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 isSelectBeforeUpdate() {
return isSelectBeforeUpdate;
}
@Override
public boolean isDynamicInsert() {
return isDynamicInsert;
}
@Override
public boolean isDynamicUpdate() {
return isDynamicUpdate;
}
@Override
public CustomSql getCustomInsert() {
return customInsert;
}
@Override
public CustomSql getCustomUpdate() {
return customUpdate;
}
@Override
public CustomSql getCustomDelete() {
return customDelete;
}
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(AnnotationUsage<Entity> entityAnnotation, String entityName) {
final String name = entityAnnotation.getAttributeValue( "name" );
if ( isNotEmpty( name ) ) {
return name;
}
return unqualify( entityName );
}
private boolean determineMutability(ClassDetails classDetails, ModelCategorizationContext modelContext) {
final AnnotationUsage<Immutable> immutableAnn = classDetails.getAnnotationUsage( Immutable.class );
return immutableAnn == null;
}
private boolean determineCacheability(
ClassDetails classDetails,
ModelCategorizationContext modelContext) {
final AnnotationUsage<Cacheable> cacheableAnn = classDetails.getAnnotationUsage( 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.getBoolean( "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.getBoolean( "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 CustomSql extractCustomSql(AnnotationUsage<?> customSqlAnnotation) {
if ( customSqlAnnotation == null ) {
return null;
}
final String sql = customSqlAnnotation.getAttributeValue( "sql" );
final boolean isCallable = customSqlAnnotation.getAttributeValue( "callable" );
final ResultCheckStyle checkValue = customSqlAnnotation.getAttributeValue( "check" );
final ExecuteUpdateResultCheckStyle checkStyle;
if ( checkValue == null ) {
checkStyle = isCallable
? ExecuteUpdateResultCheckStyle.NONE
: ExecuteUpdateResultCheckStyle.COUNT;
}
else {
checkStyle = ExecuteUpdateResultCheckStyle.fromResultCheckStyle( checkValue );
}
return new CustomSql( sql, isCallable, checkStyle );
}
private String[] determineSynchronizedTableNames() {
final AnnotationUsage<Synchronize> synchronizeAnnotation = getClassDetails().getAnnotationUsage( Synchronize.class );
if ( synchronizeAnnotation != null ) {
return synchronizeAnnotation.<String>getList( "value" ).toArray( new String[0] );
}
return EMPTY_STRINGS;
}
private int determineBatchSize() {
final AnnotationUsage<BatchSize> batchSizeAnnotation = getClassDetails().getAnnotationUsage( BatchSize.class );
if ( batchSizeAnnotation != null ) {
return batchSizeAnnotation.getAttributeValue( "size" );
}
return -1;
}
private boolean decodeSelectBeforeUpdate() {
//noinspection deprecation
final AnnotationUsage<SelectBeforeUpdate> selectBeforeUpdateAnnotation = getClassDetails().getAnnotationUsage( SelectBeforeUpdate.class );
if ( selectBeforeUpdateAnnotation == null ) {
return false;
}
return selectBeforeUpdateAnnotation.getBoolean( "value" );
}
private boolean decodeDynamicInsert() {
final AnnotationUsage<DynamicInsert> dynamicInsertAnnotation = getClassDetails().getAnnotationUsage( DynamicInsert.class );
if ( dynamicInsertAnnotation == null ) {
return false;
}
return dynamicInsertAnnotation.getBoolean( "value" );
}
private boolean decodeDynamicUpdate() {
final AnnotationUsage<DynamicUpdate> dynamicUpdateAnnotation = getClassDetails().getAnnotationUsage( DynamicUpdate.class );
if ( dynamicUpdateAnnotation == null ) {
return false;
}
return dynamicUpdateAnnotation.getBoolean( "value" );
}
}

View File

@ -0,0 +1,633 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.Parameter;
import org.hibernate.boot.jaxb.mapping.spi.JaxbCollectionUserTypeRegistrationImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbCompositeUserTypeRegistrationImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbConfigurationParameterImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbConverterRegistrationImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddableInstantiatorRegistrationImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbFilterDefImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbGenericIdGeneratorImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbJavaTypeRegistrationImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbJdbcTypeRegistrationImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbSequenceGeneratorImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbTableGeneratorImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbUserTypeRegistrationImpl;
import org.hibernate.boot.models.categorize.spi.CollectionTypeRegistration;
import org.hibernate.boot.models.categorize.spi.CompositeUserTypeRegistration;
import org.hibernate.boot.models.categorize.spi.ConversionRegistration;
import org.hibernate.boot.models.categorize.spi.EmbeddableInstantiatorRegistration;
import org.hibernate.boot.models.categorize.spi.FilterDefRegistration;
import org.hibernate.boot.models.categorize.spi.GenericGeneratorRegistration;
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
import org.hibernate.boot.models.categorize.spi.JavaTypeRegistration;
import org.hibernate.boot.models.categorize.spi.JdbcTypeRegistration;
import org.hibernate.boot.models.categorize.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.categorize.spi.SequenceGeneratorRegistration;
import org.hibernate.boot.models.categorize.spi.TableGeneratorRegistration;
import org.hibernate.boot.models.categorize.spi.UserTypeRegistration;
import org.hibernate.boot.models.categorize.xml.internal.XmlAnnotationHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.models.internal.MutableAnnotationUsage;
import org.hibernate.models.internal.dynamic.DynamicAnnotationUsage;
import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.SourceModelContext;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static org.hibernate.boot.models.HibernateAnnotations.COLLECTION_TYPE_REG;
import static org.hibernate.boot.models.HibernateAnnotations.COMPOSITE_TYPE_REG;
import static org.hibernate.boot.models.HibernateAnnotations.CONVERTER_REG;
import static org.hibernate.boot.models.HibernateAnnotations.EMBEDDABLE_INSTANTIATOR_REG;
import static org.hibernate.boot.models.HibernateAnnotations.FILTER_DEF;
import static org.hibernate.boot.models.HibernateAnnotations.JAVA_TYPE_REG;
import static org.hibernate.boot.models.HibernateAnnotations.JDBC_TYPE_REG;
import static org.hibernate.boot.models.HibernateAnnotations.TYPE_REG;
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
/**
* @author Steve Ebersole
*/
public class GlobalRegistrationsImpl implements GlobalRegistrations {
private final ClassDetailsRegistry classDetailsRegistry;
private final AnnotationDescriptorRegistry descriptorRegistry;
private List<JpaEventListener> jpaEventListeners;
private List<ConversionRegistration> converterRegistrations;
private List<JavaTypeRegistration> javaTypeRegistrations;
private List<JdbcTypeRegistration> jdbcTypeRegistrations;
private List<UserTypeRegistration> userTypeRegistrations;
private List<CompositeUserTypeRegistration> compositeUserTypeRegistrations;
private List<CollectionTypeRegistration> collectionTypeRegistrations;
private List<EmbeddableInstantiatorRegistration> embeddableInstantiatorRegistrations;
private Map<String, FilterDefRegistration> filterDefRegistrations;
private Map<String, SequenceGeneratorRegistration> sequenceGeneratorRegistrations;
private Map<String, TableGeneratorRegistration> tableGeneratorRegistrations;
private Map<String, GenericGeneratorRegistration> genericGeneratorRegistrations;
public GlobalRegistrationsImpl(SourceModelContext sourceModelContext) {
this( sourceModelContext.getClassDetailsRegistry(), sourceModelContext.getAnnotationDescriptorRegistry() );
}
public GlobalRegistrationsImpl(ClassDetailsRegistry classDetailsRegistry, AnnotationDescriptorRegistry descriptorRegistry) {
this.classDetailsRegistry = classDetailsRegistry;
this.descriptorRegistry = descriptorRegistry;
}
@Override
public List<JpaEventListener> getEntityListenerRegistrations() {
return jpaEventListeners == null ? emptyList() : jpaEventListeners;
}
@Override
public List<ConversionRegistration> getConverterRegistrations() {
return converterRegistrations == null ? emptyList() : converterRegistrations;
}
@Override
public List<JavaTypeRegistration> getJavaTypeRegistrations() {
return javaTypeRegistrations == null ? emptyList() : javaTypeRegistrations;
}
@Override
public List<JdbcTypeRegistration> getJdbcTypeRegistrations() {
return jdbcTypeRegistrations == null ? emptyList() : jdbcTypeRegistrations;
}
@Override
public List<UserTypeRegistration> getUserTypeRegistrations() {
return userTypeRegistrations == null ? emptyList() : userTypeRegistrations;
}
@Override
public List<CompositeUserTypeRegistration> getCompositeUserTypeRegistrations() {
return compositeUserTypeRegistrations == null ? emptyList() : compositeUserTypeRegistrations;
}
@Override
public List<CollectionTypeRegistration> getCollectionTypeRegistrations() {
return collectionTypeRegistrations == null ? emptyList() : collectionTypeRegistrations;
}
@Override
public List<EmbeddableInstantiatorRegistration> getEmbeddableInstantiatorRegistrations() {
return embeddableInstantiatorRegistrations == null ? emptyList() : embeddableInstantiatorRegistrations;
}
@Override
public Map<String, FilterDefRegistration> getFilterDefRegistrations() {
return filterDefRegistrations == null ? emptyMap() : filterDefRegistrations;
}
@Override
public Map<String, SequenceGeneratorRegistration> getSequenceGeneratorRegistrations() {
return sequenceGeneratorRegistrations == null ? emptyMap() : sequenceGeneratorRegistrations;
}
@Override
public Map<String, TableGeneratorRegistration> getTableGeneratorRegistrations() {
return tableGeneratorRegistrations == null ? emptyMap() : tableGeneratorRegistrations;
}
@Override
public Map<String, GenericGeneratorRegistration> getGenericGeneratorRegistrations() {
return genericGeneratorRegistrations == null ? emptyMap() : genericGeneratorRegistrations;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JavaTypeRegistration
public void collectJavaTypeRegistrations(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( JAVA_TYPE_REG, (usage) -> collectJavaTypeRegistration(
usage.getAttributeValue( "javaType" ),
usage.getAttributeValue( "descriptorClass" )
) );
}
public void collectJavaTypeRegistrations(List<JaxbJavaTypeRegistrationImpl> registrations) {
if ( CollectionHelper.isEmpty( registrations ) ) {
return;
}
registrations.forEach( (reg) -> collectJavaTypeRegistration(
classDetailsRegistry.resolveClassDetails( reg.getClazz() ),
classDetailsRegistry.resolveClassDetails( reg.getDescriptor() )
) );
}
public void collectJavaTypeRegistration(ClassDetails javaType, ClassDetails descriptor) {
collectJavaTypeRegistration( new JavaTypeRegistration( javaType, descriptor ) );
}
public void collectJavaTypeRegistration(JavaTypeRegistration registration) {
if ( javaTypeRegistrations == null ) {
javaTypeRegistrations = new ArrayList<>();
}
javaTypeRegistrations.add( registration );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JdbcTypeRegistration
public void collectJdbcTypeRegistrations(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( JDBC_TYPE_REG, (usage) -> collectJdbcTypeRegistration(
usage.getAttributeValue( "registrationCode" ),
usage.getAttributeValue( "value" )
) );
}
public void collectJdbcTypeRegistrations(List<JaxbJdbcTypeRegistrationImpl> registrations) {
if ( CollectionHelper.isEmpty( registrations ) ) {
return;
}
registrations.forEach( (reg) -> collectJdbcTypeRegistration(
reg.getCode(),
classDetailsRegistry.resolveClassDetails( reg.getDescriptor() )
) );
}
public void collectJdbcTypeRegistration(Integer registrationCode, ClassDetails descriptor) {
if ( jdbcTypeRegistrations == null ) {
jdbcTypeRegistrations = new ArrayList<>();
}
jdbcTypeRegistrations.add( new JdbcTypeRegistration( registrationCode, descriptor ) );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ConversionRegistration
public void collectConverterRegistrations(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( CONVERTER_REG, (usage) -> {
final ClassDetails domainType = usage.getAttributeValue( "domainType" );
final ClassDetails converterType = usage.getAttributeValue( "converter" );
final boolean autoApply = usage.getAttributeValue( "autoApply" );
collectConverterRegistration( new ConversionRegistration( domainType, converterType, autoApply, CONVERTER_REG ) );
} );
}
public void collectConverterRegistrations(List<JaxbConverterRegistrationImpl> registrations) {
if ( CollectionHelper.isEmpty( registrations ) ) {
return;
}
registrations.forEach( (registration) -> {
final ClassDetails explicitDomainType;
final String explicitDomainTypeName = registration.getClazz();
if ( StringHelper.isNotEmpty( explicitDomainTypeName ) ) {
explicitDomainType = classDetailsRegistry.resolveClassDetails( explicitDomainTypeName );
}
else {
explicitDomainType = null;
}
final ClassDetails converterType = classDetailsRegistry.resolveClassDetails( registration.getConverter() );
final boolean autoApply = registration.isAutoApply();
collectConverterRegistration( new ConversionRegistration( explicitDomainType, converterType, autoApply, CONVERTER_REG ) );
} );
}
public void collectConverterRegistration(ConversionRegistration conversion) {
if ( converterRegistrations == null ) {
converterRegistrations = new ArrayList<>();
}
converterRegistrations.add( conversion );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// UserTypeRegistration
public void collectUserTypeRegistrations(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( TYPE_REG, (usage) -> collectUserTypeRegistration(
usage.getAttributeValue( "basicClass" ),
usage.getAttributeValue( "userType" )
) );
}
public void collectUserTypeRegistrations(List<JaxbUserTypeRegistrationImpl> registrations) {
if ( CollectionHelper.isEmpty( registrations ) ) {
return;
}
registrations.forEach( (reg) -> {
final ClassDetails domainTypeDetails = classDetailsRegistry.resolveClassDetails( reg.getClazz() );
final ClassDetails descriptorDetails = classDetailsRegistry.resolveClassDetails( reg.getDescriptor() );
collectUserTypeRegistration( domainTypeDetails, descriptorDetails );
} );
}
public void collectUserTypeRegistration(ClassDetails domainClass, ClassDetails userTypeClass) {
if ( userTypeRegistrations == null ) {
userTypeRegistrations = new ArrayList<>();
}
userTypeRegistrations.add( new UserTypeRegistration( domainClass, userTypeClass ) );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CompositeUserTypeRegistration
public void collectCompositeUserTypeRegistrations(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( COMPOSITE_TYPE_REG, (usage) -> collectCompositeUserTypeRegistration(
usage.getAttributeValue( "embeddableClass" ),
usage.getAttributeValue( "userType" )
) );
}
public void collectCompositeUserTypeRegistrations(List<JaxbCompositeUserTypeRegistrationImpl> registrations) {
if ( CollectionHelper.isEmpty( registrations ) ) {
return;
}
registrations.forEach( (reg) -> collectCompositeUserTypeRegistration(
classDetailsRegistry.resolveClassDetails( reg.getClazz() ),
classDetailsRegistry.resolveClassDetails( reg.getDescriptor() )
) );
}
public void collectCompositeUserTypeRegistration(ClassDetails domainClass, ClassDetails userTypeClass) {
if ( compositeUserTypeRegistrations == null ) {
compositeUserTypeRegistrations = new ArrayList<>();
}
compositeUserTypeRegistrations.add( new CompositeUserTypeRegistration( domainClass, userTypeClass ) );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CollectionTypeRegistration
public void collectCollectionTypeRegistrations(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( COLLECTION_TYPE_REG, (usage) -> collectCollectionTypeRegistration(
usage.getAttributeValue( "classification" ),
usage.getAttributeValue( "type" ),
extractParameterMap( usage )
) );
}
private Map<String,String> extractParameterMap(AnnotationUsage<? extends Annotation> source) {
final List<AnnotationUsage<Parameter>> parameters = source.getAttributeValue( "parameters" );
final Map<String,String> result = new HashMap<>();
for ( AnnotationUsage<Parameter> parameter : parameters ) {
result.put(
parameter.getAttributeValue( "name" ),
parameter.getAttributeValue( "value" )
);
}
return result;
}
public void collectCollectionTypeRegistrations(List<JaxbCollectionUserTypeRegistrationImpl> registrations) {
if ( CollectionHelper.isEmpty( registrations ) ) {
return;
}
registrations.forEach( (reg) -> collectCollectionTypeRegistration(
reg.getClassification(),
classDetailsRegistry.resolveClassDetails( reg.getDescriptor() ),
extractParameterMap( reg.getParameters() )
) );
}
private Map<String, String> extractParameterMap(List<JaxbConfigurationParameterImpl> parameters) {
if ( CollectionHelper.isEmpty( parameters ) ) {
return Collections.emptyMap();
}
final Map<String,String> result = new HashMap<>();
parameters.forEach( parameter -> result.put( parameter.getName(), parameter.getValue() ) );
return result;
}
public void collectCollectionTypeRegistration(
CollectionClassification classification,
ClassDetails userTypeClass,
Map<String,String> parameters) {
if ( collectionTypeRegistrations == null ) {
collectionTypeRegistrations = new ArrayList<>();
}
collectionTypeRegistrations.add( new CollectionTypeRegistration( classification, userTypeClass, parameters ) );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// EmbeddableInstantiatorRegistration
public void collectEmbeddableInstantiatorRegistrations(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( EMBEDDABLE_INSTANTIATOR_REG, (usage) -> collectEmbeddableInstantiatorRegistration(
usage.getAttributeValue( "embeddableClass" ),
usage.getAttributeValue( "instantiator" )
) );
}
public void collectEmbeddableInstantiatorRegistrations(List<JaxbEmbeddableInstantiatorRegistrationImpl> registrations) {
if ( CollectionHelper.isEmpty( registrations ) ) {
return;
}
registrations.forEach( (reg) -> collectEmbeddableInstantiatorRegistration(
classDetailsRegistry.resolveClassDetails( reg.getEmbeddableClass() ),
classDetailsRegistry.resolveClassDetails( reg.getInstantiator() )
) );
}
public void collectEmbeddableInstantiatorRegistration(ClassDetails embeddableClass, ClassDetails instantiator) {
if ( embeddableInstantiatorRegistrations == null ) {
embeddableInstantiatorRegistrations = new ArrayList<>();
}
embeddableInstantiatorRegistrations.add( new EmbeddableInstantiatorRegistration( embeddableClass, instantiator ) );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Filter-defs
public void collectFilterDefinitions(AnnotationTarget annotationTarget) {
annotationTarget.forEachAnnotationUsage( FILTER_DEF, (usage) -> collectFilterDefinition(
usage.getAttributeValue( "name" ),
usage.getAttributeValue( "defaultCondition" ),
extractFilterParameters( usage )
) );
}
private Map<String, ClassDetails> extractFilterParameters(AnnotationUsage<FilterDef> source) {
final List<AnnotationUsage<ParamDef>> parameters = source.getAttributeValue( "parameters" );
if ( isEmpty( parameters ) ) {
return null;
}
final Map<String, ClassDetails> result = new HashMap<>( parameters.size() );
for ( AnnotationUsage<ParamDef> parameter : parameters ) {
result.put( parameter.getAttributeValue( "name" ), parameter.getAttributeValue( "type" ) );
}
return result;
}
public void collectFilterDefinitions(List<JaxbFilterDefImpl> filterDefinitions) {
if ( CollectionHelper.isEmpty( filterDefinitions ) ) {
return;
}
filterDefinitions.forEach( (filterDefinition) -> collectFilterDefinition(
filterDefinition.getName(),
filterDefinition.getDefaultCondition(),
extractFilterParameters( filterDefinition )
) );
}
private Map<String, ClassDetails> extractFilterParameters(JaxbFilterDefImpl source) {
final List<JaxbFilterDefImpl.JaxbFilterParamImpl> parameters = source.getFilterParams();
if ( isEmpty( parameters ) ) {
return null;
}
final Map<String, ClassDetails> result = new HashMap<>( parameters.size() );
for ( JaxbFilterDefImpl.JaxbFilterParamImpl parameter : parameters ) {
// for now, don't check whether nothing was specified; this situation
// should resolve to Object - let's see how that reacts
final ClassDetails targetClassDetails = XmlAnnotationHelper.resolveJavaType(
parameter.getType(),
classDetailsRegistry
);
result.put( parameter.getName(), targetClassDetails );
}
return result;
}
public void collectFilterDefinition(String name, String defaultCondition, Map<String, ClassDetails> parameters) {
if ( filterDefRegistrations == null ) {
filterDefRegistrations = new HashMap<>();
}
if ( filterDefRegistrations.put( name, new FilterDefRegistration( name, defaultCondition, parameters ) ) != null ) {
throw new AnnotationException( "Multiple '@FilterDef' annotations define a filter named '" + name + "'" );
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// EntityListenerRegistration
public void collectEntityListenerRegistrations(List<JaxbEntityListenerImpl> listeners) {
if ( CollectionHelper.isEmpty( listeners ) ) {
return;
}
listeners.forEach( (jaxbEntityListener) -> {
final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails( jaxbEntityListener.getClazz() );
final JpaEventListener listener = JpaEventListener.from(
JpaEventListenerStyle.LISTENER,
classDetails,
jaxbEntityListener
);
addJpaEventListener( listener );
} );
}
public void addJpaEventListener(JpaEventListener listener) {
if ( jpaEventListeners == null ) {
jpaEventListeners = new ArrayList<>();
}
jpaEventListeners.add( listener );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Id generators
public void collectIdGenerators(JaxbEntityMappingsImpl jaxbRoot) {
collectSequenceGenerators( jaxbRoot.getSequenceGenerators() );
collectTableGenerators( jaxbRoot.getTableGenerators() );
collectGenericGenerators( jaxbRoot.getGenericGenerators() );
// todo : add support for @IdGeneratorType in mapping.xsd?
}
public void collectIdGenerators(ClassDetails classDetails) {
classDetails.forEachAnnotationUsage( SequenceGenerator.class, this::collectSequenceGenerator );
classDetails.forEachAnnotationUsage( TableGenerator.class, this::collectTableGenerator );
classDetails.forEachAnnotationUsage( GenericGenerator.class, this::collectGenericGenerator );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Sequence generator
public void collectSequenceGenerators(List<JaxbSequenceGeneratorImpl> sequenceGenerators) {
if ( CollectionHelper.isEmpty( sequenceGenerators ) ) {
return;
}
sequenceGenerators.forEach( (generator) -> {
final MutableAnnotationUsage<SequenceGenerator> annotationUsage = makeAnnotation( SequenceGenerator.class );
annotationUsage.setAttributeValue( "name", generator.getName() );
annotationUsage.setAttributeValue( "sequenceName", generator.getSequenceName() );
annotationUsage.setAttributeValue( "catalog", generator.getCatalog() );
annotationUsage.setAttributeValue( "schema", generator.getSchema() );
annotationUsage.setAttributeValue( "initialValue", generator.getInitialValue() );
annotationUsage.setAttributeValue( "allocationSize", generator.getAllocationSize() );
collectSequenceGenerator( new SequenceGeneratorRegistration( generator.getName(), annotationUsage ) );
} );
}
private <A extends Annotation> MutableAnnotationUsage<A> makeAnnotation(Class<A> annotationType) {
final AnnotationDescriptor<A> descriptor = descriptorRegistry.getDescriptor( annotationType );
return new DynamicAnnotationUsage<>( descriptor );
}
public void collectSequenceGenerator(AnnotationUsage<SequenceGenerator> usage) {
collectSequenceGenerator( new SequenceGeneratorRegistration( usage.getAttributeValue( "name" ), usage ) );
}
public void collectSequenceGenerator(SequenceGeneratorRegistration generatorRegistration) {
if ( sequenceGeneratorRegistrations == null ) {
sequenceGeneratorRegistrations = new HashMap<>();
}
sequenceGeneratorRegistrations.put( generatorRegistration.getName(), generatorRegistration );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Table generator
public void collectTableGenerators(List<JaxbTableGeneratorImpl> tableGenerators) {
if ( CollectionHelper.isEmpty( tableGenerators ) ) {
return;
}
tableGenerators.forEach( (generator) -> {
final MutableAnnotationUsage<TableGenerator> annotationUsage = makeAnnotation( TableGenerator.class );
annotationUsage.setAttributeValue( "name", generator.getName() );
annotationUsage.setAttributeValue( "table", generator.getTable() );
annotationUsage.setAttributeValue( "catalog", generator.getCatalog() );
annotationUsage.setAttributeValue( "schema", generator.getSchema() );
annotationUsage.setAttributeValue( "pkColumnName", generator.getPkColumnName() );
annotationUsage.setAttributeValue( "valueColumnName", generator.getValueColumnName() );
annotationUsage.setAttributeValue( "pkColumnValue", generator.getPkColumnValue() );
annotationUsage.setAttributeValue( "initialValue", generator.getInitialValue() );
annotationUsage.setAttributeValue( "allocationSize", generator.getAllocationSize() );
collectTableGenerator( new TableGeneratorRegistration( generator.getName(), annotationUsage ) );
} );
}
public void collectTableGenerator(AnnotationUsage<TableGenerator> usage) {
collectTableGenerator( new TableGeneratorRegistration( usage.getAttributeValue( "name" ), usage ) );
}
public void collectTableGenerator(TableGeneratorRegistration generatorRegistration) {
if ( tableGeneratorRegistrations == null ) {
tableGeneratorRegistrations = new HashMap<>();
}
tableGeneratorRegistrations.put( generatorRegistration.getName(), generatorRegistration );
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Generic generators
private void collectGenericGenerators(List<JaxbGenericIdGeneratorImpl> genericGenerators) {
if ( CollectionHelper.isEmpty( genericGenerators ) ) {
return;
}
genericGenerators.forEach( (generator) -> {
final MutableAnnotationUsage<GenericGenerator> annotationUsage = makeAnnotation( GenericGenerator.class );
annotationUsage.setAttributeValue( "name", generator.getName() );
annotationUsage.setAttributeValue( "strategy", generator.getClazz() );
// todo : update the mapping.xsd to account for new @GenericGenerator definition
collectGenericGenerator( new GenericGeneratorRegistration( generator.getName(), annotationUsage ) );
} );
}
public void collectGenericGenerator(AnnotationUsage<GenericGenerator> usage) {
collectGenericGenerator( new GenericGeneratorRegistration( usage.getAttributeValue( "name" ), usage ) );
}
public void collectGenericGenerator(GenericGeneratorRegistration generatorRegistration) {
if ( genericGeneratorRegistrations == null ) {
genericGeneratorRegistrations = new HashMap<>();
}
genericGeneratorRegistrations.put( generatorRegistration.getName(), generatorRegistration );
}
}

View File

@ -0,0 +1,292 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
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.models.ModelsException;
import org.hibernate.models.spi.AnnotationUsage;
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;
import static org.hibernate.boot.models.categorize.ModelCategorizationLogging.MODEL_CATEGORIZATION_LOGGER;
/**
* 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 boolean belowRootEntity;
private EntityTypeMetadata rootEntityMetadata;
private AnnotationUsage<Inheritance> inheritanceAnnotation;
private AnnotationUsage<OptimisticLocking> optimisticLockingAnnotation;
private AnnotationUsage<Cache> cacheAnnotation;
private AnnotationUsage<NaturalIdCache> naturalIdCacheAnnotation;
private KeyMapping idMapping;
private AttributeMetadata versionAttribute;
private AttributeMetadata tenantIdAttribute;
private AnnotationUsage<IdClass> idClassAnnotation;
private Object collectedIdAttributes;
private Object collectedNaturalIdAttributes;
public HierarchyMetadataCollector(
EntityHierarchy entityHierarchy,
ClassDetails rootEntityClassDetails,
HierarchyTypeConsumer delegateConsumer) {
this.entityHierarchy = entityHierarchy;
this.rootEntityClassDetails = rootEntityClassDetails;
this.delegateConsumer = delegateConsumer;
}
public EntityTypeMetadata getRootEntityMetadata() {
return rootEntityMetadata;
}
public KeyMapping getIdMapping() {
if ( idMapping == null ) {
idMapping = buildIdMapping();
}
return idMapping;
}
public AnnotationUsage<Inheritance> getInheritanceAnnotation() {
return inheritanceAnnotation;
}
public AttributeMetadata getVersionAttribute() {
return versionAttribute;
}
public AttributeMetadata getTenantIdAttribute() {
return tenantIdAttribute;
}
public AnnotationUsage<OptimisticLocking> getOptimisticLockingAnnotation() {
return optimisticLockingAnnotation;
}
public AnnotationUsage<Cache> getCacheAnnotation() {
return cacheAnnotation;
}
public AnnotationUsage<NaturalIdCache> getNaturalIdCacheAnnotation() {
return naturalIdCacheAnnotation;
}
private KeyMapping buildIdMapping() {
if ( collectedIdAttributes instanceof List ) {
//noinspection unchecked
final List<AttributeMetadata> idAttributes = (List<AttributeMetadata>) collectedIdAttributes;
final ClassDetails idClassDetails;
if ( idClassAnnotation == null ) {
idClassDetails = null;
}
else {
idClassDetails = idClassAnnotation.getAttributeValue( "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 );
}
throw new ModelsException(
String.format(
Locale.ROOT,
"Unexpected attribute nature [%s] - %s",
idAttribute.getNature(),
entityHierarchy.getRoot().getEntityName()
)
);
}
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 );
}
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 AnnotationUsage<EmbeddedId> eIdAnn = attributeMember.getAnnotationUsage( EmbeddedId.class );
if ( eIdAnn != null ) {
collectIdAttribute( attributeMetadata );
}
final AnnotationUsage<Id> idAnn = attributeMember.getAnnotationUsage( Id.class );
if ( idAnn != null ) {
collectIdAttribute( attributeMetadata );
}
}
if ( attributeMember.getAnnotationUsage( NaturalId.class ) != null ) {
collectNaturalIdAttribute( attributeMetadata );
}
if ( versionAttribute == null ) {
if ( attributeMember.getAnnotationUsage( Version.class ) != null ) {
versionAttribute = attributeMetadata;
}
}
if ( tenantIdAttribute == null ) {
if ( attributeMember.getAnnotationUsage( TenantId.class ) != null ) {
tenantIdAttribute = attributeMetadata;
}
}
} );
}
}
private <A extends Annotation> AnnotationUsage<A> applyLocalAnnotation(Class<A> annotationType, ClassDetails classDetails, AnnotationUsage<A> currentValue) {
final AnnotationUsage<A> localInheritanceAnnotation = classDetails.getAnnotationUsage( annotationType );
if ( localInheritanceAnnotation != null ) {
if ( currentValue != null ) {
MODEL_CATEGORIZATION_LOGGER.debugf(
"Ignoring @%s from %s in favor of usage from %s",
annotationType.getSimpleName(),
classDetails.getName(),
currentValue.getAnnotationTarget().getName()
);
}
// the one "closest" to the root-entity should win
return localInheritanceAnnotation;
}
return currentValue;
}
public void collectIdAttribute(AttributeMetadata member) {
assert member != null;
if ( collectedIdAttributes == null ) {
collectedIdAttributes = member;
}
else if ( collectedIdAttributes instanceof List ) {
//noinspection unchecked,rawtypes
final List<AttributeMetadata> membersList = (List) collectedIdAttributes;
membersList.add( member );
}
else if ( collectedIdAttributes instanceof AttributeMetadata ) {
final ArrayList<AttributeMetadata> combined = new ArrayList<>();
combined.add( (AttributeMetadata) collectedIdAttributes );
combined.add( member );
collectedIdAttributes = combined;
}
}
public void collectNaturalIdAttribute(AttributeMetadata member) {
assert member != null;
if ( collectedNaturalIdAttributes == null ) {
collectedNaturalIdAttributes = member;
}
else if ( collectedNaturalIdAttributes instanceof List ) {
//noinspection unchecked,rawtypes
final List<AttributeMetadata> membersList = (List) collectedNaturalIdAttributes;
membersList.add( member );
}
else if ( collectedNaturalIdAttributes instanceof AttributeMetadata ) {
final ArrayList<AttributeMetadata> combined = new ArrayList<>();
combined.add( (AttributeMetadata) collectedNaturalIdAttributes );
combined.add( member );
collectedNaturalIdAttributes = combined;
}
}
}

View File

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

View File

@ -0,0 +1,14 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
/**
* @author Steve Ebersole
*/
public class HierarchyTypeHandling {
}

View File

@ -0,0 +1,131 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.categorize.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.categorize.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.getAnnotationUsage( PrePersist.class ) != null
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
prePersist = apply( methodDetails, PrePersist.class, managedTypeDetails, prePersist );
}
else if ( methodDetails.getAnnotationUsage( PostPersist.class ) != null
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postPersist = apply( methodDetails, PostPersist.class, managedTypeDetails, postPersist );
}
else if ( methodDetails.getAnnotationUsage( PreRemove.class ) != null
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
preRemove = apply( methodDetails, PreRemove.class, managedTypeDetails, preRemove );
}
else if ( methodDetails.getAnnotationUsage( PostRemove.class ) != null
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postRemove = apply( methodDetails, PostRemove.class, managedTypeDetails, postRemove );
}
else if ( methodDetails.getAnnotationUsage( PreUpdate.class ) != null
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
preUpdate = apply( methodDetails, PreUpdate.class, managedTypeDetails, preUpdate );
}
else if ( methodDetails.getAnnotationUsage( PostUpdate.class ) != null
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postUpdate = apply( methodDetails, PostUpdate.class, managedTypeDetails, postUpdate );
}
else if ( methodDetails.getAnnotationUsage( PostLoad.class ) != null
&& matchesSignature( JpaEventListenerStyle.CALLBACK, methodDetails ) ) {
postLoad = apply( methodDetails, PostLoad.class, managedTypeDetails, postLoad );
}
}
private static <A extends Annotation> MethodDetails apply(
MethodDetails incomingValue,
Class<A> annotationType,
ClassDetails managedTypeDetails,
MethodDetails currentValue) {
if ( currentValue != null ) {
throw new ModelsException(
String.format(
Locale.ROOT,
"Encountered multiple @%s methods [%s] - %s, %s",
annotationType.getSimpleName(),
managedTypeDetails.getClassName(),
currentValue.getName(),
incomingValue.getName()
)
);
}
return incomingValue;
}
public JpaEventListener resolve() {
if ( prePersist != null
|| postPersist != null
|| preUpdate != null
|| postUpdate != null
|| preRemove != null
|| postRemove != null
|| postLoad != null ) {
return new JpaEventListener(
JpaEventListenerStyle.CALLBACK,
managedTypeDetails,
prePersist,
postPersist,
preRemove,
postRemove,
preUpdate,
postUpdate,
postLoad
);
}
return null;
}
}

View File

@ -0,0 +1,77 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.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.categorize.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;
public MappedSuperclassTypeMetadataImpl(
ClassDetails classDetails,
EntityHierarchy hierarchy,
AccessType defaultAccessType,
HierarchyTypeConsumer typeConsumer,
ModelCategorizationContext modelContext) {
super( classDetails, hierarchy, defaultAccessType, modelContext );
final LifecycleCallbackCollector lifecycleCallbackCollector = new LifecycleCallbackCollector( classDetails, modelContext );
this.attributeList = resolveAttributes( lifecycleCallbackCollector );
this.hierarchyEventListeners = collectHierarchyEventListeners( lifecycleCallbackCollector.resolve() );
this.completeEventListeners = collectCompleteEventListeners( modelContext );
postInstantiate( typeConsumer );
}
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( typeConsumer );
}
@Override
protected List<AttributeMetadata> attributeList() {
return attributeList;
}
@Override
public List<JpaEventListener> getHierarchyJpaEventListeners() {
return hierarchyEventListeners;
}
@Override
public List<JpaEventListener> getCompleteJpaEventListeners() {
return completeEventListeners;
}
}

View File

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

View File

@ -0,0 +1,58 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.lang.annotation.Annotation;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.models.spi.AnnotationUsage;
/**
* @see JpaAnnotations#NAMED_QUERY
* @see JpaAnnotations#NAMED_NATIVE_QUERY
* @see JpaAnnotations#NAMED_STORED_PROCEDURE_QUERY
* @see HibernateAnnotations#NAMED_QUERY
* @see HibernateAnnotations#NAMED_NATIVE_QUERY
*
* @author Steve Ebersole
*/
public class NamedQueryRegistration {
public enum Kind {
HQL,
NATIVE,
CALLABLE
}
private final String name;
private final Kind kind;
private final boolean isJpa;
private final AnnotationUsage<? extends Annotation> configuration;
public NamedQueryRegistration(String name, Kind kind, boolean isJpa, AnnotationUsage<? extends Annotation> configuration) {
this.name = name;
this.kind = kind;
this.isJpa = isJpa;
this.configuration = configuration;
}
public String getName() {
return name;
}
public Kind getKind() {
return kind;
}
public boolean isJpa() {
return isJpa;
}
public AnnotationUsage<? extends Annotation> getConfiguration() {
return configuration;
}
}

View File

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

View File

@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.lang.reflect.Field;
import java.util.Locale;
import java.util.function.Consumer;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.models.AnnotationAccessException;
import org.hibernate.models.spi.AnnotationDescriptor;
/**
* @author Steve Ebersole
*/
public class OrmAnnotationHelper {
public static void forEachOrmAnnotation(Consumer<AnnotationDescriptor<?>> consumer) {
JpaAnnotations.forEachAnnotation( consumer );
HibernateAnnotations.forEachAnnotation( consumer );
}
public static void forEachOrmAnnotation(Class<?> declarer, Consumer<AnnotationDescriptor<?>> consumer) {
for ( Field field : declarer.getFields() ) {
if ( AnnotationDescriptor.class.equals( field.getType() ) ) {
try {
consumer.accept( (AnnotationDescriptor<?>) field.get( null ) );
}
catch (IllegalAccessException e) {
throw new AnnotationAccessException(
String.format(
Locale.ROOT,
"Unable to access standard annotation descriptor field - %s",
field.getName()
),
e
);
}
}
}
}
}

View File

@ -0,0 +1,188 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.internal;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.hibernate.boot.models.AccessTypePlacementException;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.models.categorize.spi.ModelCategorizationContext;
import org.hibernate.models.spi.AnnotationUsage;
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.Access;
import jakarta.persistence.AccessType;
/**
* Standard implementation of the PersistentAttributeMemberResolver contract
* based strictly on the JPA specification.
*
* @author Steve Ebersole
*/
public class StandardPersistentAttributeMemberResolver extends AbstractPersistentAttributeMemberResolver {
/**
* Singleton access
*/
public static final StandardPersistentAttributeMemberResolver INSTANCE = new StandardPersistentAttributeMemberResolver();
@Override
protected List<MemberDetails> resolveAttributesMembers(
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
AccessType classLevelAccessType,
ModelCategorizationContext processingContext) {
assert classLevelAccessType != null;
final LinkedHashMap<String,MemberDetails> results = new LinkedHashMap<>();
processAttributeLevelAccess(
results::put,
transientFieldChecker,
transientMethodChecker,
classDetails,
processingContext
);
processClassLevelAccess(
results::containsKey,
results::put,
transientFieldChecker,
transientMethodChecker,
classDetails,
classLevelAccessType,
processingContext
);
return new ArrayList<>( results.values() );
}
private <M extends MemberDetails> void processAttributeLevelAccess(
BiConsumer<String,MemberDetails> memberConsumer,
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
ModelCategorizationContext processingContext) {
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, processingContext );
}
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, processingContext );
}
}
private <M extends MemberDetails> void processAttributeLevelAccessMember(
M memberDetails,
BiConsumer<String,MemberDetails> memberConsumer,
Function<M,Boolean> transiencyChecker,
ClassDetails classDetails,
ModelCategorizationContext processingContext) {
final AnnotationUsage<Access> access = memberDetails.getAnnotationUsage( JpaAnnotations.ACCESS );
if ( access == null ) {
return;
}
final AccessType attributeAccessType = access.getAttributeValue( "value" );
validateAttributeLevelAccess(
memberDetails,
attributeAccessType,
classDetails,
processingContext
);
if ( transiencyChecker.apply( memberDetails ) ) {
// the field is @Transient
return;
}
memberConsumer.accept( memberDetails.resolveAttributeName(), memberDetails );
}
private void validateAttributeLevelAccess(
MemberDetails annotationTarget,
AccessType attributeAccessType,
ClassDetails classDetails,
ModelCategorizationContext processingContext) {
// Apply the checks defined in section `2.3.2 Explicit Access Type` of the persistence specification
// Mainly, it is never legal to:
// 1. specify @Access(FIELD) on a getter
// 2. specify @Access(PROPERTY) on a field
if ( ( attributeAccessType == AccessType.FIELD && !annotationTarget.isField() )
|| ( attributeAccessType == AccessType.PROPERTY && annotationTarget.isField() ) ) {
throw new AccessTypePlacementException( classDetails, annotationTarget );
}
}
private void processClassLevelAccess(
Function<String,Boolean> alreadyProcessedChecker,
BiConsumer<String, MemberDetails> memberConsumer,
Function<FieldDetails,Boolean> transientFieldChecker,
Function<MethodDetails,Boolean> transientMethodChecker,
ClassDetails classDetails,
AccessType classLevelAccessType,
@SuppressWarnings("unused") ModelCategorizationContext processingContext) {
if ( classLevelAccessType == 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 == AccessType.PROPERTY;
final List<MethodDetails> methods = classDetails.getMethods();
for ( int i = 0; i < methods.size(); i++ ) {
final MethodDetails methodDetails = methods.get( i );
if ( !methodDetails.isPersistable() ) {
continue;
}
final String attributeName = methodDetails.resolveAttributeName();
if ( alreadyProcessedChecker.apply( attributeName ) ) {
continue;
}
if ( transientMethodChecker.apply( methodDetails ) ) {
// the method is @Transient
continue;
}
memberConsumer.accept( attributeName, methodDetails );
}
}
}
}

View File

@ -0,0 +1,27 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
/**
* 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.models.spi.AnnotationUsage annotations},
* {@linkplain org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl XML}, etc.)
* </li>
* <li>
* Process this "source metamodel" and produce the {@linkplain org.hibernate.boot.models.categorize.spi.CategorizedDomainModel categorized model}
* </li>
* </ol>
*
* @author Steve Ebersole
*/
package org.hibernate.boot.models.categorize;

View File

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

View File

@ -0,0 +1,17 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.MemberDetails;
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface AllMemberConsumer {
void acceptMember(MemberDetails memberDetails);
}

View File

@ -0,0 +1,17 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.internal.util.IndexedConsumer;
/**
* @author Steve Ebersole
*/
@FunctionalInterface
public interface AttributeConsumer extends IndexedConsumer<AttributeMetadata> {
}

View File

@ -0,0 +1,43 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.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.
*/
enum AttributeNature {
BASIC,
EMBEDDED,
ANY,
TO_ONE,
PLURAL
}
}

View File

@ -0,0 +1,13 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
/**
* @author Steve Ebersole
*/
public interface BasicKeyMapping extends SingleAttributeKeyMapping {
}

View File

@ -0,0 +1,136 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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;
import org.hibernate.models.spi.AnnotationUsage;
/**
* 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(
AnnotationUsage<Cache> cacheAnnotation,
AccessType implicitCacheAccessType,
String implicitRegionName) {
if ( cacheAnnotation == null ) {
regionName = implicitRegionName;
accessType = implicitCacheAccessType;
cacheLazyProperties = true;
}
else {
final String explicitRegionName = cacheAnnotation.getString( "region" );
regionName = StringHelper.isEmpty( explicitRegionName ) ? implicitRegionName : explicitRegionName;
accessType = interpretAccessStrategy( cacheAnnotation.getAttributeValue( "usage" ) );
final Boolean explicitIncludeLazy = cacheAnnotation.getBoolean( "includeLazy" );
if ( explicitIncludeLazy != null ) {
cacheLazyProperties = explicitIncludeLazy;
}
else {
final String include = cacheAnnotation.getAttributeValue( "include" );
assert "all".equals( include ) || "non-lazy".equals( include );
cacheLazyProperties = include.equals( "all" );
}
}
}
private AccessType interpretAccessStrategy(CacheConcurrencyStrategy usage) {
if ( usage == null ) {
return null;
}
switch ( usage ) {
case NONE: {
return null;
}
case READ_ONLY: {
return AccessType.READ_ONLY;
}
case READ_WRITE: {
return AccessType.READ_WRITE;
}
case NONSTRICT_READ_WRITE: {
return AccessType.NONSTRICT_READ_WRITE;
}
case TRANSACTIONAL: {
return AccessType.TRANSACTIONAL;
}
default: {
throw new ModelsException( "Unexpected cache concurrency strategy specified - " + usage );
}
}
}
public String getRegionName() {
return regionName;
}
public void setRegionName(String regionName) {
this.regionName = regionName;
}
public AccessType getAccessType() {
return accessType;
}
public void setAccessType(AccessType accessType) {
this.accessType = accessType;
}
public boolean isCacheLazyProperties() {
return cacheLazyProperties;
}
public void setCacheLazyProperties(boolean cacheLazyProperties) {
this.cacheLazyProperties = cacheLazyProperties;
}
public void overlay(CacheRegionDefinition overrides) {
if ( overrides == null ) {
return;
}
accessType = AccessType.fromExternalName( overrides.getUsage() );
if ( StringHelper.isEmpty( overrides.getRegion() ) ) {
regionName = overrides.getRegion();
}
// ugh, primitive boolean
cacheLazyProperties = overrides.isCacheLazy();
}
public void overlay(CacheRegion overrides) {
if ( overrides == null ) {
return;
}
this.accessType = overrides.accessType;
this.regionName = overrides.regionName;
this.cacheLazyProperties = overrides.cacheLazyProperties;
}
@Override
public String toString() {
return "Caching{" +
"region='" + regionName + '\''
+ ", accessType=" + accessType
+ ", cacheLazyProperties=" + cacheLazyProperties + '}';
}
}

View File

@ -0,0 +1,99 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.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();
/**
* 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 );
}
/**
* Global registrations collected while processing the persistence-unit.
*/
GlobalRegistrations getGlobalRegistrations();
}

View File

@ -0,0 +1,47 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.Map;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.models.spi.ClassDetails;
/**
* Registration for a {@linkplain org.hibernate.usertype.UserCollectionType}
*
* @see org.hibernate.annotations.CollectionTypeRegistration
* @see org.hibernate.boot.jaxb.mapping.spi.JaxbCollectionUserTypeImpl
*
* @author Steve Ebersole
*/
public class CollectionTypeRegistration {
private final CollectionClassification classification;
private final ClassDetails userTypeClass;
private final Map<String,String> parameterMap;
public CollectionTypeRegistration(
CollectionClassification classification,
ClassDetails userTypeClass,
Map<String, String> parameterMap) {
this.classification = classification;
this.userTypeClass = userTypeClass;
this.parameterMap = parameterMap;
}
public CollectionClassification getClassification() {
return classification;
}
public ClassDetails getUserTypeClass() {
return userTypeClass;
}
public Map<String, String> getParameterMap() {
return parameterMap;
}
}

View File

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

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.ClassDetails;
/**
* Registration for a {@linkplain org.hibernate.usertype.CompositeUserType}
*
* @see org.hibernate.annotations.CompositeTypeRegistration
* @see org.hibernate.boot.jaxb.mapping.spi.JaxbCompositeUserTypeRegistrationImpl
*
* @author Steve Ebersole
*/
public class CompositeUserTypeRegistration {
private final ClassDetails embeddableClass;
private final ClassDetails userTypeClass;
public CompositeUserTypeRegistration(ClassDetails embeddableClass, ClassDetails userTypeClass) {
this.embeddableClass = embeddableClass;
this.userTypeClass = userTypeClass;
}
public ClassDetails getEmbeddableClass() {
return embeddableClass;
}
public ClassDetails getUserTypeClass() {
return userTypeClass;
}
}

View File

@ -0,0 +1,189 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Objects;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorBypassedImpl;
import org.hibernate.boot.model.convert.internal.AutoApplicableConverterDescriptorStandardImpl;
import org.hibernate.boot.model.convert.internal.ConverterHelper;
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.convert.spi.JpaAttributeConverterCreationContext;
import org.hibernate.boot.model.convert.spi.RegisteredConversion;
import org.hibernate.boot.models.Copied;
import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.descriptor.converter.internal.JpaAttributeConverterImpl;
import org.hibernate.type.descriptor.converter.spi.JpaAttributeConverter;
import org.hibernate.type.descriptor.java.spi.JavaTypeRegistry;
import org.hibernate.type.spi.TypeConfiguration;
import com.fasterxml.classmate.ResolvedType;
import jakarta.persistence.AttributeConverter;
/**
* A registered conversion.
*
* @see org.hibernate.annotations.ConverterRegistration
*
* @todo copied from RegisteredConversion because of the "early" creation of `ConverterDescriptor`
* upstream. Technically the conversion from ClassDetails to Class should be fine since
* conversions are only valid for basic types which we will never enhance.
*
* @author Steve Ebersole
*/
@Copied(RegisteredConversion.class)
public class ConversionRegistration {
private final ClassDetails explicitDomainType;
private final ClassDetails converterType;
private final boolean autoApply;
private final AnnotationDescriptor<? extends Annotation> source;
public ConversionRegistration(
ClassDetails explicitDomainType,
ClassDetails converterType,
boolean autoApply,
AnnotationDescriptor<? extends Annotation> source) {
assert converterType != null;
this.explicitDomainType = explicitDomainType;
this.converterType = converterType;
this.autoApply = autoApply;
this.source = source;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
ConversionRegistration that = (ConversionRegistration) o;
return autoApply == that.autoApply
&& Objects.equals( explicitDomainType, that.explicitDomainType )
&& converterType.equals( that.converterType );
}
@Override
public int hashCode() {
return Objects.hash( explicitDomainType, converterType );
}
public ClassDetails getExplicitDomainType() {
return explicitDomainType;
}
public ClassDetails getConverterType() {
return converterType;
}
public boolean isAutoApply() {
return autoApply;
}
public AnnotationDescriptor<? extends Annotation> getSource() {
return source;
}
@Override
public String toString() {
return "ConversionRegistration( " + converterType.getClassName() + ", " + source.getAnnotationType().getSimpleName() + ", " + autoApply + ")";
}
public ConverterDescriptor makeConverterDescriptor(ClassmateContext classmateContext) {
final Class<Object> explicitDomainType = this.explicitDomainType.toJavaClass();
final Class<? extends AttributeConverter<?,?>> converterType = this.converterType.toJavaClass();
final List<ResolvedType> resolvedParamTypes = ConverterHelper.resolveConverterClassParamTypes(
converterType,
classmateContext
);
final ResolvedType relationalType = resolvedParamTypes.get( 1 );
final ResolvedType domainTypeToMatch;
if ( !void.class.equals( explicitDomainType ) ) {
domainTypeToMatch = classmateContext.getTypeResolver().resolve( explicitDomainType );
}
else {
domainTypeToMatch = resolvedParamTypes.get( 0 );
}
return new ConverterDescriptorImpl( converterType, domainTypeToMatch, relationalType, autoApply );
}
private static class ConverterDescriptorImpl implements ConverterDescriptor {
private final Class<? extends AttributeConverter<?, ?>> converterType;
private final ResolvedType domainTypeToMatch;
private final ResolvedType relationalType;
private final boolean autoApply;
private final AutoApplicableConverterDescriptor autoApplyDescriptor;
public ConverterDescriptorImpl(
Class<? extends AttributeConverter<?, ?>> converterType,
ResolvedType domainTypeToMatch,
ResolvedType relationalType,
boolean autoApply) {
this.converterType = converterType;
this.domainTypeToMatch = domainTypeToMatch;
this.relationalType = relationalType;
this.autoApply = autoApply;
this.autoApplyDescriptor = autoApply
? new AutoApplicableConverterDescriptorStandardImpl( this )
: AutoApplicableConverterDescriptorBypassedImpl.INSTANCE;
}
@Override
public Class<? extends AttributeConverter<?, ?>> getAttributeConverterClass() {
return converterType;
}
@Override
public ResolvedType getDomainValueResolvedType() {
return domainTypeToMatch;
}
@Override
public ResolvedType getRelationalValueResolvedType() {
return relationalType;
}
@Override
public AutoApplicableConverterDescriptor getAutoApplyDescriptor() {
return autoApplyDescriptor;
}
@SuppressWarnings("unchecked")
@Override
public JpaAttributeConverter<?, ?> createJpaAttributeConverter(JpaAttributeConverterCreationContext context) {
final ManagedBean<? extends AttributeConverter<?, ?>> converterBean = context
.getManagedBeanRegistry()
.getBean( converterType );
final TypeConfiguration typeConfiguration = context.getTypeConfiguration();
final JavaTypeRegistry javaTypeRegistry = typeConfiguration.getJavaTypeRegistry();
javaTypeRegistry.resolveDescriptor( domainTypeToMatch.getErasedType() );
//noinspection rawtypes
return new JpaAttributeConverterImpl(
converterBean,
javaTypeRegistry.getDescriptor( converterBean.getBeanClass() ),
javaTypeRegistry.resolveDescriptor( domainTypeToMatch.getErasedType() ),
javaTypeRegistry.resolveDescriptor( relationalType.getErasedType() )
);
}
}
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.ClassDetails;
/**
* Registered {@linkplain org.hibernate.metamodel.spi.EmbeddableInstantiator}
*
* @see org.hibernate.annotations.EmbeddableInstantiatorRegistration
* @see org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddableInstantiatorRegistrationImpl
*
* @author Steve Ebersole
*/
public class EmbeddableInstantiatorRegistration {
private final org.hibernate.models.spi.ClassDetails embeddableClass;
private final ClassDetails instantiator;
public EmbeddableInstantiatorRegistration(ClassDetails embeddableClass, ClassDetails instantiator) {
this.embeddableClass = embeddableClass;
this.instantiator = instantiator;
}
public ClassDetails getEmbeddableClass() {
return embeddableClass;
}
public ClassDetails getInstantiator() {
return instantiator;
}
}

View File

@ -0,0 +1,77 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.engine.OptimisticLockStyle;
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 inheritance strategy for the hierarchy.
*/
InheritanceType getInheritanceType();
KeyMapping getIdMapping();
KeyMapping getNaturalIdMapping();
AttributeMetadata getVersionAttribute();
AttributeMetadata getTenantIdAttribute();
/**
* Style of optimistic locking for the hierarchy.
*/
OptimisticLockStyle getOptimisticLockStyle();
/**
* The caching configuration for entities in this hierarchy.
*/
CacheRegion getCacheRegion();
/**
* The caching configuration for this hierarchy's {@linkplain org.hibernate.annotations.NaturalId natural-id}
*/
NaturalIdCacheRegion getNaturalIdCacheRegion();
/**
* Describes a type's place in the hierarchy relative to the {@linkplain #getRoot() root entity}
*/
enum HierarchyRelation { SUPER, ROOT, SUB }
@FunctionalInterface
interface HierarchyTypeVisitor {
void visitType(
IdentifiableTypeMetadata type,
IdentifiableTypeMetadata superType,
EntityHierarchy hierarchy,
HierarchyRelation relation);
}
}

View File

@ -0,0 +1,95 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
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 a select prior to performing a {@linkplain org.hibernate.Session#update}
*
* @deprecated Because {@linkplain org.hibernate.Session#update} itself is deprecated
*/
@Deprecated
boolean isSelectBeforeUpdate();
/**
* 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
*/
CustomSql getCustomInsert();
/**
* Custom SQL to perform an UPDATE of this entity
*/
CustomSql getCustomUpdate();
/**
* Custom SQL to perform an DELETE of this entity
*/
CustomSql getCustomDelete();
}

View File

@ -0,0 +1,45 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.Map;
import org.hibernate.models.spi.ClassDetails;
/**
* Global registration of a filter definition
*
* @see org.hibernate.annotations.FilterDef
* @see org.hibernate.boot.jaxb.mapping.JaxbFilterDef
*
* @author Marco Belladelli
*/
public class FilterDefRegistration {
private final String name;
private final String defaultCondition;
private final Map<String, ClassDetails> parameters;
public FilterDefRegistration(String name, String defaultCondition, Map<String, ClassDetails> parameters) {
this.name = name;
this.defaultCondition = defaultCondition;
this.parameters = parameters;
}
public String getName() {
return name;
}
public String getDefaultCondition() {
return defaultCondition;
}
public Map<String, ClassDetails> getParameters() {
return parameters;
}
}

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.models.spi.AnnotationUsage;
/**
* Global registration of a generic generator
*
* @see GenericGenerator
* @see org.hibernate.boot.jaxb.mapping.spi.JaxbGenericIdGeneratorImpl
*
* @author Steve Ebersole
*/
public class GenericGeneratorRegistration {
private final String name;
private final AnnotationUsage<GenericGenerator> configuration;
public GenericGeneratorRegistration(String name, AnnotationUsage<GenericGenerator> configuration) {
this.name = name;
this.configuration = configuration;
}
public String getName() {
return name;
}
public AnnotationUsage<GenericGenerator> getConfiguration() {
return configuration;
}
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.List;
import java.util.Map;
/**
* Registrations which are considered global, collected across annotations
* and XML mappings.
*
* @author Steve Ebersole
*/
public interface GlobalRegistrations {
List<JpaEventListener> getEntityListenerRegistrations();
List<ConversionRegistration> getConverterRegistrations();
List<JavaTypeRegistration> getJavaTypeRegistrations();
List<JdbcTypeRegistration> getJdbcTypeRegistrations();
List<UserTypeRegistration> getUserTypeRegistrations();
List<CompositeUserTypeRegistration> getCompositeUserTypeRegistrations();
List<CollectionTypeRegistration> getCollectionTypeRegistrations();
List<EmbeddableInstantiatorRegistration> getEmbeddableInstantiatorRegistrations();
Map<String, FilterDefRegistration> getFilterDefRegistrations();
Map<String, SequenceGeneratorRegistration> getSequenceGeneratorRegistrations();
Map<String, TableGeneratorRegistration> getTableGeneratorRegistrations();
Map<String, GenericGeneratorRegistration> getGenericGeneratorRegistrations();
// todo : named entity graphs
// todo : named queries
}

View File

@ -0,0 +1,71 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.List;
import java.util.function.Consumer;
/**
* Metadata about an {@linkplain jakarta.persistence.metamodel.IdentifiableType identifiable type}
*
* @author Steve Ebersole
*/
public interface IdentifiableTypeMetadata extends ManagedTypeMetadata, TableOwner {
/**
* The hierarchy in which this IdentifiableType occurs.
*/
EntityHierarchy getHierarchy();
/**
* The super-type, if one
*/
IdentifiableTypeMetadata getSuperType();
/**
* Whether this type is considered abstract.
*/
default boolean isAbstract() {
return getClassDetails().isAbstract();
}
/**
* Whether this type has subtypes
*/
boolean hasSubTypes();
/**
* Get the number of direct subtypes
*/
int getNumberOfSubTypes();
/**
* Get the direct subtypes
*/
Iterable<IdentifiableTypeMetadata> getSubTypes();
/**
* Visit each direct subtype
*/
void forEachSubType(Consumer<IdentifiableTypeMetadata> consumer);
/**
* Event listeners in effect for this type, minus
* {@linkplain jakarta.persistence.ExcludeDefaultListeners default listeners}.
*
* @apiNote Kept separate from {@linkplain #getCompleteJpaEventListeners()}
* to facilitate types building their complete set with their
* {@linkplain jakarta.persistence.ExcludeSuperclassListeners superclass listeners}.
*/
List<JpaEventListener> getHierarchyJpaEventListeners();
/**
* Event listeners in effect for this type, including
* {@linkplain jakarta.persistence.ExcludeDefaultListeners default listeners}
*/
List<JpaEventListener> getCompleteJpaEventListeners();
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.ClassDetails;
/**
* {@linkplain org.hibernate.type.descriptor.java.JavaType} registration
*
* @see org.hibernate.annotations.JavaTypeRegistration
* @see org.hibernate.boot.jaxb.mapping.spi.JaxbJavaTypeRegistrationImpl
*
* @author Steve Ebersole
*/
public class JavaTypeRegistration {
private final ClassDetails domainType;
private final ClassDetails descriptor;
public JavaTypeRegistration(ClassDetails domainType, ClassDetails descriptor) {
this.domainType = domainType;
this.descriptor = descriptor;
}
public ClassDetails getDomainType() {
return domainType;
}
public ClassDetails getDescriptor() {
return descriptor;
}
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.ClassDetails;
/**
* {@linkplain org.hibernate.type.descriptor.jdbc.JdbcType} registration
*
* @see org.hibernate.annotations.JdbcTypeRegistration
* @see org.hibernate.boot.jaxb.mapping.JaxbJdbcTypeRegistration
*
* @author Steve Ebersole
*/
public class JdbcTypeRegistration {
private final Integer code;
private final ClassDetails descriptor;
public JdbcTypeRegistration(Integer code, ClassDetails descriptor) {
this.code = code;
this.descriptor = descriptor;
}
public Integer getCode() {
return code;
}
public ClassDetails getDescriptor() {
return descriptor;
}
}

View File

@ -0,0 +1,275 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistenceUnitDefaultsImpl;
import org.hibernate.internal.util.MutableObject;
import org.hibernate.models.ModelsException;
import org.hibernate.models.internal.jdk.VoidClassDetails;
import org.hibernate.models.spi.ClassDetails;
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;
/**
* JPA-style event listener with support for resolving callback methods
* {@linkplain #from(JpaEventListenerStyle, ClassDetails, JaxbEntityListenerImpl) by name} (from XML)
* or by {@linkplain #from(JpaEventListenerStyle, ClassDetails) annotation}.
*
* Represents a global entity listener defined in the persistence unit
*
* @see JaxbPersistenceUnitDefaultsImpl#getEntityListeners()
* @see JaxbEntityListenerImpl
* @see GlobalRegistrations#getEntityListenerRegistrations()
*
* @author Steve Ebersole
*/
public class JpaEventListener {
private final JpaEventListenerStyle consumerType;
private final ClassDetails listenerClass;
private final MethodDetails prePersistMethod;
private final MethodDetails postPersistMethod;
private final MethodDetails preRemoveMethod;
private final MethodDetails postRemoveMethod;
private final MethodDetails preUpdateMethod;
private final MethodDetails postUpdateMethod;
private final MethodDetails postLoadMethod;
public JpaEventListener(
JpaEventListenerStyle consumerType,
ClassDetails listenerClass,
MethodDetails prePersistMethod,
MethodDetails postPersistMethod,
MethodDetails preRemoveMethod,
MethodDetails postRemoveMethod,
MethodDetails preUpdateMethod,
MethodDetails postUpdateMethod,
MethodDetails postLoadMethod) {
this.consumerType = consumerType;
this.listenerClass = listenerClass;
this.prePersistMethod = prePersistMethod;
this.postPersistMethod = postPersistMethod;
this.preRemoveMethod = preRemoveMethod;
this.postRemoveMethod = postRemoveMethod;
this.preUpdateMethod = preUpdateMethod;
this.postUpdateMethod = postUpdateMethod;
this.postLoadMethod = postLoadMethod;
}
public JpaEventListenerStyle getStyle() {
return consumerType;
}
public ClassDetails getCallbackClass() {
return listenerClass;
}
public MethodDetails getPrePersistMethod() {
return prePersistMethod;
}
public MethodDetails getPostPersistMethod() {
return postPersistMethod;
}
public MethodDetails getPreRemoveMethod() {
return preRemoveMethod;
}
public MethodDetails getPostRemoveMethod() {
return postRemoveMethod;
}
public MethodDetails getPreUpdateMethod() {
return preUpdateMethod;
}
public MethodDetails getPostUpdateMethod() {
return postUpdateMethod;
}
public MethodDetails getPostLoadMethod() {
return postLoadMethod;
}
/**
* Create a listener descriptor from XML (with explicitly named methods)
*
* @see JaxbPersistenceUnitDefaultsImpl#getEntityListeners()
* @see JaxbEntityListenerImpl
* @see GlobalRegistrations#getEntityListenerRegistrations()
*/
public static JpaEventListener from(
JpaEventListenerStyle consumerType,
ClassDetails listenerClassDetails,
JaxbEntityListenerImpl jaxbMapping) {
final MutableObject<MethodDetails> prePersistMethod = new MutableObject<>();
final MutableObject<MethodDetails> postPersistMethod = new MutableObject<>();
final MutableObject<MethodDetails> preRemoveMethod = new MutableObject<>();
final MutableObject<MethodDetails> postRemoveMethod = new MutableObject<>();
final MutableObject<MethodDetails> preUpdateMethod = new MutableObject<>();
final MutableObject<MethodDetails> postUpdateMethod = new MutableObject<>();
final MutableObject<MethodDetails> postLoadMethod = new MutableObject<>();
listenerClassDetails.forEachMethod( (index, methodDetails) -> {
if ( jaxbMapping.getPrePersist() != null
&& methodDetails.getName().equals( jaxbMapping.getPrePersist().getMethodName() )
&& matchesSignature( consumerType, methodDetails ) ) {
prePersistMethod.set( methodDetails );
}
else if ( jaxbMapping.getPostPersist().getMethodName() != null
&& methodDetails.getName().equals( jaxbMapping.getPostPersist().getMethodName() )
&& matchesSignature( consumerType, methodDetails ) ) {
postPersistMethod.set( methodDetails );
}
else if ( jaxbMapping.getPreRemove() != null
&& methodDetails.getName().equals( jaxbMapping.getPreRemove().getMethodName() )
&& matchesSignature( consumerType, methodDetails ) ) {
preRemoveMethod.set( methodDetails );
}
else if ( jaxbMapping.getPostRemove() != null
&& methodDetails.getName().equals( jaxbMapping.getPostRemove().getMethodName() )
&& matchesSignature( consumerType, methodDetails ) ) {
postRemoveMethod.set( methodDetails );
}
else if ( jaxbMapping.getPreUpdate() != null
&& methodDetails.getName().equals( jaxbMapping.getPreUpdate().getMethodName() )
&& matchesSignature( consumerType, methodDetails ) ) {
preUpdateMethod.set( methodDetails );
}
else if ( jaxbMapping.getPostUpdate() != null
&& methodDetails.getName().equals( jaxbMapping.getPostUpdate().getMethodName() )
&& matchesSignature( consumerType, methodDetails ) ) {
postUpdateMethod.set( methodDetails );
}
else if ( jaxbMapping.getPostLoad() != null
&& methodDetails.getName().equals( jaxbMapping.getPostLoad().getMethodName() )
&& matchesSignature( consumerType, methodDetails ) ) {
postLoadMethod.set( methodDetails );
}
} );
final JpaEventListener jpaEventListener = new JpaEventListener(
consumerType,
listenerClassDetails,
prePersistMethod.get(),
postPersistMethod.get(),
preRemoveMethod.get(),
postRemoveMethod.get(),
preUpdateMethod.get(),
postUpdateMethod.get(),
postLoadMethod.get()
);
errorIfEmpty( jpaEventListener );
return jpaEventListener;
}
private static void errorIfEmpty(JpaEventListener jpaEventListener) {
if ( jpaEventListener.prePersistMethod == null
&& jpaEventListener.postPersistMethod == null
&& jpaEventListener.preRemoveMethod == null
&& jpaEventListener.postRemoveMethod == null
&& jpaEventListener.preUpdateMethod == null
&& jpaEventListener.postUpdateMethod == null
&& jpaEventListener.postLoadMethod == null ) {
throw new ModelsException( "Mapping for entity-listener specified no callback methods - " + jpaEventListener.listenerClass.getClassName() );
}
}
public static JpaEventListener from(JpaEventListenerStyle consumerType, ClassDetails listenerClassDetails) {
final MutableObject<MethodDetails> prePersistMethod = new MutableObject<>();
final MutableObject<MethodDetails> postPersistMethod = new MutableObject<>();
final MutableObject<MethodDetails> preRemoveMethod = new MutableObject<>();
final MutableObject<MethodDetails> postRemoveMethod = new MutableObject<>();
final MutableObject<MethodDetails> preUpdateMethod = new MutableObject<>();
final MutableObject<MethodDetails> postUpdateMethod = new MutableObject<>();
final MutableObject<MethodDetails> postLoadMethod = new MutableObject<>();
listenerClassDetails.forEachMethod( (index, methodDetails) -> {
if ( methodDetails.getAnnotationUsage( PrePersist.class ) != null
&& matchesSignature( consumerType, methodDetails ) ) {
prePersistMethod.set( methodDetails );
}
else if ( methodDetails.getAnnotationUsage( PostPersist.class ) != null
&& matchesSignature( consumerType, methodDetails ) ) {
postPersistMethod.set( methodDetails );
}
else if ( methodDetails.getAnnotationUsage( PreRemove.class ) != null
&& matchesSignature( consumerType, methodDetails ) ) {
preRemoveMethod.set( methodDetails );
}
else if ( methodDetails.getAnnotationUsage( PostRemove.class ) != null
&& matchesSignature( consumerType, methodDetails ) ) {
postRemoveMethod.set( methodDetails );
}
else if ( methodDetails.getAnnotationUsage( PreUpdate.class ) != null
&& matchesSignature( consumerType, methodDetails ) ) {
preUpdateMethod.set( methodDetails );
}
else if ( methodDetails.getAnnotationUsage( PostUpdate.class ) != null
&& matchesSignature( consumerType, methodDetails ) ) {
postUpdateMethod.set( methodDetails );
}
else if ( methodDetails.getAnnotationUsage( PostLoad.class ) != null
&& matchesSignature( consumerType, methodDetails ) ) {
postLoadMethod.set( methodDetails );
}
} );
final JpaEventListener jpaEventListener = new JpaEventListener(
consumerType,
listenerClassDetails,
prePersistMethod.get(),
postPersistMethod.get(),
preRemoveMethod.get(),
postRemoveMethod.get(),
preUpdateMethod.get(),
postUpdateMethod.get(),
postLoadMethod.get()
);
errorIfEmpty( jpaEventListener );
return jpaEventListener;
}
public static JpaEventListener tryAsCallback(ClassDetails classDetails) {
try {
return from( JpaEventListenerStyle.CALLBACK, classDetails );
}
catch ( ModelsException e ) {
return null;
}
}
public static boolean matchesSignature(JpaEventListenerStyle callbackType, MethodDetails methodDetails) {
if ( callbackType == JpaEventListenerStyle.CALLBACK ) {
// should have no arguments. and technically (spec) have a void return
return methodDetails.getArgumentTypes().isEmpty()
&& methodDetails.getReturnType() == VoidClassDetails.VOID_CLASS_DETAILS;
}
else {
assert callbackType == JpaEventListenerStyle.LISTENER;
// should have 1 argument. and technically (spec) have a void return
return methodDetails.getArgumentTypes().size() == 1
&& methodDetails.getReturnType() == VoidClassDetails.VOID_CLASS_DETAILS;
}
}
}

View File

@ -0,0 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
/**
* JPA defines 2 ways events callbacks can happen...
*
* @author Steve Ebersole
*/
public enum JpaEventListenerStyle {
/**
* The event method is declared on the entity class.
* The annotated method should define no arguments and have a void return type.
*/
CALLBACK,
/**
* The event method is declared on a separate "listener" class named by {@linkplain jakarta.persistence.EntityListeners}.
* The annotated method should accept a single argument - the entity instance - and have a void return type.
*/
LISTENER
}

View File

@ -0,0 +1,20 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.ClassDetails;
/**
* @author Steve Ebersole
*/
public interface KeyMapping {
ClassDetails getKeyType();
void forEachAttribute(AttributeConsumer consumer);
boolean contains(AttributeMetadata attributeMetadata);
}

View File

@ -0,0 +1,265 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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 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.categorize.internal.DomainModelCategorizationCollector;
import org.hibernate.boot.models.categorize.internal.ModelCategorizationContextImpl;
import org.hibernate.boot.models.categorize.internal.OrmAnnotationHelper;
import org.hibernate.boot.models.categorize.xml.spi.XmlPreProcessingResult;
import org.hibernate.boot.models.categorize.xml.spi.XmlPreProcessor;
import org.hibernate.boot.models.categorize.xml.spi.XmlProcessingResult;
import org.hibernate.boot.models.categorize.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.models.internal.SourceModelBuildingContextImpl;
import org.hibernate.models.internal.jandex.JandexClassDetails;
import org.hibernate.models.internal.jandex.JandexIndexerHelper;
import org.hibernate.models.internal.jdk.JdkBuilders;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.ClassLoading;
import org.hibernate.models.spi.RegistryPrimer;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.CompositeIndex;
import org.jboss.jandex.IndexView;
import org.jboss.jandex.Indexer;
import static org.hibernate.boot.models.categorize.internal.EntityHierarchyBuilder.createEntityHierarchies;
import static org.hibernate.models.internal.util.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,
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 XmlPreProcessingResult xmlPreProcessingResult = XmlPreProcessor.preProcessXmlResources( managedResources );
final List<String> allKnownClassNames = mutableJoin(
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 IndexView jandexIndex = resolveJandexIndex( allKnownClassNames, bootstrapContext.getJandexView(), classLoading );
final SourceModelBuildingContextImpl sourceModelBuildingContext = new SourceModelBuildingContextImpl(
classLoading,
jandexIndex,
ManagedResourcesProcessor::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 mutableClassDetailsRegistry = sourceModelBuildingContext.getClassDetailsRegistry();
final AnnotationDescriptorRegistry descriptorRegistry = sourceModelBuildingContext.getAnnotationDescriptorRegistry();
final DomainModelCategorizationCollector modelCategorizationCollector = new DomainModelCategorizationCollector(
areIdGeneratorsGlobal,
mutableClassDetailsRegistry,
descriptorRegistry
);
final XmlProcessingResult xmlProcessingResult = XmlProcessor.processXml( xmlPreProcessingResult, modelCategorizationCollector, sourceModelBuildingContext );
allKnownClassNames.forEach( (className) -> {
final ClassDetails classDetails = mutableClassDetailsRegistry.resolveClassDetails( className );
modelCategorizationCollector.apply( classDetails );
} );
xmlPreProcessingResult.getMappedNames().forEach( (className) -> {
final ClassDetails classDetails = mutableClassDetailsRegistry.resolveClassDetails( className );
modelCategorizationCollector.apply( classDetails );
} );
xmlProcessingResult.apply( xmlPreProcessingResult.getPersistenceUnitMetadata() );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// - create entity-hierarchies
// - create the CategorizedDomainModel
//
// INPUTS:
// - rootEntities
// - mappedSuperClasses
// - embeddables
//
// OUTPUTS:
// - CategorizedDomainModel
final ClassDetailsRegistry classDetailsRegistryImmutable = mutableClassDetailsRegistry
.makeImmutableCopy();
final AnnotationDescriptorRegistry annotationDescriptorRegistryImmutable = descriptorRegistry
.makeImmutableCopy();
// Collect the entity hierarchies based on the set of `rootEntities`
final ModelCategorizationContextImpl mappingBuildingContext = new ModelCategorizationContextImpl(
classDetailsRegistryImmutable,
annotationDescriptorRegistryImmutable,
modelCategorizationCollector.getGlobalRegistrations()
);
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, classDetailsRegistryImmutable, annotationDescriptorRegistryImmutable );
}
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()
);
}
}
public static IndexView resolveJandexIndex(
List<String> allKnownClassNames,
IndexView suppliedJandexIndex,
ClassLoading classLoading) {
// todo : we could build a new Jandex (Composite)Index that includes the `managedResources#getAnnotatedClassNames`
// and all classes from `managedResources#getXmlMappingBindings`. Only really worth it in the case
// of runtime enhancement. This would definitely need to be toggle-able.
// +
// For now, let's not as it does not matter for this PoC
if ( 1 == 1 ) {
return suppliedJandexIndex;
}
final Indexer jandexIndexer = new Indexer();
for ( String knownClassName : allKnownClassNames ) {
JandexIndexerHelper.apply( knownClassName, jandexIndexer, classLoading );
}
if ( suppliedJandexIndex == null ) {
return jandexIndexer.complete();
}
return CompositeIndex.create( suppliedJandexIndex, jandexIndexer.complete() );
}
public static void preFillRegistries(RegistryPrimer.Contributions contributions, SourceModelBuildingContext buildingContext) {
OrmAnnotationHelper.forEachOrmAnnotation( contributions::registerAnnotation );
final IndexView jandexIndex = buildingContext.getJandexIndex();
if ( jandexIndex == null ) {
return;
}
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getClassDetailsRegistry();
final AnnotationDescriptorRegistry annotationDescriptorRegistry = buildingContext.getAnnotationDescriptorRegistry();
for ( ClassInfo knownClass : jandexIndex.getKnownClasses() ) {
final String className = knownClass.name().toString();
if ( knownClass.isAnnotation() ) {
// it is always safe to load the annotation classes - we will never be enhancing them
//noinspection rawtypes
final Class annotationClass = buildingContext
.getClassLoading()
.classForName( className );
//noinspection unchecked
annotationDescriptorRegistry.resolveDescriptor(
annotationClass,
(t) -> JdkBuilders.buildAnnotationDescriptor( annotationClass, buildingContext.getAnnotationDescriptorRegistry() )
);
}
classDetailsRegistry.resolveClassDetails(
className,
(name) -> new JandexClassDetails( knownClass, buildingContext )
);
}
}
}

View File

@ -0,0 +1,53 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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;
import jakarta.persistence.AccessType;
/**
* 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
*/
AccessType getAccessType();
/**
* Get the number of declared attributes
*/
int getNumberOfAttributes();
/**
* Get the declared attributes
*/
Collection<AttributeMetadata> getAttributes();
AttributeMetadata findAttribute(String name);
/**
* Visit each declared attributes
*/
void forEachAttribute(IndexedConsumer<AttributeMetadata> consumer);
}

View File

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

View File

@ -0,0 +1,34 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* 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.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetailsRegistry;
import jakarta.persistence.SharedCacheMode;
/**
* Contextual information used while building {@linkplain ManagedTypeMetadata} and friends.
*
* @author Steve Ebersole
*/
public interface ModelCategorizationContext {
ClassDetailsRegistry getClassDetailsRegistry();
AnnotationDescriptorRegistry getAnnotationDescriptorRegistry();
SharedCacheMode getSharedCacheMode();
default PersistentAttributeMemberResolver getPersistentAttributeMemberResolver() {
return StandardPersistentAttributeMemberResolver.INSTANCE;
}
List<JpaEventListener> getDefaultEventListeners();
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.annotations.NaturalIdCache;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.spi.AnnotationUsage;
/**
* 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(AnnotationUsage<NaturalIdCache> cacheAnnotation, CacheRegion cacheRegion) {
this.regionName = determineRegionName( cacheAnnotation, cacheRegion );
}
private static String determineRegionName(AnnotationUsage<NaturalIdCache> cacheAnnotation, CacheRegion cacheRegion) {
if ( cacheAnnotation != null ) {
final String explicitRegionName = cacheAnnotation.getAttributeValue( "region" );
if ( StringHelper.isNotEmpty( explicitRegionName ) ) {
return explicitRegionName;
}
}
// use the default value
return cacheRegion.getRegionName() + "##NaturalId";
}
public String getRegionName() {
return regionName;
}
}

View File

@ -0,0 +1,36 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.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();
}

Some files were not shown because too many files have changed in this diff Show More