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.jpa
api jakartaLibs.jta api jakartaLibs.jta
implementation libs.hcann implementation libs.hibernateModels
implementation libs.jandex implementation libs.jandex
implementation libs.classmate implementation libs.classmate
implementation libs.byteBuddy implementation libs.byteBuddy
implementation libs.hcann
implementation jakartaLibs.jaxbApi implementation jakartaLibs.jaxbApi
implementation jakartaLibs.jaxb 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