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:
parent
bcd927b21b
commit
b3241be1f0
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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" ) );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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()
|
||||||
|
) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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" );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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.
|
||||||
|
//
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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 + "`)";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) {}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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" );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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> {
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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 + '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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 )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
Loading…
Reference in New Issue