HHH-17504 - Ongoing JPA 32 work

HHH-17350 - Work on hibernate-models, XSD and JAXB
HHH-16114 - Improve boot metamodel binding
HHH-15996 - Develop an abstraction for Annotation in annotation processing
HHH-16012 - Develop an abstraction for domain model Class refs
HHH-15997 - Support for dynamic models in orm.xml
HHH-15698 - Support for entity-name in mapping.xsd
This commit is contained in:
Steve Ebersole 2024-03-06 11:54:30 -06:00
parent 540b87e78a
commit e5d9586ba3
139 changed files with 5825 additions and 5295 deletions

View File

@ -126,7 +126,7 @@ xjc {
xjcExtensions += ['inheritance', 'simplify']
}
mapping {
xsdFile = file( 'src/main/resources/org/hibernate/xsd/mapping/mapping-3.2.0.xsd' )
xsdFile = file( 'src/main/resources/org/hibernate/xsd/mapping/mapping-7.0.xsd' )
xjcBindingFile = file( 'src/main/xjb/mapping-bindings.xjb' )
xjcExtensions += ['inheritance', 'simplify']
}

View File

@ -6,6 +6,7 @@
*/
package org.hibernate.boot.internal;
import java.util.List;
import java.util.UUID;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
@ -15,11 +16,15 @@ import org.hibernate.id.UUIDGenerator;
import org.hibernate.id.enhanced.SequenceStyleGenerator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.UniqueConstraint;
/**
* Interpretations related to value generators based on the {@linkplain GenerationType strategy/type}
@ -81,91 +86,97 @@ public class GenerationStrategyInterpreter {
}
public void interpretTableGenerator(
TableGenerator tableGeneratorAnnotation,
AnnotationUsage<TableGenerator> tableGeneratorAnnotation,
IdentifierGeneratorDefinition.Builder definitionBuilder) {
definitionBuilder.setName( tableGeneratorAnnotation.name() );
definitionBuilder.setName( tableGeneratorAnnotation.getString( "name" ) );
definitionBuilder.setStrategy( org.hibernate.id.enhanced.TableGenerator.class.getName() );
definitionBuilder.addParam( org.hibernate.id.enhanced.TableGenerator.CONFIG_PREFER_SEGMENT_PER_ENTITY, "true" );
if ( !tableGeneratorAnnotation.catalog().isEmpty()) {
definitionBuilder.addParam( PersistentIdentifierGenerator.CATALOG, tableGeneratorAnnotation.catalog() );
final String catalog = tableGeneratorAnnotation.getString( "catalog" );
if ( StringHelper.isNotEmpty( catalog ) ) {
definitionBuilder.addParam( PersistentIdentifierGenerator.CATALOG, catalog );
}
if ( !tableGeneratorAnnotation.schema().isEmpty()) {
definitionBuilder.addParam( PersistentIdentifierGenerator.SCHEMA, tableGeneratorAnnotation.schema() );
final String schema = tableGeneratorAnnotation.getString( "schema" );
if ( StringHelper.isNotEmpty( schema ) ) {
definitionBuilder.addParam( PersistentIdentifierGenerator.SCHEMA, schema );
}
if ( !tableGeneratorAnnotation.table().isEmpty()) {
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.TABLE_PARAM,
tableGeneratorAnnotation.table()
);
final String table = tableGeneratorAnnotation.getString( "table" );
if ( StringHelper.isNotEmpty( table ) ) {
definitionBuilder.addParam( org.hibernate.id.enhanced.TableGenerator.TABLE_PARAM, table );
}
if ( !tableGeneratorAnnotation.pkColumnName().isEmpty()) {
final String pkColumnName = tableGeneratorAnnotation.getString( "pkColumnName" );
if ( StringHelper.isNotEmpty( pkColumnName ) ) {
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.SEGMENT_COLUMN_PARAM,
tableGeneratorAnnotation.pkColumnName()
pkColumnName
);
}
if ( !tableGeneratorAnnotation.pkColumnValue().isEmpty()) {
final String pkColumnValue = tableGeneratorAnnotation.getString( "pkColumnValue" );
if ( StringHelper.isNotEmpty( pkColumnValue ) ) {
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.SEGMENT_VALUE_PARAM,
tableGeneratorAnnotation.pkColumnValue()
pkColumnValue
);
}
if ( !tableGeneratorAnnotation.valueColumnName().isEmpty()) {
final String valueColumnName = tableGeneratorAnnotation.getString( "valueColumnName" );
if ( StringHelper.isNotEmpty( valueColumnName ) ) {
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.VALUE_COLUMN_PARAM,
tableGeneratorAnnotation.valueColumnName()
valueColumnName
);
}
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.INCREMENT_PARAM,
String.valueOf( tableGeneratorAnnotation.allocationSize() )
String.valueOf( tableGeneratorAnnotation.getInteger( "allocationSize" ) )
);
// See comment on HHH-4884 wrt initialValue. Basically initialValue is really the stated value + 1
definitionBuilder.addParam(
org.hibernate.id.enhanced.TableGenerator.INITIAL_PARAM,
String.valueOf( tableGeneratorAnnotation.initialValue() + 1 )
String.valueOf( tableGeneratorAnnotation.getInteger( "initialValue" ) + 1 )
);
// TODO : implement unique-constraint support
if ( tableGeneratorAnnotation.uniqueConstraints() != null
&& tableGeneratorAnnotation.uniqueConstraints().length > 0 ) {
LOG.ignoringTableGeneratorConstraints( tableGeneratorAnnotation.name() );
final List<AnnotationUsage<UniqueConstraint>> uniqueConstraints = tableGeneratorAnnotation.getList( "uniqueConstraints" );
if ( CollectionHelper.isNotEmpty( uniqueConstraints ) ) {
LOG.ignoringTableGeneratorConstraints( tableGeneratorAnnotation.getString( "name" ) );
}
}
public void interpretSequenceGenerator(
SequenceGenerator sequenceGeneratorAnnotation,
AnnotationUsage<SequenceGenerator> sequenceGeneratorAnnotation,
IdentifierGeneratorDefinition.Builder definitionBuilder) {
definitionBuilder.setName( sequenceGeneratorAnnotation.name() );
definitionBuilder.setName( sequenceGeneratorAnnotation.getString( "name" ) );
definitionBuilder.setStrategy( SequenceStyleGenerator.class.getName() );
if ( !sequenceGeneratorAnnotation.catalog().isEmpty()) {
definitionBuilder.addParam(
PersistentIdentifierGenerator.CATALOG,
sequenceGeneratorAnnotation.catalog()
);
final String catalog = sequenceGeneratorAnnotation.getString( "catalog" );
if ( StringHelper.isNotEmpty( catalog ) ) {
definitionBuilder.addParam( PersistentIdentifierGenerator.CATALOG, catalog );
}
if ( !sequenceGeneratorAnnotation.schema().isEmpty()) {
definitionBuilder.addParam(
PersistentIdentifierGenerator.SCHEMA,
sequenceGeneratorAnnotation.schema()
);
final String schema = sequenceGeneratorAnnotation.getString( "schema" );
if ( StringHelper.isNotEmpty( schema ) ) {
definitionBuilder.addParam( PersistentIdentifierGenerator.SCHEMA, schema );
}
if ( !sequenceGeneratorAnnotation.sequenceName().isEmpty()) {
definitionBuilder.addParam(
SequenceStyleGenerator.SEQUENCE_PARAM,
sequenceGeneratorAnnotation.sequenceName()
);
final String sequenceName = sequenceGeneratorAnnotation.getString( "sequenceName" );
if ( StringHelper.isNotEmpty( sequenceName ) ) {
definitionBuilder.addParam( SequenceStyleGenerator.SEQUENCE_PARAM, sequenceName );
}
definitionBuilder.addParam(
SequenceStyleGenerator.INCREMENT_PARAM,
String.valueOf( sequenceGeneratorAnnotation.allocationSize() )
String.valueOf( sequenceGeneratorAnnotation.getInteger( "allocationSize" ) )
);
definitionBuilder.addParam(
SequenceStyleGenerator.INITIAL_PARAM,
String.valueOf( sequenceGeneratorAnnotation.initialValue() )
String.valueOf( sequenceGeneratorAnnotation.getInteger( "initialValue" ) )
);
}
}

View File

@ -62,10 +62,17 @@ import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.source.internal.ImplicitColumnNamingSecondPass;
import org.hibernate.boot.model.source.spi.LocalMetadataBuildingContext;
import org.hibernate.boot.models.categorize.internal.ClassLoaderServiceLoading;
import org.hibernate.boot.models.categorize.internal.GlobalRegistrationsImpl;
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
import org.hibernate.boot.models.categorize.spi.ManagedResourcesProcessor;
import org.hibernate.boot.models.xml.internal.PersistenceUnitMetadataImpl;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.boot.query.NamedHqlQueryDefinition;
import org.hibernate.boot.query.NamedNativeQueryDefinition;
import org.hibernate.boot.query.NamedProcedureCallDefinition;
import org.hibernate.boot.query.NamedResultSetMappingDescriptor;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
@ -81,6 +88,7 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.generator.Generator;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
@ -99,6 +107,10 @@ import org.hibernate.mapping.Table;
import org.hibernate.metamodel.CollectionClassification;
import org.hibernate.metamodel.mapping.DiscriminatorType;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.models.internal.SourceModelBuildingContextImpl;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.query.named.NamedObjectRepository;
import org.hibernate.query.sqm.function.SqmFunctionDescriptor;
import org.hibernate.query.sqm.function.SqmFunctionRegistry;
@ -114,6 +126,7 @@ import jakarta.persistence.Entity;
import jakarta.persistence.MapsId;
import static org.hibernate.boot.model.naming.Identifier.toIdentifier;
import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize;
/**
* The implementation of the {@linkplain InFlightMetadataCollector in-flight
@ -131,6 +144,9 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private final BootstrapContext bootstrapContext;
private final MetadataBuildingOptions options;
private final GlobalRegistrations globalRegistrations;
private final PersistenceUnitMetadata persistenceUnitMetadata;
private final AttributeConverterManager attributeConverterManager = new AttributeConverterManager();
private final UUID uuid;
@ -138,7 +154,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private final Map<String,PersistentClass> entityBindingMap = new HashMap<>();
private final List<Component> composites = new ArrayList<>();
private final Map<Class<?>, Component> genericComponentsMap = new HashMap<>();
private final Map<XClass, List<XClass>> embeddableSubtypes = new HashMap<>();
private final Map<ClassDetails, List<ClassDetails>> embeddableSubtypes = new HashMap<>();
private final Map<Class<?>, DiscriminatorType<?>> embeddableDiscriminatorTypesMap = new HashMap<>();
private final Map<String,Collection> collectionBindingMap = new HashMap<>();
@ -168,20 +184,28 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private final Set<String> defaultSqlResultSetMappingNames = new HashSet<>();
private final Set<String> defaultNamedProcedureNames = new HashSet<>();
private Map<Class<?>, MappedSuperclass> mappedSuperClasses;
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithMapsId;
private Map<XClass, Map<String, PropertyData>> propertiesAnnotatedWithIdAndToOne;
private Map<ClassDetails, Map<String, PropertyData>> propertiesAnnotatedWithMapsId;
private Map<ClassDetails, Map<String, PropertyData>> propertiesAnnotatedWithIdAndToOne;
private Map<String, String> mappedByResolver;
private Map<String, String> propertyRefResolver;
private Set<DelayedPropertyReferenceHandler> delayedPropertyReferenceHandlers;
private List<Function<MetadataBuildingContext, Boolean>> valueResolvers;
private final SourceModelBuildingContext sourceModelBuildingContext;
public InFlightMetadataCollectorImpl(
BootstrapContext bootstrapContext,
SourceModelBuildingContext sourceModelBuildingContext,
MetadataBuildingOptions options) {
this.bootstrapContext = bootstrapContext;
this.uuid = UUID.randomUUID();
this.sourceModelBuildingContext = sourceModelBuildingContext;
this.options = options;
this.uuid = UUID.randomUUID();
this.globalRegistrations = new GlobalRegistrationsImpl( sourceModelBuildingContext );
this.persistenceUnitMetadata = new PersistenceUnitMetadataImpl();
for ( Map.Entry<String, SqmFunctionDescriptor> sqlFunctionEntry : bootstrapContext.getSqlFunctions().entrySet() ) {
if ( sqlFunctionMap == null ) {
// we need this to be a ConcurrentHashMap for the one we ultimately pass along to the SF
@ -194,6 +218,26 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
bootstrapContext.getAuxiliaryDatabaseObjectList().forEach( getDatabase()::addAuxiliaryDatabaseObject );
}
public InFlightMetadataCollectorImpl(
BootstrapContext bootstrapContext,
MetadataBuildingOptions options) {
this(
bootstrapContext,
buildContext( bootstrapContext ),
options
);
}
private static SourceModelBuildingContext buildContext(BootstrapContext bootstrapContext) {
final ClassLoaderService classLoaderService = bootstrapContext.getServiceRegistry().getService( ClassLoaderService.class );
final ClassLoaderServiceLoading classLoading = new ClassLoaderServiceLoading( classLoaderService );
return new SourceModelBuildingContextImpl(
classLoading,
bootstrapContext.getJandexView(),
ManagedResourcesProcessor::preFillRegistries
);
}
@Override
public UUID getUUID() {
return null;
@ -209,6 +253,21 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
return bootstrapContext;
}
@Override
public SourceModelBuildingContext getSourceModelBuildingContext() {
return sourceModelBuildingContext;
}
@Override
public GlobalRegistrations getGlobalRegistrations() {
return globalRegistrations;
}
@Override
public PersistenceUnitMetadata getPersistenceUnitMetadata() {
return persistenceUnitMetadata;
}
@Override
public TypeConfiguration getTypeConfiguration() {
return bootstrapContext.getTypeConfiguration();
@ -290,13 +349,13 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
}
@Override
public void registerEmbeddableSubclass(XClass superclass, XClass subclass) {
public void registerEmbeddableSubclass(ClassDetails superclass, ClassDetails subclass) {
embeddableSubtypes.computeIfAbsent( superclass, c -> new ArrayList<>() ).add( subclass );
}
@Override
public List<XClass> getEmbeddableSubclasses(XClass superclass) {
final List<XClass> subclasses = embeddableSubtypes.get( superclass );
public List<ClassDetails> getEmbeddableSubclasses(ClassDetails superclass) {
final List<ClassDetails> subclasses = embeddableSubtypes.get( superclass );
return subclasses != null ? subclasses : List.of();
}
@ -490,9 +549,9 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
private Map<CollectionClassification, CollectionTypeRegistrationDescriptor> collectionTypeRegistrations;
@Override
public void addCollectionTypeRegistration(CollectionTypeRegistration registrationAnnotation) {
public void addCollectionTypeRegistration(AnnotationUsage<CollectionTypeRegistration> registrationAnnotation) {
addCollectionTypeRegistration(
registrationAnnotation.classification(),
registrationAnnotation.getEnum( "classification" ),
toDescriptor( registrationAnnotation )
);
}
@ -514,20 +573,26 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
return collectionTypeRegistrations.get( classification );
}
private CollectionTypeRegistrationDescriptor toDescriptor(CollectionTypeRegistration registrationAnnotation) {
final Map<String,String> parameters;
if ( registrationAnnotation.parameters().length > 0 ) {
parameters = new HashMap<>();
for ( Parameter parameter : registrationAnnotation.parameters() ) {
parameters.put( parameter.name(), parameter.value() );
}
}
else {
parameters = null;
}
return new CollectionTypeRegistrationDescriptor( registrationAnnotation.type(), parameters );
private CollectionTypeRegistrationDescriptor toDescriptor(AnnotationUsage<CollectionTypeRegistration> registrationAnnotation) {
return new CollectionTypeRegistrationDescriptor(
registrationAnnotation.getClassDetails( "type" ).toJavaClass(),
extractParameters( registrationAnnotation.getList( "parameters" ) )
);
}
private Map<String,String> extractParameters(List<AnnotationUsage<Parameter>> annotationUsages) {
if ( CollectionHelper.isEmpty( annotationUsages ) ) {
return null;
}
final Map<String,String> result = mapOfSize( annotationUsages.size() );
for ( AnnotationUsage<Parameter> parameter : annotationUsages ) {
result.put( parameter.getString( "name" ), parameter.getString( "value" ) );
}
return result;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// attribute converters
@ -1297,6 +1362,43 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
}
}
@Override
public AnnotatedClassType addClassType(ClassDetails classDetails) {
final AnnotatedClassType type = getAnnotatedClassType2( classDetails );
annotatedClassTypeMap.put( classDetails.getName(), type );
return type;
}
private static AnnotatedClassType getAnnotatedClassType2(ClassDetails classDetails) {
if ( classDetails.hasAnnotationUsage( Entity.class ) ) {
return AnnotatedClassType.ENTITY;
}
else if ( classDetails.hasAnnotationUsage( Embeddable.class ) ) {
return AnnotatedClassType.EMBEDDABLE;
}
else if ( classDetails.hasAnnotationUsage( jakarta.persistence.MappedSuperclass.class ) ) {
return AnnotatedClassType.MAPPED_SUPERCLASS;
}
else if ( classDetails.hasAnnotationUsage( Imported.class ) ) {
return AnnotatedClassType.IMPORTED;
}
else {
return AnnotatedClassType.NONE;
}
}
@Override
public AnnotatedClassType getClassType(ClassDetails classDetails) {
final AnnotatedClassType type = annotatedClassTypeMap.get( classDetails.getName() );
if ( type == null ) {
return addClassType( classDetails );
}
else {
return type;
}
}
@Override
public void addMappedSuperclass(Class<?> type, MappedSuperclass mappedSuperclass) {
if ( mappedSuperClasses == null ) {
@ -1314,7 +1416,7 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
}
@Override
public PropertyData getPropertyAnnotatedWithMapsId(XClass entityType, String propertyName) {
public PropertyData getPropertyAnnotatedWithMapsId(ClassDetails entityType, String propertyName) {
if ( propertiesAnnotatedWithMapsId == null ) {
return null;
}
@ -1324,35 +1426,36 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
}
@Override
public void addPropertyAnnotatedWithMapsId(XClass entityType, PropertyData property) {
public void addPropertyAnnotatedWithMapsId(ClassDetails entityType, PropertyData property) {
if ( propertiesAnnotatedWithMapsId == null ) {
propertiesAnnotatedWithMapsId = new HashMap<>();
}
Map<String, PropertyData> map = propertiesAnnotatedWithMapsId.get( entityType );
if ( map == null ) {
map = new HashMap<>();
propertiesAnnotatedWithMapsId.put( entityType, map );
}
map.put( property.getProperty().getAnnotation( MapsId.class ).value(), property );
final Map<String, PropertyData> map = propertiesAnnotatedWithMapsId.computeIfAbsent(
entityType,
k -> new HashMap<>()
);
map.put(
property.getAttributeMember().getAnnotationUsage( MapsId.class ).getString( "value" ),
property
);
}
@Override
public void addPropertyAnnotatedWithMapsIdSpecj(XClass entityType, PropertyData property, String mapsIdValue) {
public void addPropertyAnnotatedWithMapsIdSpecj(ClassDetails entityType, PropertyData property, String mapsIdValue) {
if ( propertiesAnnotatedWithMapsId == null ) {
propertiesAnnotatedWithMapsId = new HashMap<>();
}
Map<String, PropertyData> map = propertiesAnnotatedWithMapsId.get( entityType );
if ( map == null ) {
map = new HashMap<>();
propertiesAnnotatedWithMapsId.put( entityType, map );
}
final Map<String, PropertyData> map = propertiesAnnotatedWithMapsId.computeIfAbsent(
entityType,
k -> new HashMap<>()
);
map.put( mapsIdValue, property );
}
@Override
public PropertyData getPropertyAnnotatedWithIdAndToOne(XClass entityType, String propertyName) {
public PropertyData getPropertyAnnotatedWithIdAndToOne(ClassDetails entityType, String propertyName) {
if ( propertiesAnnotatedWithIdAndToOne == null ) {
return null;
}
@ -1362,16 +1465,15 @@ public class InFlightMetadataCollectorImpl implements InFlightMetadataCollector,
}
@Override
public void addToOneAndIdProperty(XClass entityType, PropertyData property) {
public void addToOneAndIdProperty(ClassDetails entityType, PropertyData property) {
if ( propertiesAnnotatedWithIdAndToOne == null ) {
propertiesAnnotatedWithIdAndToOne = new HashMap<>();
}
Map<String, PropertyData> map = propertiesAnnotatedWithIdAndToOne.get( entityType );
if ( map == null ) {
map = new HashMap<>();
propertiesAnnotatedWithIdAndToOne.put( entityType, map );
}
final Map<String, PropertyData> map = propertiesAnnotatedWithIdAndToOne.computeIfAbsent(
entityType,
k -> new HashMap<>()
);
map.put( property.getPropertyName(), property );
}

View File

@ -42,6 +42,7 @@ import org.hibernate.boot.model.process.spi.MetadataBuildingProcess;
import org.hibernate.boot.model.relational.AuxiliaryDatabaseObject;
import org.hibernate.boot.model.relational.ColumnOrderingStrategy;
import org.hibernate.boot.model.relational.ColumnOrderingStrategyStandard;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.boot.registry.BootstrapServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -94,6 +95,7 @@ import jakarta.persistence.SharedCacheMode;
import static org.hibernate.cfg.AvailableSettings.JPA_COMPLIANCE;
import static org.hibernate.cfg.AvailableSettings.WRAPPER_ARRAY_HANDLING;
import static org.hibernate.engine.config.spi.StandardConverters.BOOLEAN;
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
/**
* @author Steve Ebersole
@ -983,18 +985,33 @@ public class MetadataBuilderImpl implements MetadataBuilderImplementor, TypeCont
}
if ( mappingDefaults.getImplicitCatalogName() == null ) {
mappingDefaults.implicitCatalogName = StringHelper.nullIfEmpty(
mappingDefaults.implicitCatalogName = nullIfEmpty(
jpaOrmXmlPersistenceUnitDefaults.getDefaultCatalogName()
);
}
if ( mappingDefaults.getImplicitSchemaName() == null ) {
mappingDefaults.implicitSchemaName = StringHelper.nullIfEmpty(
mappingDefaults.implicitSchemaName = nullIfEmpty(
jpaOrmXmlPersistenceUnitDefaults.getDefaultSchemaName()
);
}
}
@Override
public void apply(PersistenceUnitMetadata persistenceUnitMetadata) {
if ( !mappingDefaults.implicitlyQuoteIdentifiers ) {
mappingDefaults.implicitlyQuoteIdentifiers = persistenceUnitMetadata.useQuotedIdentifiers();
}
if ( mappingDefaults.getImplicitCatalogName() == null ) {
mappingDefaults.implicitCatalogName = nullIfEmpty( persistenceUnitMetadata.getDefaultCatalog() );
}
if ( mappingDefaults.getImplicitSchemaName() == null ) {
mappingDefaults.implicitSchemaName = nullIfEmpty( persistenceUnitMetadata.getDefaultSchema() );
}
}
public void setBootstrapContext(BootstrapContext bootstrapContext) {
this.bootstrapContext = bootstrapContext;
}

View File

@ -19,6 +19,9 @@ import org.hibernate.boot.model.internal.QueryHintDefinition;
import org.hibernate.boot.query.NamedProcedureCallDefinition;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.procedure.internal.NamedCallableQueryMementoImpl;
import org.hibernate.procedure.internal.Util;
import org.hibernate.procedure.spi.NamedCallableQueryMemento;
@ -47,14 +50,15 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
private final ParameterDefinitions parameterDefinitions;
private final Map<String, Object> hints;
public NamedProcedureCallDefinitionImpl(NamedStoredProcedureQuery annotation) {
this.registeredName = annotation.name();
this.procedureName = annotation.procedureName();
this.hints = new QueryHintDefinition( registeredName, annotation.hints() ).getHintsMap();
this.resultClasses = annotation.resultClasses();
this.resultSetMappings = annotation.resultSetMappings();
public NamedProcedureCallDefinitionImpl(AnnotationUsage<NamedStoredProcedureQuery> annotation) {
this.registeredName = annotation.getString( "name" );
this.procedureName = annotation.getString( "procedureName" );
this.hints = new QueryHintDefinition( registeredName, annotation.getList( "hints" ) ).getHintsMap();
this.parameterDefinitions = new ParameterDefinitions( annotation.parameters() );
this.resultClasses = interpretResultClasses( annotation );
this.resultSetMappings = interpretResultMappings( annotation );
this.parameterDefinitions = new ParameterDefinitions( annotation.getList( "parameters" ) );
final boolean specifiesResultClasses = resultClasses != null && resultClasses.length > 0;
final boolean specifiesResultSetMappings = resultSetMappings != null && resultSetMappings.length > 0;
@ -69,6 +73,30 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
}
}
private Class<?>[] interpretResultClasses(AnnotationUsage<NamedStoredProcedureQuery> annotation) {
final List<ClassDetails> resultClassDetails = annotation.getList( "resultClasses" );
if ( resultClassDetails == null ) {
return null;
}
final Class<?>[] resultClasses = new Class<?>[resultClassDetails.size()];
for ( int i = 0; i < resultClassDetails.size(); i++ ) {
resultClasses[i] = resultClassDetails.get( i ).toJavaClass();
}
return resultClasses;
}
private String[] interpretResultMappings(AnnotationUsage<NamedStoredProcedureQuery> annotation) {
final List<String> list = annotation.getList( "resultSetMappings" );
if ( list == null ) {
return null;
}
final String[] strings = new String[list.size()];
for ( int i = 0; i < list.size(); i++ ) {
strings[i] = list.get( i );
}
return strings;
}
@Override
public String getRegistrationName() {
return registeredName;
@ -136,20 +164,22 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
private final ParameterStrategy parameterStrategy;
private final ParameterDefinition<?>[] parameterDefinitions;
ParameterDefinitions(StoredProcedureParameter[] parameters) {
if ( parameters == null || parameters.length == 0 ) {
ParameterDefinitions(List<AnnotationUsage<StoredProcedureParameter>> parameters) {
if ( CollectionHelper.isEmpty( parameters ) ) {
parameterStrategy = ParameterStrategy.POSITIONAL;
parameterDefinitions = new ParameterDefinition[0];
}
else {
parameterStrategy = StringHelper.isNotEmpty( parameters[0].name() )
final AnnotationUsage<StoredProcedureParameter> parameterAnn = parameters.get( 0 );
final boolean firstParameterHasName = StringHelper.isNotEmpty( parameterAnn.findAttributeValue( "name" ) );
parameterStrategy = firstParameterHasName
? ParameterStrategy.NAMED
: ParameterStrategy.POSITIONAL;
parameterDefinitions = new ParameterDefinition[ parameters.length ];
parameterDefinitions = new ParameterDefinition[ parameters.size() ];
for ( int i = 0; i < parameters.length; i++ ) {
for ( int i = 0; i < parameters.size(); i++ ) {
// i+1 for the position because the apis say the numbers are 1-based, not zero
parameterDefinitions[i] = new ParameterDefinition<>(i + 1, parameters[i]);
parameterDefinitions[i] = new ParameterDefinition<>(i + 1, parameters.get( i ));
}
}
}
@ -173,12 +203,11 @@ public class NamedProcedureCallDefinitionImpl implements NamedProcedureCallDefin
private final ParameterMode parameterMode;
private final Class<T> type;
@SuppressWarnings("unchecked")
ParameterDefinition(int position, StoredProcedureParameter annotation) {
ParameterDefinition(int position, AnnotationUsage<StoredProcedureParameter> annotation) {
this.position = position;
this.name = normalize( annotation.name() );
this.parameterMode = annotation.mode();
this.type = (Class<T>) annotation.type();
this.name = normalize( annotation.getString( "name" ) );
this.parameterMode = annotation.getEnum( "mode" );
this.type = annotation.getClassDetails( "type" ).toJavaClass();
}
public ParameterMemento toMemento(SessionFactoryImplementor sessionFactory) {

View File

@ -102,6 +102,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbCustomSqlImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbDatabaseObjectImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbDatabaseObjectScopeImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbDiscriminatorColumnImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbDiscriminatorFormulaImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbElementCollectionImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddableAttributesContainerImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddableImpl;
@ -1207,10 +1208,14 @@ public class HbmXmlTransformer {
entity.getDiscriminatorColumn().setName( hbmClass.getDiscriminator().getColumnAttribute() );
}
else if ( StringHelper.isEmpty( hbmClass.getDiscriminator().getFormulaAttribute() ) ) {
entity.setDiscriminatorFormula( hbmClass.getDiscriminator().getFormulaAttribute() );
final JaxbDiscriminatorFormulaImpl formula = new JaxbDiscriminatorFormulaImpl();
formula.setFragment( hbmClass.getDiscriminator().getFormulaAttribute() );
entity.setDiscriminatorFormula( formula );
}
else if ( StringHelper.isEmpty( hbmClass.getDiscriminator().getFormula() ) ) {
entity.setDiscriminatorFormula( hbmClass.getDiscriminator().getFormulaAttribute().trim() );
final JaxbDiscriminatorFormulaImpl formula = new JaxbDiscriminatorFormulaImpl();
formula.setFragment( hbmClass.getDiscriminator().getFormula().trim() );
entity.setDiscriminatorFormula( formula );
}
else {
entity.setDiscriminatorColumn( new JaxbDiscriminatorColumnImpl() );

View File

@ -89,8 +89,8 @@ public interface JaxbEntity extends JaxbEntityOrMappedSuperclass {
JaxbDiscriminatorColumnImpl getDiscriminatorColumn();
void setDiscriminatorColumn(JaxbDiscriminatorColumnImpl value);
String getDiscriminatorFormula();
void setDiscriminatorFormula(String value);
JaxbDiscriminatorFormulaImpl getDiscriminatorFormula();
void setDiscriminatorFormula(JaxbDiscriminatorFormulaImpl value);
JaxbSequenceGeneratorImpl getSequenceGenerators();

View File

@ -0,0 +1,17 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.jaxb.mapping.spi;
import java.util.List;
/**
* @author Steve Ebersole
*/
public interface JaxbNamedQueryBase {
String getName();
List<? extends JaxbQueryHint> getHints();
}

View File

@ -0,0 +1,26 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.jaxb.mapping.spi;
/**
* Models a named query hint in the JAXB model
*
* @author Steve Ebersole
*/
public interface JaxbQueryHint {
/**
* The hint name.
*
* @see org.hibernate.jpa.AvailableHints
*/
String getName();
/**
* The hint value.
*/
String getValue();
}

View File

@ -7,7 +7,6 @@
package org.hibernate.boot.model;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
@ -17,16 +16,17 @@ import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.Internal;
import org.hibernate.boot.internal.GenerationStrategyInterpreter;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.models.spi.MutableAnnotationUsage;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Index;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.UniqueConstraint;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableMap;
import static org.hibernate.boot.models.JpaAnnotations.TABLE_GENERATOR;
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
/**
@ -150,126 +150,19 @@ public class IdentifierGeneratorDefinition implements Serializable {
private static IdentifierGeneratorDefinition buildTableGeneratorDefinition(String name) {
final Builder builder = new Builder();
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretTableGenerator(
new TableGenerator() {
@Override
public String name() {
return name;
}
@Override
public String table() {
return "";
}
@Override
public int initialValue() {
return 0;
}
@Override
public int allocationSize() {
return 50;
}
@Override
public String catalog() {
return "";
}
@Override
public String schema() {
return "";
}
@Override
public String pkColumnName() {
return "";
}
@Override
public String valueColumnName() {
return "";
}
@Override
public String pkColumnValue() {
return "";
}
@Override
public UniqueConstraint[] uniqueConstraints() {
return new UniqueConstraint[0];
}
@Override
public Index[] indexes() {
return new Index[0];
}
@Override
public String options() {
return "";
}
@Override
public Class<? extends Annotation> annotationType() {
return TableGenerator.class;
}
},
builder
);
final MutableAnnotationUsage<TableGenerator> tableGeneratorUsage = TABLE_GENERATOR.createUsage( null, null );
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretTableGenerator( tableGeneratorUsage, builder );
return builder.build();
}
private static IdentifierGeneratorDefinition buildSequenceGeneratorDefinition(String name) {
final Builder builder = new Builder();
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretSequenceGenerator(
new SequenceGenerator() {
@Override
public String name() {
return name;
}
@Override
public String sequenceName() {
return "";
}
@Override
public String catalog() {
return "";
}
@Override
public String schema() {
return "";
}
@Override
public int initialValue() {
return 1;
}
@Override
public int allocationSize() {
return 50;
}
@Override
public String options() {
return "";
}
@Override
public Class<? extends Annotation> annotationType() {
return SequenceGenerator.class;
}
},
builder
final MutableAnnotationUsage<SequenceGenerator> sequenceGeneratorUsage = JpaAnnotations.SEQUENCE_GENERATOR.createUsage(
null,
null
);
sequenceGeneratorUsage.setAttributeValue( "name", name );
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretSequenceGenerator( sequenceGeneratorUsage, builder );
return builder.build();
}

View File

@ -17,7 +17,6 @@ import java.util.function.Function;
import org.hibernate.AnnotationException;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterAutoApplyHandler;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
@ -25,6 +24,7 @@ import org.hibernate.boot.model.convert.spi.RegisteredConversion;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.spi.MemberDetails;
import org.jboss.logging.Logger;
@ -161,12 +161,13 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
@Override
public ConverterDescriptor findAutoApplyConverterForAttribute(
XProperty property,
MemberDetails attributeMember,
MetadataBuildingContext context) {
return locateMatchingConverter(
property,
attributeMember,
ConversionSite.ATTRIBUTE,
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForAttribute( property, context ),
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForAttribute(
attributeMember, context ),
context
);
}
@ -174,13 +175,13 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
private static final StringHelper.Renderer<ConverterDescriptor> RENDERER = value -> value.getAttributeConverterClass().getName();
private ConverterDescriptor locateMatchingConverter(
XProperty xProperty,
MemberDetails memberDetails,
ConversionSite conversionSite,
Function<AutoApplicableConverterDescriptor, ConverterDescriptor> matcher,
MetadataBuildingContext context) {
if ( registeredConversionsByDomainType != null ) {
// we had registered conversions - see if any of them match and, if so, use that conversion
final ResolvedType resolveAttributeType = resolveAttributeType( xProperty, context );
final ResolvedType resolveAttributeType = resolveAttributeType( memberDetails, context );
final RegisteredConversion registrationForDomainType =
registeredConversionsByDomainType.get( resolveAttributeType.getErasedType() );
if ( registrationForDomainType != null ) {
@ -197,9 +198,9 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
descriptor.getAttributeConverterClass().getName(),
descriptor.getDomainValueResolvedType().getSignature(),
conversionSite.getSiteDescriptor(),
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
xProperty.getType().getName()
memberDetails.getDeclaringType().getName(),
memberDetails.getName(),
memberDetails.getType().getName()
);
}
@ -229,8 +230,8 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
Locale.ROOT,
"Multiple auto-apply converters matched %s [%s.%s] : %s",
conversionSite.getSiteDescriptor(),
xProperty.getDeclaringClass().getName(),
xProperty.getName(),
memberDetails.getDeclaringType().getName(),
memberDetails.getName(),
StringHelper.join( matches, RENDERER )
)
);
@ -238,24 +239,25 @@ public class AttributeConverterManager implements ConverterAutoApplyHandler {
@Override
public ConverterDescriptor findAutoApplyConverterForCollectionElement(
XProperty property,
MemberDetails attributeMember,
MetadataBuildingContext context) {
return locateMatchingConverter(
property,
attributeMember,
ConversionSite.COLLECTION_ELEMENT,
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForCollectionElement( property, context ),
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForCollectionElement(
attributeMember, context ),
context
);
}
@Override
public ConverterDescriptor findAutoApplyConverterForMapKey(
XProperty property,
MemberDetails attributeMember,
MetadataBuildingContext context) {
return locateMatchingConverter(
property,
attributeMember,
ConversionSite.MAP_KEY,
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForMapKey( property, context ),
(autoApplyDescriptor) -> autoApplyDescriptor.getAutoAppliedConverterDescriptorForMapKey( attributeMember, context ),
context
);
}

View File

@ -6,10 +6,10 @@
*/
package org.hibernate.boot.model.convert.internal;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.models.spi.MemberDetails;
/**
* An implementation of AutoApplicableConverterDescriptor that always reports
@ -28,21 +28,21 @@ public class AutoApplicableConverterDescriptorBypassedImpl implements AutoApplic
@Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(
XProperty xProperty,
MemberDetails memberDetails,
MetadataBuildingContext context) {
return null;
}
@Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(
XProperty xProperty,
MemberDetails memberDetails,
MetadataBuildingContext context) {
return null;
}
@Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey(
XProperty xProperty,
MemberDetails memberDetails,
MetadataBuildingContext context) {
return null;
}

View File

@ -15,6 +15,7 @@ import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.convert.spi.AutoApplicableConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.models.spi.MemberDetails;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.members.ResolvedMember;
@ -37,9 +38,9 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
@Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(
XProperty xProperty,
MemberDetails memberDetails,
MetadataBuildingContext context) {
final ResolvedType attributeType = resolveAttributeType( xProperty, context );
final ResolvedType attributeType = resolveAttributeType( memberDetails, context );
return typesMatch( linkedConverterDescriptor.getDomainValueResolvedType(), attributeType )
? linkedConverterDescriptor
@ -48,9 +49,9 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
@Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(
XProperty xProperty,
MemberDetails memberDetails,
MetadataBuildingContext context) {
final ResolvedMember<?> collectionMember = resolveMember( xProperty, context );
final ResolvedMember<?> collectionMember = resolveMember( memberDetails, context );
final ResolvedType elementType;
Class<?> erasedType = collectionMember.getType().getErasedType();
@ -82,10 +83,10 @@ public class AutoApplicableConverterDescriptorStandardImpl implements AutoApplic
@Override
public ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey(
XProperty xProperty,
MemberDetails memberDetails,
MetadataBuildingContext context) {
final ResolvedMember<?> collectionMember = resolveMember( xProperty, context );
final ResolvedMember<?> collectionMember = resolveMember( memberDetails, context );
final ResolvedType keyType;
if ( Map.class.isAssignableFrom( collectionMember.getType().getErasedType() ) ) {

View File

@ -14,13 +14,11 @@ import java.util.List;
import org.hibernate.AnnotationException;
import org.hibernate.HibernateException;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.internal.ClassmateContext;
import org.hibernate.boot.model.internal.HCANNHelper;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.util.GenericsHelper;
import org.hibernate.internal.util.type.PrimitiveWrapperHelper;
import org.hibernate.models.spi.MemberDetails;
import com.fasterxml.classmate.ResolvedType;
import com.fasterxml.classmate.ResolvedTypeWithMembers;
@ -37,24 +35,21 @@ public class ConverterHelper {
return GenericsHelper.extractParameterizedType( base );
}
public static ResolvedType resolveAttributeType(XProperty xProperty, MetadataBuildingContext context) {
return resolveMember( xProperty, context ).getType();
public static ResolvedType resolveAttributeType(MemberDetails memberDetails, MetadataBuildingContext context) {
return resolveMember( memberDetails, context ).getType();
}
public static ResolvedMember<? extends Member> resolveMember(XProperty xProperty, MetadataBuildingContext buildingContext) {
public static ResolvedMember<? extends Member> resolveMember(MemberDetails memberDetails, MetadataBuildingContext buildingContext) {
final ClassmateContext classmateContext = buildingContext.getBootstrapContext().getClassmateContext();
final ReflectionManager reflectionManager = buildingContext.getBootstrapContext().getReflectionManager();
final ResolvedType declaringClassType = classmateContext.getTypeResolver().resolve(
reflectionManager.toClass( xProperty.getDeclaringClass() )
);
final ResolvedType declaringClassType = classmateContext.getTypeResolver().resolve( memberDetails.getDeclaringType().toJavaClass() );
final ResolvedTypeWithMembers declaringClassWithMembers = classmateContext.getMemberResolver().resolve(
declaringClassType,
null,
null
);
final Member member = toMember( xProperty );
final Member member = memberDetails.toJavaMember();
if ( member instanceof Method ) {
for ( ResolvedMethod resolvedMember : declaringClassWithMembers.getMemberMethods() ) {
if ( resolvedMember.getName().equals( member.getName() ) ) {
@ -78,18 +73,6 @@ public class ConverterHelper {
);
}
public static Member toMember(XProperty xProperty) {
try {
return HCANNHelper.getUnderlyingMember( xProperty );
}
catch (Exception e) {
throw new HibernateException(
"Could not resolve member signature from XProperty reference",
e
);
}
}
public static List<ResolvedType> resolveConverterClassParamTypes(
Class<? extends AttributeConverter<?, ?>> converterClass,
ClassmateContext context) {

View File

@ -6,8 +6,8 @@
*/
package org.hibernate.boot.model.convert.spi;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.models.spi.MemberDetails;
/**
* Contract for handling auto-apply checks for JPA AttributeConverters
@ -15,7 +15,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
* @author Steve Ebersole
*/
public interface AutoApplicableConverterDescriptor {
ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(XProperty xProperty, MetadataBuildingContext context);
ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(XProperty xProperty, MetadataBuildingContext context);
ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey(XProperty xProperty, MetadataBuildingContext context);
ConverterDescriptor getAutoAppliedConverterDescriptorForAttribute(MemberDetails memberDetails, MetadataBuildingContext context);
ConverterDescriptor getAutoAppliedConverterDescriptorForCollectionElement(MemberDetails memberDetails, MetadataBuildingContext context);
ConverterDescriptor getAutoAppliedConverterDescriptorForMapKey(MemberDetails memberDetails, MetadataBuildingContext context);
}

View File

@ -7,8 +7,8 @@
package org.hibernate.boot.model.convert.spi;
import org.hibernate.Incubating;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Converter;
@ -27,25 +27,25 @@ public interface ConverterAutoApplyHandler {
* by the passed property descriptor. {@code null} indicates that no auto-applied
* converter matched
*
* @param property The HCANN descriptor for the basic attribute
* @param attributeMember The HCANN descriptor for the basic attribute
*/
ConverterDescriptor findAutoApplyConverterForAttribute(XProperty property, MetadataBuildingContext context);
ConverterDescriptor findAutoApplyConverterForAttribute(MemberDetails attributeMember, MetadataBuildingContext context);
/**
* Resolve the auto-applied converter to be applied to the elements of a plural attribute
* described by the passed property descriptor. {@code null} indicates that no auto-applied
* converter matched
*
* @param property The HCANN descriptor for the plural attribute
* @param attributeMember The HCANN descriptor for the plural attribute
*/
ConverterDescriptor findAutoApplyConverterForCollectionElement(XProperty property, MetadataBuildingContext context);
ConverterDescriptor findAutoApplyConverterForCollectionElement(MemberDetails attributeMember, MetadataBuildingContext context);
/**
* Resolve the auto-applied converter to be applied to the keys of a plural Map attribute
* described by the passed property descriptor. {@code null} indicates that no auto-applied
* converter matched
*
* @param property The HCANN descriptor for the Map-typed plural attribute
* @param attributeMember The HCANN descriptor for the Map-typed plural attribute
*/
ConverterDescriptor findAutoApplyConverterForMapKey(XProperty property, MetadataBuildingContext context);
ConverterDescriptor findAutoApplyConverterForMapKey(MemberDetails attributeMember, MetadataBuildingContext context);
}

View File

@ -15,27 +15,28 @@ import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.ColumnTransformers;
import org.hibernate.annotations.TimeZoneColumn;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitBasicColumnNameSource;
import org.hibernate.boot.model.source.spi.AttributePath;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.util.StringHelper;
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.MemberDetails;
import org.hibernate.usertype.internal.AbstractTimeZoneStorageCompositeUserType;
import org.hibernate.usertype.internal.OffsetTimeCompositeUserType;
import org.jboss.logging.Logger;
import jakarta.persistence.AssociationOverride;
import jakarta.persistence.AssociationOverrides;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.CheckConstraint;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
@ -55,25 +56,27 @@ import static org.hibernate.internal.util.StringHelper.qualify;
public abstract class AbstractPropertyHolder implements PropertyHolder {
private static final Logger log = CoreLogging.logger( AbstractPropertyHolder.class );
protected AbstractPropertyHolder parent;
private Map<String, Column[]> holderColumnOverride;
private Map<String, Column[]> currentPropertyColumnOverride;
private Map<String, ColumnTransformer> holderColumnTransformerOverride;
private Map<String, ColumnTransformer> currentPropertyColumnTransformerOverride;
private Map<String, JoinColumn[]> holderJoinColumnOverride;
private Map<String, JoinColumn[]> currentPropertyJoinColumnOverride;
private Map<String, JoinTable> holderJoinTableOverride;
private Map<String, JoinTable> currentPropertyJoinTableOverride;
private Map<String, ForeignKey> holderForeignKeyOverride;
private Map<String, ForeignKey> currentPropertyForeignKeyOverride;
private final String path;
protected final AbstractPropertyHolder parent;
private final MetadataBuildingContext context;
private Boolean isInIdClass;
private Map<String, List<AnnotationUsage<Column>>> holderColumnOverride;
private Map<String, List<AnnotationUsage<Column>>> currentPropertyColumnOverride;
private Map<String, AnnotationUsage<ColumnTransformer>> holderColumnTransformerOverride;
private Map<String, AnnotationUsage<ColumnTransformer>> currentPropertyColumnTransformerOverride;
private Map<String, List<AnnotationUsage<JoinColumn>>> holderJoinColumnOverride;
private Map<String, List<AnnotationUsage<JoinColumn>>> currentPropertyJoinColumnOverride;
private Map<String, AnnotationUsage<JoinTable>> holderJoinTableOverride;
private Map<String, AnnotationUsage<JoinTable>> currentPropertyJoinTableOverride;
private Map<String, AnnotationUsage<ForeignKey>> holderForeignKeyOverride;
private Map<String, AnnotationUsage<ForeignKey>> currentPropertyForeignKeyOverride;
AbstractPropertyHolder(
String path,
PropertyHolder parent,
XClass clazzToProcess,
ClassDetails clazzToProcess,
MetadataBuildingContext context) {
this.path = path;
this.parent = (AbstractPropertyHolder) parent;
@ -84,12 +87,12 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
protected abstract String normalizeCompositePathForLogging(String attributeName);
protected abstract String normalizeCompositePath(String attributeName);
protected abstract AttributeConversionInfo locateAttributeConversionInfo(XProperty property);
protected abstract AttributeConversionInfo locateAttributeConversionInfo(MemberDetails attributeMember);
protected abstract AttributeConversionInfo locateAttributeConversionInfo(String path);
@Override
public ConverterDescriptor resolveAttributeConverterDescriptor(XProperty property) {
AttributeConversionInfo info = locateAttributeConversionInfo( property );
public ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails attributeMember) {
AttributeConversionInfo info = locateAttributeConversionInfo( attributeMember );
if ( info != null ) {
if ( info.isConversionDisabled() ) {
return null;
@ -104,12 +107,12 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
}
}
log.debugf( "Attempting to locate auto-apply AttributeConverter for property [%s:%s]", path, property.getName() );
log.debugf( "Attempting to locate auto-apply AttributeConverter for attributeMember [%s:%s]", path, attributeMember.getName() );
return context.getMetadataCollector()
.getConverterRegistry()
.getAttributeConverterAutoApplyHandler()
.findAutoApplyConverterForAttribute( property, context );
.findAutoApplyConverterForAttribute( attributeMember, context );
}
protected IllegalStateException buildExceptionFromInstantiationError(AttributeConversionInfo info, Exception e) {
@ -179,10 +182,12 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
/**
* Set the property to be processed. property can be null
*
* @param property The property
* @param attributeMember The property
*/
protected void setCurrentProperty(XProperty property) {
if ( property == null ) {
protected void setCurrentProperty(MemberDetails attributeMember) {
// todo (jpa32) : some of this (association override handling esp) does the same work multiple times - consolidate
if ( attributeMember == null ) {
this.currentPropertyColumnOverride = null;
this.currentPropertyColumnTransformerOverride = null;
this.currentPropertyJoinColumnOverride = null;
@ -190,27 +195,27 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
this.currentPropertyForeignKeyOverride = null;
}
else {
this.currentPropertyColumnOverride = buildColumnOverride( property, getPath(), context );
this.currentPropertyColumnOverride = buildColumnOverride( attributeMember, getPath(), context );
if ( this.currentPropertyColumnOverride.isEmpty() ) {
this.currentPropertyColumnOverride = null;
}
this.currentPropertyColumnTransformerOverride = buildColumnTransformerOverride( property );
this.currentPropertyColumnTransformerOverride = buildColumnTransformerOverride( attributeMember );
if ( this.currentPropertyColumnTransformerOverride.isEmpty() ) {
this.currentPropertyColumnTransformerOverride = null;
}
this.currentPropertyJoinColumnOverride = buildJoinColumnOverride( property, getPath() );
this.currentPropertyJoinColumnOverride = buildJoinColumnOverride( attributeMember, getPath() );
if ( this.currentPropertyJoinColumnOverride.isEmpty() ) {
this.currentPropertyJoinColumnOverride = null;
}
this.currentPropertyJoinTableOverride = buildJoinTableOverride( property, getPath() );
this.currentPropertyJoinTableOverride = buildJoinTableOverride( attributeMember, getPath() );
if ( this.currentPropertyJoinTableOverride.isEmpty() ) {
this.currentPropertyJoinTableOverride = null;
}
this.currentPropertyForeignKeyOverride = buildForeignKeyOverride( property, getPath() );
this.currentPropertyForeignKeyOverride = buildForeignKeyOverride( attributeMember, getPath() );
if ( this.currentPropertyForeignKeyOverride.isEmpty() ) {
this.currentPropertyForeignKeyOverride = null;
}
@ -224,46 +229,55 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
* These rules are here to support both JPA 2 and legacy overriding rules.
*/
@Override
public Column[] getOverriddenColumn(String propertyName) {
Column[] result = getExactOverriddenColumn( propertyName );
if ( result == null && propertyName.contains(".collection&&element.") ) {
//support for non map collections where no prefix is needed
result = getExactOverriddenColumn( propertyName.replace(".collection&&element.", ".") );
public List<AnnotationUsage<Column>> getOverriddenColumn(String propertyName) {
final List<AnnotationUsage<Column>> result = getExactOverriddenColumn( propertyName );
if ( result == null ) {
if ( propertyName.contains( ".collection&&element." ) ) {
//support for non map collections where no prefix is needed
//TODO cache the underlying regexp
return getExactOverriddenColumn( propertyName.replace( ".collection&&element.", "." ) );
}
}
return result;
}
@Override
public ColumnTransformer getOverriddenColumnTransformer(String logicalColumnName) {
ColumnTransformer override = null;
public AnnotationUsage<ColumnTransformer> getOverriddenColumnTransformer(String logicalColumnName) {
AnnotationUsage<ColumnTransformer> result = null;
if ( parent != null ) {
override = parent.getOverriddenColumnTransformer( logicalColumnName );
result = parent.getOverriddenColumnTransformer( logicalColumnName );
}
if ( override == null && currentPropertyColumnTransformerOverride != null ) {
override = currentPropertyColumnTransformerOverride.get( logicalColumnName );
if ( result == null && currentPropertyColumnTransformerOverride != null ) {
result = currentPropertyColumnTransformerOverride.get( logicalColumnName );
}
if ( override == null && holderColumnTransformerOverride != null ) {
override = holderColumnTransformerOverride.get( logicalColumnName );
if ( result == null && holderColumnTransformerOverride != null ) {
result = holderColumnTransformerOverride.get( logicalColumnName );
}
return override;
return result;
}
/**
* Get column overriding, property first, then parent, then holder
* find the overridden rules from the exact property name.
*/
private Column[] getExactOverriddenColumn(String propertyName) {
Column[] override = null;
private List<AnnotationUsage<Column>> getExactOverriddenColumn(String propertyName) {
List<AnnotationUsage<Column>> result = null;
if ( parent != null ) {
override = parent.getExactOverriddenColumn( propertyName );
result = parent.getExactOverriddenColumn( propertyName );
}
if ( override == null && currentPropertyColumnOverride != null ) {
override = currentPropertyColumnOverride.get( propertyName );
if ( result == null && currentPropertyColumnOverride != null ) {
result = currentPropertyColumnOverride.get( propertyName );
}
if ( override == null && holderColumnOverride != null ) {
override = holderColumnOverride.get( propertyName );
if ( result == null && holderColumnOverride != null ) {
result = holderColumnOverride.get( propertyName );
}
return override;
return result;
}
/**
@ -273,12 +287,12 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
* These rules are here to support both JPA 2 and legacy overriding rules.
*/
@Override
public JoinColumn[] getOverriddenJoinColumn(String propertyName) {
JoinColumn[] result = getExactOverriddenJoinColumn( propertyName );
public List<AnnotationUsage<JoinColumn>> getOverriddenJoinColumn(String propertyName) {
final List<AnnotationUsage<JoinColumn>> result = getExactOverriddenJoinColumn( propertyName );
if ( result == null && propertyName.contains( ".collection&&element." ) ) {
//support for non map collections where no prefix is needed
//TODO cache the underlying regexp
result = getExactOverriddenJoinColumn( propertyName.replace( ".collection&&element.", "." ) );
return getExactOverriddenJoinColumn( propertyName.replace( ".collection&&element.", "." ) );
}
return result;
}
@ -286,57 +300,60 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
/**
* Get column overriding, property first, then parent, then holder
*/
private JoinColumn[] getExactOverriddenJoinColumn(String propertyName) {
JoinColumn[] override = null;
private List<AnnotationUsage<JoinColumn>> getExactOverriddenJoinColumn(String propertyName) {
List<AnnotationUsage<JoinColumn>> result = null;
if ( parent != null ) {
override = parent.getExactOverriddenJoinColumn( propertyName );
result = parent.getExactOverriddenJoinColumn( propertyName );
}
if ( override == null && currentPropertyJoinColumnOverride != null ) {
override = currentPropertyJoinColumnOverride.get( propertyName );
if ( result == null && currentPropertyJoinColumnOverride != null ) {
result = currentPropertyJoinColumnOverride.get( propertyName );
}
if ( override == null && holderJoinColumnOverride != null ) {
override = holderJoinColumnOverride.get( propertyName );
if ( result == null && holderJoinColumnOverride != null ) {
result = holderJoinColumnOverride.get( propertyName );
}
return override;
return result;
}
@Override
public ForeignKey getOverriddenForeignKey(String propertyName) {
ForeignKey result = getExactOverriddenForeignKey( propertyName );
public AnnotationUsage<ForeignKey> getOverriddenForeignKey(String propertyName) {
final AnnotationUsage<ForeignKey> result = getExactOverriddenForeignKey( propertyName );
if ( result == null && propertyName.contains( ".collection&&element." ) ) {
//support for non map collections where no prefix is needed
//TODO cache the underlying regexp
result = getExactOverriddenForeignKey( propertyName.replace( ".collection&&element.", "." ) );
return getExactOverriddenForeignKey( propertyName.replace( ".collection&&element.", "." ) );
}
return result;
}
private ForeignKey getExactOverriddenForeignKey(String propertyName) {
ForeignKey override = null;
private AnnotationUsage<ForeignKey> getExactOverriddenForeignKey(String propertyName) {
AnnotationUsage<ForeignKey> result = null;
if ( parent != null ) {
override = parent.getExactOverriddenForeignKey( propertyName );
result = parent.getExactOverriddenForeignKey( propertyName );
}
if ( override == null && currentPropertyForeignKeyOverride != null ) {
override = currentPropertyForeignKeyOverride.get( propertyName );
if ( result == null && currentPropertyForeignKeyOverride != null ) {
result = currentPropertyForeignKeyOverride.get( propertyName );
}
if ( override == null && holderForeignKeyOverride != null ) {
override = holderForeignKeyOverride.get( propertyName );
if ( result == null && holderForeignKeyOverride != null ) {
result = holderForeignKeyOverride.get( propertyName );
}
return override;
return result;
}
/**
* Get column overriding, property first, then parent, then holder
* replace the placeholder 'collection&amp;&amp;element' with nothing
*
* <p>
* These rules are here to support both JPA 2 and legacy overriding rules.
*/
@Override
public JoinTable getJoinTable(XProperty property) {
final String propertyName = qualify( getPath(), property.getName() );
JoinTable result = getOverriddenJoinTable( propertyName );
if (result == null) {
result = property.getAnnotation( JoinTable.class );
public AnnotationUsage<JoinTable> getJoinTable(MemberDetails attributeMember) {
final String propertyName = qualify( getPath(), attributeMember.getName() );
final AnnotationUsage<JoinTable> result = getOverriddenJoinTable( propertyName );
if ( result == null ) {
return attributeMember.getAnnotationUsage( JoinTable.class );
}
return result;
}
@ -347,12 +364,12 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
*
* These rules are here to support both JPA 2 and legacy overriding rules.
*/
public JoinTable getOverriddenJoinTable(String propertyName) {
JoinTable result = getExactOverriddenJoinTable( propertyName );
public AnnotationUsage<JoinTable> getOverriddenJoinTable(String propertyName) {
final AnnotationUsage<JoinTable> result = getExactOverriddenJoinTable( propertyName );
if ( result == null && propertyName.contains( ".collection&&element." ) ) {
//support for non map collections where no prefix is needed
//TODO cache the underlying regexp
result = getExactOverriddenJoinTable( propertyName.replace( ".collection&&element.", "." ) );
return getExactOverriddenJoinTable( propertyName.replace( ".collection&&element.", "." ) );
}
return result;
}
@ -360,8 +377,8 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
/**
* Get column overriding, property first, then parent, then holder
*/
private JoinTable getExactOverriddenJoinTable(String propertyName) {
JoinTable override = null;
private AnnotationUsage<JoinTable> getExactOverriddenJoinTable(String propertyName) {
AnnotationUsage<JoinTable> override = null;
if ( parent != null ) {
override = parent.getExactOverriddenJoinTable( propertyName );
}
@ -374,24 +391,23 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
return override;
}
private void buildHierarchyColumnOverride(XClass element) {
XClass current = element;
Map<String, Column[]> columnOverride = new HashMap<>();
Map<String, ColumnTransformer> columnTransformerOverride = new HashMap<>();
Map<String, JoinColumn[]> joinColumnOverride = new HashMap<>();
Map<String, JoinTable> joinTableOverride = new HashMap<>();
Map<String, ForeignKey> foreignKeyOverride = new HashMap<>();
XClass objectClass = context.getBootstrapContext().getReflectionManager().toXClass(Object.class);
while ( current != null && !objectClass.equals( current ) ) {
if ( current.isAnnotationPresent( Entity.class )
|| current.isAnnotationPresent( MappedSuperclass.class )
|| current.isAnnotationPresent( Embeddable.class ) ) {
private void buildHierarchyColumnOverride(ClassDetails element) {
ClassDetails current = element;
Map<String, List<AnnotationUsage<Column>>> columnOverride = new HashMap<>();
Map<String, AnnotationUsage<ColumnTransformer>> columnTransformerOverride = new HashMap<>();
Map<String, List<AnnotationUsage<JoinColumn>>> joinColumnOverride = new HashMap<>();
Map<String, AnnotationUsage<JoinTable>> joinTableOverride = new HashMap<>();
Map<String, AnnotationUsage<ForeignKey>> foreignKeyOverride = new HashMap<>();
while ( current != null && !ClassDetails.OBJECT_CLASS_DETAILS.equals( current ) ) {
if ( current.hasAnnotationUsage( Entity.class )
|| current.hasAnnotationUsage( MappedSuperclass.class )
|| current.hasAnnotationUsage( Embeddable.class ) ) {
//FIXME is embeddable override?
Map<String, Column[]> currentOverride = buildColumnOverride( current, getPath(), context );
Map<String, ColumnTransformer> currentTransformerOverride = buildColumnTransformerOverride( current );
Map<String, JoinColumn[]> currentJoinOverride = buildJoinColumnOverride( current, getPath() );
Map<String, JoinTable> currentJoinTableOverride = buildJoinTableOverride( current, getPath() );
Map<String, ForeignKey> currentForeignKeyOverride = buildForeignKeyOverride( current, getPath() );
Map<String, List<AnnotationUsage<Column>>> currentOverride = buildColumnOverride( current, getPath(), context );
Map<String, AnnotationUsage<ColumnTransformer>> currentTransformerOverride = buildColumnTransformerOverride( current );
Map<String, List<AnnotationUsage<JoinColumn>>> currentJoinOverride = buildJoinColumnOverride( current, getPath() );
Map<String, AnnotationUsage<JoinTable>> currentJoinTableOverride = buildJoinTableOverride( current, getPath() );
Map<String, AnnotationUsage<ForeignKey>> currentForeignKeyOverride = buildForeignKeyOverride( current, getPath() );
currentOverride.putAll( columnOverride ); //subclasses have precedence over superclasses
currentTransformerOverride.putAll( columnTransformerOverride ); //subclasses have precedence over superclasses
currentJoinOverride.putAll( joinColumnOverride ); //subclasses have precedence over superclasses
@ -403,7 +419,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
joinTableOverride = currentJoinTableOverride;
foreignKeyOverride = currentForeignKeyOverride;
}
current = current.getSuperclass();
current = current.getSuperClass();
}
holderColumnOverride = !columnOverride.isEmpty() ? columnOverride : null;
@ -413,109 +429,102 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
holderForeignKeyOverride = !foreignKeyOverride.isEmpty() ? foreignKeyOverride : null;
}
private static Map<String, Column[]> buildColumnOverride(
XAnnotatedElement element,
private static Map<String, List<AnnotationUsage<Column>>> buildColumnOverride(
AnnotationTarget element,
String path,
MetadataBuildingContext context) {
final Map<String, Column[]> columnOverride = new HashMap<>();
if ( element != null ) {
AttributeOverride singleOverride = element.getAnnotation( AttributeOverride.class );
AttributeOverrides multipleOverrides = element.getAnnotation( AttributeOverrides.class );
AttributeOverride[] overrides;
if ( singleOverride != null ) {
overrides = new AttributeOverride[]{ singleOverride };
}
else if ( multipleOverrides != null ) {
overrides = multipleOverrides.value();
}
else {
overrides = null;
}
final Map<String, List<AnnotationUsage<Column>>> columnOverrideMap = new HashMap<>();
if ( element == null ) {
return columnOverrideMap;
}
if ( overrides != null ) {
final Map<String, List<Column>> columnOverrideList = new HashMap<>();
final List<AnnotationUsage<AttributeOverride>> overrides = element.getRepeatedAnnotationUsages( AttributeOverride.class );
if ( CollectionHelper.isNotEmpty( overrides ) ) {
final Map<String, List<AnnotationUsage<Column>>> columnOverrideList = new HashMap<>();
for ( AnnotationUsage<AttributeOverride> depAttr : overrides ) {
final String qualifiedName = StringHelper.qualify( path, depAttr.getString( "name" ) );
final AnnotationUsage<Column> column = depAttr.getNestedUsage( "column" );
for ( AttributeOverride depAttr : overrides ) {
final String qualifiedName = qualify( path, depAttr.name() );
if ( columnOverrideList.containsKey( qualifiedName ) ) {
columnOverrideList.get( qualifiedName ).add( depAttr.column() );
}
else {
List<Column> list = new ArrayList<>();
list.add( depAttr.column() );
columnOverrideList.put( qualifiedName, list );
}
}
for ( Map.Entry<String, List<Column>> entry : columnOverrideList.entrySet() ) {
columnOverride.put( entry.getKey(), entry.getValue().toArray( new Column[0] ) );
}
}
else if ( useColumnForTimeZoneStorage( element, context ) ) {
final Column column = createTemporalColumn( element, path, context );
if ( isOffsetTimeClass( element ) ) {
columnOverride.put(
path + "." + OffsetTimeCompositeUserType.LOCAL_TIME_NAME,
new Column[] { column }
);
if ( columnOverrideList.containsKey( qualifiedName ) ) {
// already an entry, just add to that List
columnOverrideList.get( qualifiedName ).add( column );
}
else {
columnOverride.put(
path + "." + AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME,
new Column[] { column }
);
// not yet an entry, create the list and add
final List<AnnotationUsage<Column>> list = new ArrayList<>();
list.add( column );
columnOverrideList.put( qualifiedName, list );
}
final Column offsetColumn = createTimeZoneColumn( element, column );
columnOverride.put(
path + "." + AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME,
new Column[]{ offsetColumn }
}
}
else if ( useColumnForTimeZoneStorage( element, context ) ) {
final AnnotationUsage<Column> column = createTemporalColumn( element, path, context );
if ( isOffsetTimeClass( element ) ) {
columnOverrideMap.put(
path + "." + OffsetTimeCompositeUserType.LOCAL_TIME_NAME,
List.of( column )
);
}
}
return columnOverride;
}
private static Column createTimeZoneColumn(XAnnotatedElement element, Column column) {
final TimeZoneColumn timeZoneColumn = element.getAnnotation( TimeZoneColumn.class );
if ( timeZoneColumn != null ) {
return new ColumnImpl(
timeZoneColumn.name(),
false,
column.nullable(),
timeZoneColumn.insertable(),
timeZoneColumn.updatable(),
timeZoneColumn.columnDefinition(),
timeZoneColumn.table(),
0
);
}
else {
return new ColumnImpl(
column.name() + "_tz",
false,
column.nullable(),
column.insertable(),
column.updatable(),
"",
column.table(),
0
else {
columnOverrideMap.put(
path + "." + AbstractTimeZoneStorageCompositeUserType.INSTANT_NAME,
List.of( column )
);
}
final AnnotationUsage<Column> offsetColumn = createTimeZoneColumn( element, column, context );
columnOverrideMap.put(
path + "." + AbstractTimeZoneStorageCompositeUserType.ZONE_OFFSET_NAME,
List.of( offsetColumn )
);
}
return columnOverrideMap;
}
private static Column createTemporalColumn(XAnnotatedElement element, String path, MetadataBuildingContext context) {
private static AnnotationUsage<Column> createTimeZoneColumn(
AnnotationTarget element,
AnnotationUsage<Column> column,
MetadataBuildingContext context) {
final AnnotationUsage<TimeZoneColumn> timeZoneColumn = element.getAnnotationUsage( TimeZoneColumn.class );
return JpaAnnotations.COLUMN.createUsage(
element,
(created) -> {
final String columnName = timeZoneColumn != null
? timeZoneColumn.getString( "name" )
: column.getString( "name" ) + "_tz";
created.setAttributeValue( "name", columnName );
created.setAttributeValue( "nullable", column.getBoolean( "nullable" ) );
final AnnotationUsage<?> source = timeZoneColumn != null
? timeZoneColumn
: column;
created.setAttributeValue( "table", source.getAttributeValue( "table" ) );
created.setAttributeValue( "insertable", source.getAttributeValue( "insertable" ) );
created.setAttributeValue( "updatable", source.getAttributeValue( "updatable" ) );
if ( timeZoneColumn != null ) {
created.setAttributeValue( "columnDefinition", timeZoneColumn.getAttributeValue( "columnDefinition" ) );
}
},
context.getMetadataCollector().getSourceModelBuildingContext()
);
}
private static AnnotationUsage<Column> createTemporalColumn(
AnnotationTarget element,
String path,
MetadataBuildingContext context) {
int precision;
final Column annotatedColumn = element.getAnnotation( Column.class );
final AnnotationUsage<Column> annotatedColumn = element.getAnnotationUsage( Column.class );
if ( annotatedColumn != null ) {
if ( !annotatedColumn.name().isEmpty() ) {
if ( StringHelper.isNotEmpty( annotatedColumn.getString( "name" ) ) ) {
return annotatedColumn;
}
precision = annotatedColumn.precision();
precision = annotatedColumn.getInteger( "precision" );
}
else {
precision = 0;
}
// Base the name of the synthetic dateTime field on the name of the original attribute
final Identifier implicitName = context.getObjectNameNormalizer().normalizeIdentifierQuoting(
context.getBuildingOptions().getImplicitNamingStrategy().determineBasicColumnName(
@ -539,108 +548,76 @@ public abstract class AbstractPropertyHolder implements PropertyHolder {
}
)
);
return new ColumnImpl(
implicitName.getText(),
false,
true,
true,
true,
"",
"",
precision
return JpaAnnotations.COLUMN.createUsage(
element,
(created) -> {
created.setAttributeValue( "name", implicitName.getText() );
created.setAttributeValue( "precision", precision );
},
context.getMetadataCollector().getSourceModelBuildingContext()
);
}
private static Map<String, ColumnTransformer> buildColumnTransformerOverride(XAnnotatedElement element) {
Map<String, ColumnTransformer> columnOverride = new HashMap<>();
private static Map<String, AnnotationUsage<ColumnTransformer>> buildColumnTransformerOverride(AnnotationTarget element) {
final Map<String, AnnotationUsage<ColumnTransformer>> columnOverride = new HashMap<>();
if ( element != null ) {
ColumnTransformer singleOverride = element.getAnnotation( ColumnTransformer.class );
ColumnTransformers multipleOverrides = element.getAnnotation( ColumnTransformers.class );
ColumnTransformer[] overrides;
if ( singleOverride != null ) {
overrides = new ColumnTransformer[]{ singleOverride };
}
else if ( multipleOverrides != null ) {
overrides = multipleOverrides.value();
}
else {
overrides = null;
}
element.forEachAnnotationUsage( ColumnTransformer.class, (usage) -> {
columnOverride.put( usage.getString( "forColumn" ), usage );
} );
}
return columnOverride;
}
if ( overrides != null ) {
for ( ColumnTransformer depAttr : overrides ) {
columnOverride.put(
depAttr.forColumn(),
depAttr
);
}
private static Map<String, List<AnnotationUsage<JoinColumn>>> buildJoinColumnOverride(AnnotationTarget element, String path) {
final Map<String, List<AnnotationUsage<JoinColumn>>> columnOverride = new HashMap<>();
if ( element != null ) {
final List<AnnotationUsage<AssociationOverride>> overrides = buildAssociationOverrides( element, path );
for ( AnnotationUsage<AssociationOverride> override : overrides ) {
columnOverride.put(
qualify( path, override.getString( "name" ) ),
override.getList( "joinColumns" )
);
}
}
return columnOverride;
}
private static Map<String, JoinColumn[]> buildJoinColumnOverride(XAnnotatedElement element, String path) {
Map<String, JoinColumn[]> columnOverride = new HashMap<>();
private static Map<String, AnnotationUsage<ForeignKey>> buildForeignKeyOverride(AnnotationTarget element, String path) {
final Map<String, AnnotationUsage<ForeignKey>> foreignKeyOverride = new HashMap<>();
if ( element != null ) {
AssociationOverride[] overrides = buildAssociationOverrides( element, path );
if ( overrides != null ) {
for ( AssociationOverride depAttr : overrides ) {
columnOverride.put(
qualify( path, depAttr.name() ),
depAttr.joinColumns()
);
}
}
}
return columnOverride;
}
private static Map<String, ForeignKey> buildForeignKeyOverride(XAnnotatedElement element, String path) {
Map<String, ForeignKey> foreignKeyOverride = new HashMap<>();
if ( element != null ) {
AssociationOverride[] overrides = buildAssociationOverrides( element, path );
if ( overrides != null ) {
for ( AssociationOverride depAttr : overrides ) {
foreignKeyOverride.put( qualify( path, depAttr.name() ), depAttr.foreignKey() );
}
final List<AnnotationUsage<AssociationOverride>> overrides = buildAssociationOverrides( element, path );
for ( AnnotationUsage<AssociationOverride> override : overrides ) {
foreignKeyOverride.put(
qualify( path, override.getString( "name" ) ),
override.getNestedUsage( "foreignKey" )
);
}
}
return foreignKeyOverride;
}
private static AssociationOverride[] buildAssociationOverrides(XAnnotatedElement element, String path) {
AssociationOverride singleOverride = element.getAnnotation( AssociationOverride.class );
AssociationOverrides pluralOverrides = element.getAnnotation( AssociationOverrides.class );
AssociationOverride[] overrides;
if ( singleOverride != null ) {
overrides = new AssociationOverride[] { singleOverride };
}
else if ( pluralOverrides != null ) {
overrides = pluralOverrides.value();
}
else {
overrides = null;
}
private static List<AnnotationUsage<AssociationOverride>> buildAssociationOverrides(AnnotationTarget element, String path) {
final List<AnnotationUsage<AssociationOverride>> overrides = new ArrayList<>();
element.forEachAnnotationUsage( AssociationOverride.class, overrides::add );
return overrides;
}
private static Map<String, JoinTable> buildJoinTableOverride(XAnnotatedElement element, String path) {
Map<String, JoinTable> tableOverride = new HashMap<>();
private static Map<String, AnnotationUsage<JoinTable>> buildJoinTableOverride(AnnotationTarget element, String path) {
final Map<String, AnnotationUsage<JoinTable>> result = new HashMap<>();
if ( element != null ) {
AssociationOverride[] overrides = buildAssociationOverrides( element, path );
if ( overrides != null ) {
for ( AssociationOverride depAttr : overrides ) {
if ( depAttr.joinColumns().length == 0 ) {
tableOverride.put(
qualify( path, depAttr.name() ),
depAttr.joinTable()
);
}
final List<AnnotationUsage<AssociationOverride>> overrides = buildAssociationOverrides( element, path );
for ( AnnotationUsage<AssociationOverride> override : overrides ) {
final List<AnnotationUsage<JoinColumn>> joinColumns = override.getList( "joinColumns" );
if ( CollectionHelper.isNotEmpty( joinColumns ) ) {
result.put(
qualify( path, override.getString( "name" ) ),
override.getNestedUsage( "joinTable" )
);
}
}
}
return tableOverride;
return result;
}
@Override

View File

@ -10,8 +10,6 @@ import java.util.List;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Struct;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedNameImpl;
@ -22,6 +20,10 @@ import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.descriptor.java.spi.EmbeddableAggregateJavaType;
import org.hibernate.type.spi.TypeConfiguration;
@ -38,14 +40,14 @@ public final class AggregateComponentBinder {
Component component,
PropertyHolder propertyHolder,
PropertyData inferredData,
XClass componentXClass,
ClassDetails returnedClassOrElement,
AnnotatedColumns columns,
MetadataBuildingContext context) {
if ( isAggregate( inferredData.getProperty(), componentXClass ) ) {
if ( isAggregate( inferredData.getAttributeMember(), inferredData.getClassOrElementType(), context ) ) {
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
final TypeConfiguration typeConfiguration = metadataCollector.getTypeConfiguration();
// Determine a struct name if this is a struct through some means
final QualifiedName structQualifiedName = determineStructName( columns, inferredData, componentXClass, context );
final QualifiedName structQualifiedName = determineStructName( columns, inferredData, returnedClassOrElement, context );
final String structName = structQualifiedName == null ? null : structQualifiedName.render();
// We must register a special JavaType for the embeddable which can provide a recommended JdbcType
@ -54,24 +56,24 @@ public final class AggregateComponentBinder {
() -> new EmbeddableAggregateJavaType<>( component.getComponentClass(), structName )
);
component.setStructName( structQualifiedName );
component.setStructColumnNames( determineStructAttributeNames( inferredData, componentXClass ) );
component.setStructColumnNames( determineStructAttributeNames( inferredData, returnedClassOrElement ) );
// Determine the aggregate column
BasicValueBinder basicValueBinder = new BasicValueBinder( BasicValueBinder.Kind.ATTRIBUTE, component, context );
basicValueBinder.setPropertyName( inferredData.getPropertyName() );
basicValueBinder.setReturnedClassName( inferredData.getPropertyClass().getName() );
basicValueBinder.setReturnedClassName( inferredData.getClassOrElementType().getName() );
basicValueBinder.setColumns( columns );
basicValueBinder.setPersistentClassName( propertyHolder.getClassName() );
basicValueBinder.setType(
inferredData.getProperty(),
inferredData.getPropertyClass(),
inferredData.getAttributeMember(),
inferredData.getPropertyType(),
inferredData.getDeclaringClass().getName(),
null
);
final BasicValue propertyValue = basicValueBinder.make();
final AggregateColumn aggregateColumn = (AggregateColumn) propertyValue.getColumn();
if ( structName != null && aggregateColumn.getSqlType() == null ) {
if ( inferredData.getProperty().isArray() || inferredData.getProperty().isCollection() ) {
if ( inferredData.getAttributeMember().isArray() || inferredData.getAttributeMember().isPlural() ) {
aggregateColumn.setSqlTypeCode( getStructPluralSqlTypeCode( context ) );
aggregateColumn.setSqlType(
context.getMetadataCollector()
@ -95,7 +97,7 @@ public final class AggregateComponentBinder {
new AggregateComponentSecondPass(
propertyHolder,
component,
componentXClass,
returnedClassOrElement,
inferredData.getPropertyName(),
context
)
@ -122,17 +124,19 @@ public final class AggregateComponentBinder {
private static QualifiedName determineStructName(
AnnotatedColumns columns,
PropertyData inferredData,
XClass returnedClassOrElement,
ClassDetails returnedClassOrElement,
MetadataBuildingContext context) {
final XProperty property = inferredData.getProperty();
final MemberDetails property = inferredData.getAttributeMember();
if ( property != null ) {
final Struct struct = property.getAnnotation( Struct.class );
final AnnotationUsage<Struct> struct = property.getAnnotationUsage( Struct.class );
if ( struct != null ) {
return toQualifiedName( struct, context );
}
final JdbcTypeCode jdbcTypeCode = property.getAnnotation( JdbcTypeCode.class );
final AnnotationUsage<JdbcTypeCode> jdbcTypeCode = property.getAnnotationUsage( JdbcTypeCode.class );
if ( jdbcTypeCode != null
&& ( jdbcTypeCode.value() == SqlTypes.STRUCT || jdbcTypeCode.value() == SqlTypes.STRUCT_ARRAY || jdbcTypeCode.value() == SqlTypes.STRUCT_TABLE )
&& ( jdbcTypeCode.getInteger( "value" ) == SqlTypes.STRUCT
|| jdbcTypeCode.getInteger( "value" ) == SqlTypes.STRUCT_ARRAY
|| jdbcTypeCode.getInteger( "value" ) == SqlTypes.STRUCT_TABLE )
&& columns != null ) {
final List<AnnotatedColumn> columnList = columns.getColumns();
final String sqlType;
@ -148,60 +152,72 @@ public final class AggregateComponentBinder {
}
}
}
final Struct struct = returnedClassOrElement.getAnnotation( Struct.class );
final AnnotationUsage<Struct> struct = returnedClassOrElement.getAnnotationUsage( Struct.class );
if ( struct != null ) {
return toQualifiedName( struct, context );
}
return null;
}
private static QualifiedName toQualifiedName(Struct struct, MetadataBuildingContext context) {
private static QualifiedName toQualifiedName(AnnotationUsage<Struct> struct, MetadataBuildingContext context) {
final Database database = context.getMetadataCollector().getDatabase();
return new QualifiedNameImpl(
database.toIdentifier( struct.catalog() ),
database.toIdentifier( struct.schema() ),
database.toIdentifier( struct.name() )
database.toIdentifier( struct.getString( "catalog" ) ),
database.toIdentifier( struct.getString( "schema" ) ),
database.toIdentifier( struct.getString( "name" ) )
);
}
private static String[] determineStructAttributeNames(PropertyData inferredData, XClass returnedClassOrElement) {
final XProperty property = inferredData.getProperty();
private static String[] determineStructAttributeNames(PropertyData inferredData, ClassDetails returnedClassOrElement) {
final MemberDetails property = inferredData.getAttributeMember();
if ( property != null ) {
final Struct struct = property.getAnnotation( Struct.class );
final AnnotationUsage<Struct> struct = property.getAnnotationUsage( Struct.class );
if ( struct != null ) {
return struct.attributes();
final List<String> attributes = struct.getList( "attributes" );
return attributes.toArray( new String[0] );
}
}
final Struct struct = returnedClassOrElement.getAnnotation( Struct.class );
final AnnotationUsage<Struct> struct = returnedClassOrElement.getAnnotationUsage( Struct.class );
if ( struct != null ) {
return struct.attributes();
final List<String> attributes = struct.getList( "attributes" );
return attributes.toArray( new String[0] );
}
return null;
}
private static boolean isAggregate(XProperty property, XClass returnedClass) {
private static boolean isAggregate(
MemberDetails property,
TypeDetails returnedClass,
MetadataBuildingContext context) {
if ( property != null ) {
final Struct struct = property.getAnnotation( Struct.class );
if ( struct != null ) {
if ( property.hasAnnotationUsage( Struct.class ) ) {
return true;
}
final JdbcTypeCode jdbcTypeCode = property.getAnnotation( JdbcTypeCode.class );
final AnnotationUsage<JdbcTypeCode> jdbcTypeCode = property.getAnnotationUsage( JdbcTypeCode.class );
if ( jdbcTypeCode != null ) {
switch ( jdbcTypeCode.value() ) {
switch ( jdbcTypeCode.getInteger( "value" ) ) {
case SqlTypes.STRUCT:
case SqlTypes.JSON:
case SqlTypes.SQLXML:
case SqlTypes.STRUCT_ARRAY:
case SqlTypes.STRUCT_TABLE:
case SqlTypes.JSON_ARRAY:
case SqlTypes.XML_ARRAY:
case SqlTypes.XML_ARRAY: {
return true;
}
}
}
}
if ( returnedClass != null ) {
return returnedClass.isAnnotationPresent( Struct.class );
return returnedClass.determineRawClass().hasAnnotationUsage( Struct.class );
}
return false;
}
}

View File

@ -14,7 +14,7 @@ import java.util.TreeSet;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.model.relational.Namespace;
import org.hibernate.boot.model.relational.QualifiedName;
@ -35,6 +35,8 @@ import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.UserDefinedObjectType;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.internal.EmbeddableHelper;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.sql.Template;
import org.hibernate.type.SqlTypes;
import org.hibernate.type.spi.TypeConfiguration;
@ -48,19 +50,19 @@ public class AggregateComponentSecondPass implements SecondPass {
private final PropertyHolder propertyHolder;
private final Component component;
private final XClass componentXClass;
private final ClassDetails returnedClassOrElement;
private final String propertyName;
private final MetadataBuildingContext context;
public AggregateComponentSecondPass(
PropertyHolder propertyHolder,
Component component,
XClass componentXClass,
ClassDetails returnedClassOrElement,
String propertyName,
MetadataBuildingContext context) {
this.propertyHolder = propertyHolder;
this.component = component;
this.componentXClass = componentXClass;
this.returnedClassOrElement = returnedClassOrElement;
this.propertyName = propertyName;
this.context = context;
}
@ -93,9 +95,9 @@ public class AggregateComponentSecondPass implements SecondPass {
structName.getSchemaName()
);
final UserDefinedObjectType udt = new UserDefinedObjectType( "orm", namespace, structName.getObjectName() );
final Comment comment = componentXClass.getAnnotation( Comment.class );
final AnnotationUsage<Comment> comment = returnedClassOrElement.getAnnotationUsage( Comment.class );
if ( comment != null ) {
udt.setComment( comment.value() );
udt.setComment( comment.getString( "value" ) );
}
for ( org.hibernate.mapping.Column aggregatedColumn : aggregatedColumns ) {
udt.addColumn( aggregatedColumn );
@ -196,8 +198,7 @@ public class AggregateComponentSecondPass implements SecondPass {
private static void validateComponent(Component component, String basePath, boolean inArray) {
for ( Property property : component.getProperties() ) {
final Value value = property.getValue();
if ( value instanceof Component ) {
final Component c = (Component) value;
if ( value instanceof Component c ) {
validateComponent( c, qualify( basePath, property.getName() ), inArray );
}
else if ( value instanceof ToOne ) {
@ -433,7 +434,7 @@ public class AggregateComponentSecondPass implements SecondPass {
String.format(
"Struct [%s] of class [%s] is defined by multiple components with different mappings [%s] and [%s] for column [%s]",
udt1.getName(),
componentXClass.getName(),
returnedClassOrElement.getName(),
column1.getSqlType(),
column2.getSqlType(),
column1.getCanonicalName()
@ -448,7 +449,7 @@ public class AggregateComponentSecondPass implements SecondPass {
"Struct [%s] is defined by multiple components %s but some columns are missing in [%s]: %s",
udt1.getName(),
findComponentClasses(),
componentXClass.getName(),
returnedClassOrElement.getName(),
missingColumns
)
);

View File

@ -16,11 +16,9 @@ import org.hibernate.annotations.Check;
import org.hibernate.annotations.Checks;
import org.hibernate.annotations.ColumnDefault;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.ColumnTransformers;
import org.hibernate.annotations.FractionalSeconds;
import org.hibernate.annotations.GeneratedColumn;
import org.hibernate.annotations.Index;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitBasicColumnNameSource;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
@ -40,6 +38,8 @@ import org.hibernate.mapping.Formula;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MemberDetails;
import org.jboss.logging.Logger;
@ -491,7 +491,7 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildFormulaFromAnnotation(
org.hibernate.annotations.Formula formulaAnn,
AnnotationUsage<org.hibernate.annotations.Formula> formulaAnn,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -512,7 +512,7 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildColumnFromNoAnnotation(
FractionalSeconds fractionalSeconds,
AnnotationUsage<FractionalSeconds> fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -532,8 +532,8 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildColumnFromAnnotation(
jakarta.persistence.Column column,
org.hibernate.annotations.FractionalSeconds fractionalSeconds,
AnnotationUsage<jakarta.persistence.Column> column,
AnnotationUsage<FractionalSeconds> fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -554,8 +554,8 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildColumnsFromAnnotations(
jakarta.persistence.Column[] columns,
FractionalSeconds fractionalSeconds,
List<AnnotationUsage<jakarta.persistence.Column>> columns,
AnnotationUsage<FractionalSeconds> fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -577,7 +577,7 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildColumnsFromAnnotations(
jakarta.persistence.Column[] columns,
List<AnnotationUsage<jakarta.persistence.Column>> columns,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -600,9 +600,9 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildColumnOrFormulaFromAnnotation(
jakarta.persistence.Column column,
org.hibernate.annotations.Formula formulaAnn,
org.hibernate.annotations.FractionalSeconds fractionalSeconds,
AnnotationUsage<jakarta.persistence.Column> column,
AnnotationUsage<org.hibernate.annotations.Formula> formulaAnn,
AnnotationUsage<FractionalSeconds> fractionalSeconds,
// Comment commentAnn,
Nullability nullability,
PropertyHolder propertyHolder,
@ -610,7 +610,7 @@ public class AnnotatedColumn {
Map<String, Join> secondaryTables,
MetadataBuildingContext context) {
return buildColumnsOrFormulaFromAnnotation(
column==null ? null : new jakarta.persistence.Column[] { column },
column==null ? null : List.of( column ),
formulaAnn,
fractionalSeconds,
// commentAnn,
@ -624,9 +624,9 @@ public class AnnotatedColumn {
}
public static AnnotatedColumns buildColumnsOrFormulaFromAnnotation(
jakarta.persistence.Column[] columns,
org.hibernate.annotations.Formula formulaAnn,
org.hibernate.annotations.FractionalSeconds fractionalSeconds,
List<AnnotationUsage<jakarta.persistence.Column>> columns,
AnnotationUsage<org.hibernate.annotations.Formula> formulaAnn,
AnnotationUsage<FractionalSeconds> fractionalSeconds,
// Comment comment,
Nullability nullability,
PropertyHolder propertyHolder,
@ -642,7 +642,7 @@ public class AnnotatedColumn {
parent.setBuildingContext( context );
parent.setJoins( secondaryTables ); //unnecessary
final AnnotatedColumn formulaColumn = new AnnotatedColumn();
formulaColumn.setFormula( formulaAnn.value() );
formulaColumn.setFormula( formulaAnn.getString( "value" ) );
formulaColumn.setImplicit( false );
// formulaColumn.setBuildingContext( context );
// formulaColumn.setPropertyHolder( propertyHolder );
@ -651,7 +651,7 @@ public class AnnotatedColumn {
return parent;
}
else {
final jakarta.persistence.Column[] actualColumns = overrideColumns( columns, propertyHolder, inferredData );
final List<AnnotationUsage<jakarta.persistence.Column>> actualColumns = overrideColumns( columns, propertyHolder, inferredData );
if ( actualColumns == null ) {
return buildImplicitColumn(
fractionalSeconds,
@ -679,24 +679,24 @@ public class AnnotatedColumn {
}
}
private static jakarta.persistence.Column[] overrideColumns(
jakarta.persistence.Column[] columns,
private static List<AnnotationUsage<jakarta.persistence.Column>> overrideColumns(
List<AnnotationUsage<jakarta.persistence.Column>> columns,
PropertyHolder propertyHolder,
PropertyData inferredData ) {
final String path = getPath( propertyHolder, inferredData );
final jakarta.persistence.Column[] overriddenCols = propertyHolder.getOverriddenColumn( path );
final List<AnnotationUsage<jakarta.persistence.Column>> overriddenCols = propertyHolder.getOverriddenColumn( path );
if ( overriddenCols != null ) {
//check for overridden first
if ( columns != null && overriddenCols.length != columns.length ) {
if ( columns != null && overriddenCols.size() != columns.size() ) {
//TODO: unfortunately, we never actually see this nice error message, since
// PersistentClass.validate() gets called first and produces a worse message
throw new AnnotationException( "Property '" + path
+ "' specifies " + columns.length
+ " '@AttributeOverride's but the overridden property has " + overriddenCols.length
+ "' specifies " + columns.size()
+ " '@AttributeOverride's but the overridden property has " + overriddenCols.size()
+ " columns (every column must have exactly one '@AttributeOverride')" );
}
LOG.debugf( "Column(s) overridden for property %s", inferredData.getPropertyName() );
return overriddenCols.length == 0 ? null : overriddenCols;
return overriddenCols.isEmpty() ? null : overriddenCols;
}
else {
return columns;
@ -710,14 +710,14 @@ public class AnnotatedColumn {
String suffixForDefaultColumnName,
Map<String, Join> secondaryTables,
MetadataBuildingContext context,
jakarta.persistence.Column[] actualCols,
FractionalSeconds fractionalSeconds) {
List<AnnotationUsage<jakarta.persistence.Column>> actualCols,
AnnotationUsage<FractionalSeconds> fractionalSeconds) {
final AnnotatedColumns parent = new AnnotatedColumns();
parent.setPropertyHolder( propertyHolder );
parent.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) );
parent.setJoins( secondaryTables );
parent.setBuildingContext( context );
for ( jakarta.persistence.Column column : actualCols ) {
for ( AnnotationUsage<jakarta.persistence.Column> column : actualCols ) {
final Database database = context.getMetadataCollector().getDatabase();
final String sqlType = getSqlType( context, column );
final String tableName = getTableName( column, database );
@ -732,7 +732,7 @@ public class AnnotatedColumn {
inferredData,
suffixForDefaultColumnName,
parent,
actualCols.length,
actualCols.size(),
database,
column,
fractionalSeconds,
@ -743,14 +743,22 @@ public class AnnotatedColumn {
return parent;
}
private static String getTableName(jakarta.persistence.Column column, Database database) {
return column.table().isEmpty() ? ""
: database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( column.table() ).render();
private static String getTableName(
AnnotationUsage<jakarta.persistence.Column> column,
Database database) {
final String table = column.getString( "table" );
return table.isEmpty()
? ""
: database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( table ).render();
}
private static String getSqlType(MetadataBuildingContext context, jakarta.persistence.Column column) {
return column.columnDefinition().isEmpty() ? null
: context.getObjectNameNormalizer().applyGlobalQuoting( column.columnDefinition() );
private static String getSqlType(
MetadataBuildingContext context,
AnnotationUsage<jakarta.persistence.Column> column) {
final String columnDefinition = column.getString( "columnDefinition" );
return columnDefinition.isEmpty()
? null
: context.getObjectNameNormalizer().applyGlobalQuoting( columnDefinition );
}
private static AnnotatedColumn buildColumn(
@ -761,8 +769,8 @@ public class AnnotatedColumn {
AnnotatedColumns parent,
int numberOfColumns,
Database database,
jakarta.persistence.Column column,
FractionalSeconds fractionalSeconds,
AnnotationUsage<jakarta.persistence.Column> column,
AnnotationUsage<FractionalSeconds> fractionalSeconds,
String sqlType,
String tableName) {
final String columnName = logicalColumnName( inferredData, suffixForDefaultColumnName, database, column );
@ -770,27 +778,20 @@ public class AnnotatedColumn {
annotatedColumn.setLogicalColumnName( columnName );
annotatedColumn.setImplicit( false );
annotatedColumn.setSqlType( sqlType );
annotatedColumn.setLength( (long) column.length() );
annotatedColumn.setLength( (long) column.getInteger( "length" ) );
if ( fractionalSeconds != null ) {
annotatedColumn.setTemporalPrecision( fractionalSeconds.value() );
annotatedColumn.setTemporalPrecision( fractionalSeconds.getInteger( "value" ) );
}
else {
annotatedColumn.setPrecision( column.precision() );
annotatedColumn.setPrecision( column.getInteger( "precision" ) );
}
annotatedColumn.setScale( column.scale() );
annotatedColumn.setScale( column.getInteger( "scale" ) );
annotatedColumn.handleArrayLength( inferredData );
// annotatedColumn.setPropertyHolder( propertyHolder );
// annotatedColumn.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) );
annotatedColumn.setNullable( column.nullable() ); //TODO force to not null if available? This is a (bad) user choice.
// if ( comment != null ) {
// annotatedColumn.setComment( comment.value() );
// }
annotatedColumn.setUnique( column.unique() );
annotatedColumn.setInsertable( column.insertable() );
annotatedColumn.setUpdatable( column.updatable() );
annotatedColumn.setNullable( column.getBoolean( "nullable" ) );
annotatedColumn.setUnique( column.getBoolean( "unique" ) );
annotatedColumn.setInsertable( column.getBoolean( "insertable" ) );
annotatedColumn.setUpdatable( column.getBoolean( "updatable" ) );
annotatedColumn.setExplicitTableName( tableName );
// annotatedColumn.setJoins( secondaryTables );
// annotatedColumn.setBuildingContext( context );
annotatedColumn.setParent( parent );
annotatedColumn.applyColumnDefault( inferredData, numberOfColumns );
annotatedColumn.applyGeneratedAs( inferredData, numberOfColumns );
@ -801,8 +802,8 @@ public class AnnotatedColumn {
}
private void handleArrayLength(PropertyData inferredData) {
if ( inferredData.getProperty().isAnnotationPresent(Array.class) ) {
setArrayLength( inferredData.getProperty().getAnnotation(Array.class).length() );
if ( inferredData.getAttributeMember().hasAnnotationUsage( Array.class) ) {
setArrayLength( inferredData.getAttributeMember().getAnnotationUsage( Array.class).getInteger( "length" ) );
}
}
@ -810,7 +811,7 @@ public class AnnotatedColumn {
PropertyData inferredData,
String suffixForDefaultColumnName,
Database database,
jakarta.persistence.Column column) {
AnnotationUsage<jakarta.persistence.Column> column) {
final String columnName = getColumnName( database, column );
// NOTE : this is the logical column name, not the physical!
return isEmpty( columnName ) && isNotEmpty( suffixForDefaultColumnName )
@ -818,22 +819,27 @@ public class AnnotatedColumn {
: columnName;
}
private static String getColumnName(Database database, jakarta.persistence.Column column) {
return column.name().isEmpty() ? null
: database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( column.name() ).render();
private static String getColumnName(Database database, AnnotationUsage<jakarta.persistence.Column> column) {
final String name = column.getString( "name" );
return name.isEmpty()
? null
: database.getJdbcEnvironment().getIdentifierHelper().toIdentifier( name ).render();
}
void applyColumnDefault(PropertyData inferredData, int length) {
final XProperty property = inferredData.getProperty();
if ( property != null ) {
final ColumnDefault columnDefault =
getOverridableAnnotation( property, ColumnDefault.class, getBuildingContext() );
final MemberDetails attributeMember = inferredData.getAttributeMember();
if ( attributeMember != null ) {
final AnnotationUsage<ColumnDefault> columnDefault = getOverridableAnnotation(
attributeMember,
ColumnDefault.class,
getBuildingContext()
);
if ( columnDefault != null ) {
if ( length != 1 ) {
throw new AnnotationException( "'@ColumnDefault' may only be applied to single-column mappings but '"
+ property.getName() + "' maps to " + length + " columns" );
+ attributeMember.getName() + "' maps to " + length + " columns" );
}
setDefaultValue( columnDefault.value() );
setDefaultValue( columnDefault.getString( "value" ) );
}
}
else {
@ -842,16 +848,19 @@ public class AnnotatedColumn {
}
void applyGeneratedAs(PropertyData inferredData, int length) {
final XProperty property = inferredData.getProperty();
if ( property != null ) {
final GeneratedColumn generatedColumn =
getOverridableAnnotation( property, GeneratedColumn.class, getBuildingContext() );
final MemberDetails attributeMember = inferredData.getAttributeMember();
if ( attributeMember != null ) {
final AnnotationUsage<GeneratedColumn> generatedColumn = getOverridableAnnotation(
attributeMember,
GeneratedColumn.class,
getBuildingContext()
);
if ( generatedColumn != null ) {
if (length!=1) {
throw new AnnotationException("'@GeneratedColumn' may only be applied to single-column mappings but '"
+ property.getName() + "' maps to " + length + " columns" );
+ attributeMember.getName() + "' maps to " + length + " columns" );
}
setGeneratedAs( generatedColumn.value() );
setGeneratedAs( generatedColumn.getString( "value" ) );
}
}
else {
@ -860,22 +869,30 @@ public class AnnotatedColumn {
}
private void applyCheckConstraint(PropertyData inferredData, int length) {
final XProperty property = inferredData.getProperty();
if ( property != null ) {
if ( property.isAnnotationPresent( Checks.class ) ) {
// if there are multiple annotations, they're not overrideable
for ( Check check : property.getAnnotation( Checks.class ).value() ) {
addCheckConstraint( check.name().isEmpty() ? null : check.name(), check.constraints() );
final MemberDetails attributeMember = inferredData.getAttributeMember();
if ( attributeMember != null ) {
// if there are multiple annotations, they're not overrideable
final AnnotationUsage<Checks> checksAnn = attributeMember.getAnnotationUsage( Checks.class );
if ( checksAnn != null ) {
final List<AnnotationUsage<Check>> checkAnns = checksAnn.getList( "value" );
for ( AnnotationUsage<Check> checkAnn : checkAnns ) {
addCheckConstraint(
checkAnn.getString( "name", (String) null ),
checkAnn.getString( "constraints" )
);
}
}
else {
final Check check = getOverridableAnnotation( property, Check.class, getBuildingContext() );
if ( check != null ) {
final AnnotationUsage<Check> checkAnn = getOverridableAnnotation( attributeMember, Check.class, getBuildingContext() );
if ( checkAnn != null ) {
if ( length != 1 ) {
throw new AnnotationException("'@Check' may only be applied to single-column mappings but '"
+ property.getName() + "' maps to " + length + " columns (use a table-level '@Check')" );
+ attributeMember.getName() + "' maps to " + length + " columns (use a table-level '@Check')" );
}
addCheckConstraint( check.name().isEmpty() ? null : check.name(), check.constraints() );
addCheckConstraint(
checkAnn.getString( "name", (String) null ),
checkAnn.getString( "constraints" )
);
}
}
}
@ -887,35 +904,31 @@ public class AnnotatedColumn {
//must only be called after all setters are defined and before binding
private void extractDataFromPropertyData(PropertyHolder propertyHolder, PropertyData inferredData) {
if ( inferredData != null ) {
final XProperty property = inferredData.getProperty();
if ( property != null ) {
final MemberDetails attributeMember = inferredData.getAttributeMember();
if ( attributeMember != null ) {
if ( propertyHolder.isComponent() ) {
processColumnTransformerExpressions( propertyHolder.getOverriddenColumnTransformer( logicalColumnName ) );
}
processColumnTransformerExpressions( property.getAnnotation( ColumnTransformer.class ) );
final ColumnTransformers annotations = property.getAnnotation( ColumnTransformers.class );
if ( annotations != null ) {
for ( ColumnTransformer annotation : annotations.value() ) {
processColumnTransformerExpressions( annotation );
}
}
attributeMember.forEachAnnotationUsage( ColumnTransformer.class, this::processColumnTransformerExpressions );
}
}
}
private void processColumnTransformerExpressions(ColumnTransformer annotation) {
if ( annotation != null ) {
if ( isEmpty( annotation.forColumn() )
// "" is the default value for annotations
|| annotation.forColumn().equals( logicalColumnName != null ? logicalColumnName : "" ) ) {
readExpression = nullIfEmpty( annotation.read() );
writeExpression = nullIfEmpty( annotation.write() );
}
private void processColumnTransformerExpressions(AnnotationUsage<ColumnTransformer> annotation) {
if ( annotation == null
// "" is the default value for annotations
|| isEmpty( annotation.getString( "forColumn" ) )
|| annotation.getString( "forColumn" ).equals( logicalColumnName != null ? logicalColumnName : "" ) ) {
return;
}
readExpression = nullIfEmpty( annotation.getString( "read" ) );
writeExpression = nullIfEmpty( annotation.getString( "write" ) );
}
private static AnnotatedColumns buildImplicitColumn(
FractionalSeconds fractionalSeconds,
AnnotationUsage<FractionalSeconds> fractionalSeconds,
PropertyData inferredData,
String suffixForDefaultColumnName,
Map<String, Join> secondaryTables,
@ -935,7 +948,7 @@ public class AnnotatedColumn {
// }
//not following the spec but more clean
if ( nullability != Nullability.FORCED_NULL
&& !PropertyBinder.isOptional( inferredData.getProperty(), propertyHolder ) ) {
&& !PropertyBinder.isOptional( inferredData.getAttributeMember(), propertyHolder ) ) {
column.setNullable( false );
}
final String propertyName = inferredData.getPropertyName();
@ -956,15 +969,15 @@ public class AnnotatedColumn {
column.extractDataFromPropertyData( propertyHolder, inferredData );
column.handleArrayLength( inferredData );
if ( fractionalSeconds != null ) {
column.setTemporalPrecision( fractionalSeconds.value() );
column.setTemporalPrecision( fractionalSeconds.getInteger( "value" ) );
}
column.bind();
return columns;
}
public void addIndex(Index index, boolean inSecondPass) {
public void addIndex(AnnotationUsage<Index> index, boolean inSecondPass) {
if ( index != null ) {
addIndex( index.name(), inSecondPass );
addIndex( index.getString( "name" ), inSecondPass );
}
}

View File

@ -9,8 +9,8 @@ package org.hibernate.boot.model.internal;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.Column;
import jakarta.persistence.DiscriminatorColumn;
import jakarta.persistence.DiscriminatorType;
@ -45,9 +45,9 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
}
public static AnnotatedDiscriminatorColumn buildDiscriminatorColumn(
DiscriminatorColumn discriminatorColumn,
DiscriminatorFormula discriminatorFormula,
Column columnOverride,
AnnotationUsage<DiscriminatorColumn> discriminatorColumn,
AnnotationUsage<DiscriminatorFormula> discriminatorFormula,
AnnotationUsage<Column> columnOverride,
String defaultColumnName,
MetadataBuildingContext context) {
final AnnotatedColumns parent = new AnnotatedColumns();
@ -55,24 +55,24 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
final AnnotatedDiscriminatorColumn column = new AnnotatedDiscriminatorColumn( defaultColumnName );
final DiscriminatorType discriminatorType;
if ( discriminatorFormula != null ) {
final DiscriminatorType type = discriminatorFormula.discriminatorType();
final DiscriminatorType type = discriminatorFormula.getEnum( "discriminatorType" );
if ( type == DiscriminatorType.STRING ) {
discriminatorType = discriminatorColumn == null ? type : discriminatorColumn.discriminatorType();
discriminatorType = discriminatorColumn == null ? type : discriminatorColumn.getEnum( "discriminatorType" );
}
else {
discriminatorType = type;
}
column.setImplicit( false );
column.setFormula( discriminatorFormula.value() );
column.setFormula( discriminatorFormula.getString( "value" ) );
}
else if ( discriminatorColumn != null ) {
discriminatorType = discriminatorColumn.discriminatorType();
discriminatorType = discriminatorColumn.getEnum( "discriminatorType" );
column.setImplicit( false );
if ( !discriminatorColumn.columnDefinition().isEmpty() ) {
column.setSqlType( discriminatorColumn.columnDefinition() );
if ( !discriminatorColumn.getString( "columnDefinition" ).isEmpty() ) {
column.setSqlType( discriminatorColumn.getString( "columnDefinition" ) );
}
if ( !discriminatorColumn.name().isEmpty() ) {
column.setLogicalColumnName( discriminatorColumn.name() );
if ( !discriminatorColumn.getString( "name" ).isEmpty() ) {
column.setLogicalColumnName( discriminatorColumn.getString( "name" ) );
}
column.setNullable( false );
}
@ -81,9 +81,11 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
column.setImplicit( true );
}
if ( columnOverride != null ) {
column.setLogicalColumnName( columnOverride.name() );
if ( !columnOverride.columnDefinition().isEmpty() ) {
column.setSqlType( columnOverride.columnDefinition() );
column.setLogicalColumnName( columnOverride.getString( "name" ) );
final String columnDefinition = columnOverride.getString( "columnDefinition" );
if ( !columnDefinition.isEmpty() ) {
column.setSqlType( columnDefinition );
}
}
setDiscriminatorType( discriminatorType, discriminatorColumn, columnOverride, column );
@ -94,8 +96,8 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
private static void setDiscriminatorType(
DiscriminatorType type,
DiscriminatorColumn discriminatorColumn,
Column columnOverride,
AnnotationUsage<DiscriminatorColumn> discriminatorColumn,
AnnotationUsage<Column> columnOverride,
AnnotatedDiscriminatorColumn column) {
if ( type == null ) {
column.setDiscriminatorTypeName( "string" );
@ -114,10 +116,10 @@ public class AnnotatedDiscriminatorColumn extends AnnotatedColumn {
case STRING:
column.setDiscriminatorTypeName( "string" );
if ( columnOverride != null ) {
column.setLength( (long) columnOverride.length() );
column.setLength( (long) columnOverride.getInteger( "length" ) );
}
else if ( discriminatorColumn != null ) {
column.setLength( (long) discriminatorColumn.length() );
column.setLength( (long) discriminatorColumn.getInteger( "length" ) );
}
break;
default:

View File

@ -6,6 +6,8 @@
*/
package org.hibernate.boot.model.internal;
import java.util.List;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.JoinFormula;
@ -19,6 +21,7 @@ import org.hibernate.mapping.Column;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Value;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.PrimaryKeyJoinColumn;
@ -71,13 +74,13 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
}
static AnnotatedJoinColumn buildJoinColumn(
JoinColumn joinColumn,
AnnotationUsage<JoinColumn> joinColumn,
String mappedBy,
AnnotatedJoinColumns parent,
PropertyHolder propertyHolder,
PropertyData inferredData) {
final String path = qualify( propertyHolder.getPath(), inferredData.getPropertyName() );
final JoinColumn[] overrides = propertyHolder.getOverriddenJoinColumn( path );
final List<AnnotationUsage<JoinColumn>> overrides = propertyHolder.getOverriddenJoinColumn( path );
if ( overrides != null ) {
//TODO: relax this restriction
throw new AnnotationException( "Property '" + path
@ -87,11 +90,11 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
}
public static AnnotatedJoinColumn buildJoinFormula(
JoinFormula joinFormula,
AnnotationUsage<JoinFormula> joinFormula,
AnnotatedJoinColumns parent) {
final AnnotatedJoinColumn formulaColumn = new AnnotatedJoinColumn();
formulaColumn.setFormula( joinFormula.value() );
formulaColumn.setReferencedColumn( joinFormula.referencedColumnName() );
formulaColumn.setFormula( joinFormula.getString( "value" ) );
formulaColumn.setReferencedColumn( joinFormula.getString( "referencedColumnName" ) );
// formulaColumn.setContext( buildingContext );
// formulaColumn.setPropertyHolder( propertyHolder );
// formulaColumn.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
@ -102,7 +105,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
}
static AnnotatedJoinColumn buildJoinColumn(
JoinColumn joinColumn,
AnnotationUsage<JoinColumn> joinColumn,
// Comment comment,
String mappedBy,
AnnotatedJoinColumns parent,
@ -123,7 +126,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
}
private static AnnotatedJoinColumn explicitJoinColumn(
JoinColumn joinColumn,
AnnotationUsage<JoinColumn> joinColumn,
// Comment comment,
AnnotatedJoinColumns parent,
PropertyData inferredData,
@ -170,31 +173,36 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
// TODO default name still useful in association table
public void applyJoinAnnotation(JoinColumn joinColumn, String defaultName) {
public void applyJoinAnnotation(AnnotationUsage<JoinColumn> joinColumn, String defaultName) {
if ( joinColumn == null ) {
setImplicit( true );
}
else {
setImplicit( false );
if ( !joinColumn.columnDefinition().isEmpty() ) {
setSqlType( getBuildingContext().getObjectNameNormalizer()
.applyGlobalQuoting( joinColumn.columnDefinition() ) );
}
if ( !joinColumn.name().isEmpty() ) {
setLogicalColumnName( joinColumn.name() );
}
setNullable( joinColumn.nullable() );
setUnique( joinColumn.unique() );
setInsertable( joinColumn.insertable() );
setUpdatable( joinColumn.updatable() );
setReferencedColumn( joinColumn.referencedColumnName() );
if ( joinColumn.table().isEmpty() ) {
final String name = joinColumn.getString( "name" );
if ( !name.isEmpty() ) {
setLogicalColumnName( name );
}
final String columnDefinition = joinColumn.getString( "columnDefinition" );
if ( !columnDefinition.isEmpty() ) {
setSqlType( getBuildingContext().getObjectNameNormalizer().applyGlobalQuoting( columnDefinition ) );
}
setNullable( joinColumn.getBoolean( "nullable" ) );
setUnique( joinColumn.getBoolean( "unique" ) );
setInsertable( joinColumn.getBoolean( "insertable" ) );
setUpdatable( joinColumn.getBoolean( "updatable" ) );
setReferencedColumn( joinColumn.getString( "referencedColumnName" ) );
final String table = joinColumn.getString( "table" );
if ( table.isEmpty() ) {
setExplicitTableName( "" );
}
else {
final Database database = getBuildingContext().getMetadataCollector().getDatabase();
final Identifier logicalIdentifier = database.toIdentifier( joinColumn.table() );
final Identifier logicalIdentifier = database.toIdentifier( table );
final Identifier physicalIdentifier = getBuildingContext().getBuildingOptions()
.getPhysicalNamingStrategy()
.toPhysicalTableName( logicalIdentifier, database.getJdbcEnvironment() );
@ -207,8 +215,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
* Called for {@link jakarta.persistence.InheritanceType#JOINED} entities.
*/
public static AnnotatedJoinColumn buildInheritanceJoinColumn(
PrimaryKeyJoinColumn primaryKeyJoinColumn,
JoinColumn joinColumn,
AnnotationUsage<PrimaryKeyJoinColumn> primaryKeyJoinColumn,
AnnotationUsage<JoinColumn> joinColumn,
Value identifier,
AnnotatedJoinColumns parent,
MetadataBuildingContext context) {
@ -220,8 +228,8 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
}
private static AnnotatedJoinColumn buildExplicitInheritanceJoinColumn(
PrimaryKeyJoinColumn primaryKeyJoinColumn,
JoinColumn joinColumn,
AnnotationUsage<PrimaryKeyJoinColumn> primaryKeyJoinColumn,
AnnotationUsage<JoinColumn> joinColumn,
AnnotatedJoinColumns parent,
MetadataBuildingContext context,
String defaultColumnName) {
@ -229,15 +237,16 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
final String columnDefinition;
final String referencedColumnName;
if ( primaryKeyJoinColumn != null ) {
columnName = primaryKeyJoinColumn.name();
columnDefinition = primaryKeyJoinColumn.columnDefinition();
referencedColumnName = primaryKeyJoinColumn.referencedColumnName();
columnName = primaryKeyJoinColumn.getString( "name" );
columnDefinition = primaryKeyJoinColumn.getString( "columnDefinition" );
referencedColumnName = primaryKeyJoinColumn.getString( "referencedColumnName" );
}
else {
columnName = joinColumn.name();
columnDefinition = joinColumn.columnDefinition();
referencedColumnName = joinColumn.referencedColumnName();
columnName = joinColumn.getString( "name" );
columnDefinition = joinColumn.getString( "columnDefinition" );
referencedColumnName = joinColumn.getString( "referencedColumnName" );
}
final ObjectNameNormalizer normalizer = context.getObjectNameNormalizer();
final String columnDef = columnDefinition.isEmpty() ? null
: normalizer.toDatabaseIdentifierText( columnDefinition );
@ -459,7 +468,7 @@ public class AnnotatedJoinColumn extends AnnotatedColumn {
AnnotatedJoinColumns parent,
PropertyHolder propertyHolder,
PropertyData inferredData,
JoinColumn joinColumn) {
AnnotationUsage<JoinColumn> joinColumn) {
final AnnotatedJoinColumn column = new AnnotatedJoinColumn();
column.setImplicit( true );
// column.setPropertyHolder( propertyHolder );

View File

@ -29,6 +29,7 @@ import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.cfg.RecoverableException;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join;
@ -38,6 +39,7 @@ import org.hibernate.mapping.Property;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.JoinColumn;
@ -75,7 +77,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
private String manyToManyOwnerSideEntityName;
public static AnnotatedJoinColumns buildJoinColumnsOrFormulas(
JoinColumnOrFormula[] joinColumnOrFormulas,
List<AnnotationUsage<JoinColumnOrFormula>> joinColumnOrFormulas,
String mappedBy,
Map<String, Join> joins,
PropertyHolder propertyHolder,
@ -87,10 +89,10 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
parent.setPropertyHolder( propertyHolder );
parent.setPropertyName( getRelativePath( propertyHolder, inferredData.getPropertyName() ) );
parent.setMappedBy( mappedBy );
for ( JoinColumnOrFormula columnOrFormula : joinColumnOrFormulas ) {
final JoinFormula formula = columnOrFormula.formula();
final JoinColumn column = columnOrFormula.column();
final String annotationString = formula.value();
for ( AnnotationUsage<JoinColumnOrFormula> columnOrFormula : joinColumnOrFormulas ) {
final AnnotationUsage<JoinFormula> formula = columnOrFormula.getNestedUsage( "formula" );
final AnnotationUsage<JoinColumn> column = columnOrFormula.getNestedUsage( "column" );
final String annotationString = formula.getString( "value" );
if ( isNotEmpty( annotationString ) ) {
AnnotatedJoinColumn.buildJoinFormula( formula, parent );
}
@ -102,7 +104,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
}
static AnnotatedJoinColumns buildJoinColumnsWithFormula(
JoinFormula joinFormula,
AnnotationUsage<JoinFormula> joinFormula,
Map<String, Join> secondaryTables,
PropertyHolder propertyHolder,
PropertyData inferredData,
@ -117,7 +119,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
}
public static AnnotatedJoinColumns buildJoinColumns(
JoinColumn[] joinColumns,
List<AnnotationUsage<JoinColumn>> joinColumns,
// Comment comment,
String mappedBy,
Map<String, Join> joins,
@ -137,7 +139,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
}
public static AnnotatedJoinColumns buildJoinColumnsWithDefaultColumnSuffix(
JoinColumn[] joinColumns,
List<AnnotationUsage<JoinColumn>> joinColumns,
// Comment comment,
String mappedBy,
Map<String, Join> joins,
@ -148,15 +150,15 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
assert mappedBy == null || !mappedBy.isEmpty();
final String propertyName = inferredData.getPropertyName();
final String path = qualify( propertyHolder.getPath(), propertyName );
final JoinColumn[] overriddes = propertyHolder.getOverriddenJoinColumn( path );
final JoinColumn[] actualColumns = overriddes == null ? joinColumns : overriddes;
final List<AnnotationUsage<JoinColumn>> overrides = propertyHolder.getOverriddenJoinColumn( path );
final List<AnnotationUsage<JoinColumn>> actualColumns = overrides == null ? joinColumns : overrides;
final AnnotatedJoinColumns parent = new AnnotatedJoinColumns();
parent.setBuildingContext( context );
parent.setJoins( joins );
parent.setPropertyHolder( propertyHolder );
parent.setPropertyName( getRelativePath( propertyHolder, propertyName ) );
parent.setMappedBy( mappedBy );
if ( actualColumns == null || actualColumns.length == 0 ) {
if ( CollectionHelper.isEmpty( actualColumns ) ) {
AnnotatedJoinColumn.buildJoinColumn(
null,
// comment,
@ -169,7 +171,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
}
else {
parent.setMappedBy( mappedBy );
for ( JoinColumn actualColumn : actualColumns ) {
for ( AnnotationUsage<JoinColumn> actualColumn : actualColumns ) {
AnnotatedJoinColumn.buildJoinColumn(
actualColumn,
// comment,
@ -188,7 +190,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
* Called for join tables in {@link jakarta.persistence.ManyToMany} associations.
*/
public static AnnotatedJoinColumns buildJoinTableJoinColumns(
JoinColumn[] joinColumns,
List<AnnotationUsage<JoinColumn>> joinColumns,
Map<String, Join> secondaryTables,
PropertyHolder propertyHolder,
PropertyData inferredData,
@ -204,7 +206,7 @@ public class AnnotatedJoinColumns extends AnnotatedColumns {
AnnotatedJoinColumn.buildImplicitJoinTableJoinColumn( parent, propertyHolder, inferredData );
}
else {
for ( JoinColumn joinColumn : joinColumns ) {
for ( AnnotationUsage<JoinColumn> joinColumn : joinColumns ) {
AnnotatedJoinColumn.buildExplicitJoinTableJoinColumn( parent, propertyHolder, inferredData, joinColumn );
}
}

View File

@ -11,39 +11,30 @@ import java.util.List;
import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.annotations.CollectionTypeRegistration;
import org.hibernate.annotations.CollectionTypeRegistrations;
import org.hibernate.annotations.CompositeTypeRegistration;
import org.hibernate.annotations.CompositeTypeRegistrations;
import org.hibernate.annotations.ConverterRegistration;
import org.hibernate.annotations.ConverterRegistrations;
import org.hibernate.annotations.EmbeddableInstantiatorRegistration;
import org.hibernate.annotations.EmbeddableInstantiatorRegistrations;
import org.hibernate.annotations.FetchMode;
import org.hibernate.annotations.FetchProfile;
import org.hibernate.annotations.FetchProfile.FetchOverride;
import org.hibernate.annotations.FetchProfiles;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.GenericGenerators;
import org.hibernate.annotations.Imported;
import org.hibernate.annotations.JavaTypeRegistration;
import org.hibernate.annotations.JavaTypeRegistrations;
import org.hibernate.annotations.JdbcTypeRegistration;
import org.hibernate.annotations.JdbcTypeRegistrations;
import org.hibernate.annotations.TypeRegistration;
import org.hibernate.annotations.TypeRegistrations;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XPackage;
import org.hibernate.boot.internal.GenerationStrategyInterpreter;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.convert.spi.RegisteredConversion;
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.type.descriptor.java.BasicJavaType;
@ -54,19 +45,13 @@ import jakarta.persistence.FetchType;
import jakarta.persistence.Inheritance;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.MappedSuperclass;
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.SequenceGenerator;
import jakarta.persistence.SequenceGenerators;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.SqlResultSetMappings;
import jakarta.persistence.Table;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.TableGenerators;
import static org.hibernate.boot.model.internal.AnnotatedClassType.EMBEDDABLE;
import static org.hibernate.boot.model.internal.AnnotatedClassType.ENTITY;
@ -94,120 +79,58 @@ public final class AnnotationBinder {
private AnnotationBinder() {}
public static void bindDefaults(MetadataBuildingContext context) {
// todo (jpa32) - remove this. left for now for easy debugging
final Map<?,?> defaults = context.getBootstrapContext().getReflectionManager().getDefaults();
final GlobalRegistrations globalRegistrations = context.getMetadataCollector().getGlobalRegistrations();
// id generators ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
@SuppressWarnings("unchecked")
List<SequenceGenerator> generators = ( List<SequenceGenerator> ) defaults.get( SequenceGenerator.class );
if ( generators != null ) {
for ( SequenceGenerator sequenceGenerator : generators ) {
final IdentifierGeneratorDefinition idGen = buildIdGenerator( sequenceGenerator, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
globalRegistrations.getSequenceGeneratorRegistrations().forEach( (name, generatorRegistration) -> {
final IdentifierGeneratorDefinition.Builder definitionBuilder = new IdentifierGeneratorDefinition.Builder();
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretSequenceGenerator(
generatorRegistration.configuration(),
definitionBuilder
);
final IdentifierGeneratorDefinition idGenDef = definitionBuilder.build();
if ( LOG.isTraceEnabled() ) {
LOG.tracef( "Adding global sequence generator with name: %s", name );
}
}
{
@SuppressWarnings("unchecked")
List<TableGenerator> generators = ( List<TableGenerator> ) defaults.get( TableGenerator.class );
if ( generators != null ) {
for ( TableGenerator tableGenerator : generators ) {
final IdentifierGeneratorDefinition idGen = buildIdGenerator( tableGenerator, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
}
}
context.getMetadataCollector().addDefaultIdentifierGenerator( idGenDef );
} );
{
@SuppressWarnings("unchecked")
List<TableGenerators> generators = (List<TableGenerators>) defaults.get( TableGenerators.class );
if ( generators != null ) {
generators.forEach( tableGenerators -> {
for ( TableGenerator tableGenerator : tableGenerators.value() ) {
final IdentifierGeneratorDefinition idGen = buildIdGenerator( tableGenerator, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
} );
globalRegistrations.getTableGeneratorRegistrations().forEach( (name, generatorRegistration) -> {
final IdentifierGeneratorDefinition.Builder definitionBuilder = new IdentifierGeneratorDefinition.Builder();
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretTableGenerator(
generatorRegistration.configuration(),
definitionBuilder
);
final IdentifierGeneratorDefinition idGenDef = definitionBuilder.build();
if ( LOG.isTraceEnabled() ) {
LOG.tracef( "Adding global table generator with name: %s", name );
}
}
{
@SuppressWarnings("unchecked")
List<SequenceGenerators> generators = (List<SequenceGenerators>) defaults.get( SequenceGenerators.class );
if ( generators != null ) {
generators.forEach( sequenceGenerators -> {
for ( SequenceGenerator sequenceGenerator : sequenceGenerators.value() ) {
final IdentifierGeneratorDefinition idGen = buildIdGenerator( sequenceGenerator, context );
if ( idGen != null ) {
context.getMetadataCollector().addDefaultIdentifierGenerator( idGen );
}
}
} );
}
}
// queries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
@SuppressWarnings("unchecked")
List<NamedQuery> queries = ( List<NamedQuery> ) defaults.get( NamedQuery.class );
if ( queries != null ) {
for ( NamedQuery ann : queries ) {
QueryBinder.bindQuery( ann, context, true );
}
}
}
{
@SuppressWarnings("unchecked")
List<NamedNativeQuery> nativeQueries = ( List<NamedNativeQuery> ) defaults.get( NamedNativeQuery.class );
if ( nativeQueries != null ) {
for ( NamedNativeQuery ann : nativeQueries ) {
QueryBinder.bindNativeQuery( ann, context, true );
}
}
}
context.getMetadataCollector().addDefaultIdentifierGenerator( idGenDef );
} );
// result-set-mappings ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
@SuppressWarnings("unchecked")
List<SqlResultSetMapping> mappings = ( List<SqlResultSetMapping> ) defaults.get( SqlResultSetMapping.class );
if ( mappings != null ) {
for ( SqlResultSetMapping annotation : mappings ) {
QueryBinder.bindSqlResultSetMapping( annotation, context, true );
}
}
}
globalRegistrations.getSqlResultSetMappingRegistrations().forEach( (name, mappingRegistration) -> {
QueryBinder.bindSqlResultSetMapping( mappingRegistration.configuration(), context, true );
} );
// stored procs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
@SuppressWarnings("unchecked")
final List<NamedStoredProcedureQuery> storedProcedureQueries =
(List<NamedStoredProcedureQuery>) defaults.get( NamedStoredProcedureQuery.class );
if ( storedProcedureQueries != null ) {
for ( NamedStoredProcedureQuery annotation : storedProcedureQueries ) {
bindNamedStoredProcedureQuery( annotation, context, true );
}
}
}
{
@SuppressWarnings("unchecked")
final List<NamedStoredProcedureQueries> storedProcedureQueries =
(List<NamedStoredProcedureQueries>) defaults.get( NamedStoredProcedureQueries.class );
if ( storedProcedureQueries != null ) {
for ( NamedStoredProcedureQueries annotation : storedProcedureQueries ) {
bindNamedStoredProcedureQueries( annotation, context, true );
}
}
}
// queries ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
globalRegistrations.getNamedQueryRegistrations().forEach( (name, queryRegistration) -> {
QueryBinder.bindQuery( queryRegistration.configuration(), context, true );
} );
globalRegistrations.getNamedNativeQueryRegistrations().forEach( (name, queryRegistration) -> {
QueryBinder.bindNativeQuery( queryRegistration.configuration(), context, true );
} );
globalRegistrations.getNamedStoredProcedureQueryRegistrations().forEach( (name, queryRegistration) -> {
QueryBinder.bindNamedStoredProcedureQuery( queryRegistration.configuration(), context, true );
} );
}
public static void bindPackage(ClassLoaderService cls, String packageName, MetadataBuildingContext context) {
@ -215,448 +138,307 @@ public final class AnnotationBinder {
if ( pack == null ) {
return;
}
final XPackage annotatedPackage = context.getBootstrapContext().getReflectionManager().toXPackage( pack );
final ClassDetails packageInfoClassDetails = context.getMetadataCollector()
.getSourceModelBuildingContext()
.getClassDetailsRegistry()
.resolveClassDetails( pack.getName() + ".package-info" );
handleIdGenerators( annotatedPackage, context );
handleIdGenerators( packageInfoClassDetails, context );
bindTypeDescriptorRegistrations( annotatedPackage, context );
bindEmbeddableInstantiatorRegistrations( annotatedPackage, context );
bindUserTypeRegistrations( annotatedPackage, context );
bindCompositeUserTypeRegistrations( annotatedPackage, context );
bindConverterRegistrations( annotatedPackage, context );
bindTypeDescriptorRegistrations( packageInfoClassDetails, context );
bindEmbeddableInstantiatorRegistrations( packageInfoClassDetails, context );
bindUserTypeRegistrations( packageInfoClassDetails, context );
bindCompositeUserTypeRegistrations( packageInfoClassDetails, context );
bindConverterRegistrations( packageInfoClassDetails, context );
bindGenericGenerators( annotatedPackage, context );
bindQueries( annotatedPackage, context );
bindFilterDefs( annotatedPackage, context );
bindGenericGenerators( packageInfoClassDetails, context );
bindQueries( packageInfoClassDetails, context );
bindFilterDefs( packageInfoClassDetails, context );
}
private static void handleIdGenerators(XPackage annotatedPackage, MetadataBuildingContext context) {
if ( annotatedPackage.isAnnotationPresent( SequenceGenerator.class ) ) {
final SequenceGenerator sequenceGenerator = annotatedPackage.getAnnotation( SequenceGenerator.class );
IdentifierGeneratorDefinition idGen = buildIdGenerator( sequenceGenerator, context );
private static void handleIdGenerators(ClassDetails packageInfoClassDetails, MetadataBuildingContext context) {
packageInfoClassDetails.forEachAnnotationUsage( SequenceGenerator.class, (usage) -> {
IdentifierGeneratorDefinition idGen = buildIdGenerator( usage, context );
context.getMetadataCollector().addIdentifierGenerator( idGen );
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add sequence generator with name: {0}", idGen.getName() );
}
}
if ( annotatedPackage.isAnnotationPresent( SequenceGenerators.class ) ) {
final SequenceGenerators sequenceGenerators = annotatedPackage.getAnnotation( SequenceGenerators.class );
for ( SequenceGenerator tableGenerator : sequenceGenerators.value() ) {
context.getMetadataCollector().addIdentifierGenerator( buildIdGenerator( tableGenerator, context ) );
}
}
} );
if ( annotatedPackage.isAnnotationPresent( TableGenerator.class ) ) {
final TableGenerator tableGenerator = annotatedPackage.getAnnotation( TableGenerator.class );
IdentifierGeneratorDefinition idGen = buildIdGenerator( tableGenerator, context );
packageInfoClassDetails.forEachAnnotationUsage( TableGenerator.class, (usage) -> {
IdentifierGeneratorDefinition idGen = buildIdGenerator( usage, context );
context.getMetadataCollector().addIdentifierGenerator( idGen );
}
if ( annotatedPackage.isAnnotationPresent( TableGenerators.class ) ) {
final TableGenerators tableGenerators = annotatedPackage.getAnnotation( TableGenerators.class );
for ( TableGenerator tableGenerator : tableGenerators.value() ) {
context.getMetadataCollector().addIdentifierGenerator( buildIdGenerator( tableGenerator, context ) );
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add table generator with name: {0}", idGen.getName() );
}
}
} );
}
private static void bindGenericGenerators(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
final GenericGenerator genericGenerator = annotatedElement.getAnnotation( GenericGenerator.class );
final GenericGenerators genericGenerators = annotatedElement.getAnnotation( GenericGenerators.class );
if ( genericGenerator != null ) {
bindGenericGenerator( genericGenerator, context );
}
if ( genericGenerators != null ) {
for ( GenericGenerator generator : genericGenerators.value() ) {
bindGenericGenerator( generator, context );
}
}
private static void bindGenericGenerators(AnnotationTarget annotatedElement, MetadataBuildingContext context) {
annotatedElement.forEachAnnotationUsage( GenericGenerator.class, (usage) -> {
bindGenericGenerator( usage, context );
} );
}
private static void bindGenericGenerator(GenericGenerator def, MetadataBuildingContext context) {
private static void bindGenericGenerator(AnnotationUsage<GenericGenerator> def, MetadataBuildingContext context) {
context.getMetadataCollector().addIdentifierGenerator( buildIdGenerator( def, context ) );
}
private static void bindNamedJpaQueries(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
QueryBinder.bindSqlResultSetMapping(
annotatedElement.getAnnotation( SqlResultSetMapping.class ),
context,
false
);
QueryBinder.bindSqlResultSetMappings(
annotatedElement.getAnnotation( SqlResultSetMappings.class ),
context,
false
);
QueryBinder.bindQuery(
annotatedElement.getAnnotation( NamedQuery.class ),
context,
false
);
QueryBinder.bindQueries(
annotatedElement.getAnnotation( NamedQueries.class ),
context,
false
);
QueryBinder.bindNativeQuery(
annotatedElement.getAnnotation( NamedNativeQuery.class ),
context,
false
);
QueryBinder.bindNativeQueries(
annotatedElement.getAnnotation( NamedNativeQueries.class ),
context,
false
);
public static void bindQueries(AnnotationTarget annotationTarget, MetadataBuildingContext context) {
bindNamedJpaQueries( annotationTarget, context );
bindNamedHibernateQueries( annotationTarget, context );
}
public static void bindQueries(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
bindNamedJpaQueries( annotatedElement, context );
QueryBinder.bindQuery(
annotatedElement.getAnnotation( org.hibernate.annotations.NamedQuery.class ),
private static void bindNamedHibernateQueries(AnnotationTarget annotationTarget, MetadataBuildingContext context) {
annotationTarget.forEachAnnotationUsage( org.hibernate.annotations.NamedQuery.class, (usage) -> QueryBinder.bindQuery(
usage,
context
);
) );
QueryBinder.bindQueries(
annotatedElement.getAnnotation( org.hibernate.annotations.NamedQueries.class ),
annotationTarget.forEachAnnotationUsage( org.hibernate.annotations.NamedNativeQuery.class, (usage) -> QueryBinder.bindNativeQuery(
usage,
context
);
QueryBinder.bindNativeQuery(
annotatedElement.getAnnotation( org.hibernate.annotations.NamedNativeQuery.class ),
context
);
QueryBinder.bindNativeQueries(
annotatedElement.getAnnotation( org.hibernate.annotations.NamedNativeQueries.class ),
context
);
// NamedStoredProcedureQuery handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bindNamedStoredProcedureQuery(
annotatedElement.getAnnotation( NamedStoredProcedureQuery.class ),
context,
false
);
// NamedStoredProcedureQueries handling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bindNamedStoredProcedureQueries(
annotatedElement.getAnnotation( NamedStoredProcedureQueries.class ),
context,
false
);
) );
}
private static void bindNamedStoredProcedureQueries(
NamedStoredProcedureQueries annotation,
MetadataBuildingContext context,
boolean isDefault) {
if ( annotation != null ) {
for ( NamedStoredProcedureQuery queryAnnotation : annotation.value() ) {
bindNamedStoredProcedureQuery( queryAnnotation, context, isDefault );
}
}
}
private static void bindNamedJpaQueries(AnnotationTarget annotationTarget, MetadataBuildingContext context) {
annotationTarget.forEachAnnotationUsage( SqlResultSetMapping.class, (usage) -> QueryBinder.bindSqlResultSetMapping(
usage,
context,
false
) );
private static void bindNamedStoredProcedureQuery(
NamedStoredProcedureQuery annotation,
MetadataBuildingContext context,
boolean isDefault) {
if ( annotation != null ) {
QueryBinder.bindNamedStoredProcedureQuery( annotation, context, isDefault );
}
annotationTarget.forEachAnnotationUsage( NamedQuery.class, (usage) -> QueryBinder.bindQuery(
usage,
context,
false
) );
annotationTarget.forEachAnnotationUsage( NamedNativeQuery.class, (usage) -> QueryBinder.bindNativeQuery(
usage,
context,
false
) );
annotationTarget.forEachAnnotationUsage( NamedStoredProcedureQuery.class, (usage) -> QueryBinder.bindNamedStoredProcedureQuery(
usage,
context,
false
) );
}
/**
* Bind an annotated class. A subclass must be bound <em>after</em> its superclass.
*
* @param annotatedClass entity to bind as {@code XClass} instance
* @param classDetails entity to bind as {@code XClass} instance
* @param inheritanceStatePerClass Metadata about the inheritance relationships for all mapped classes
*
* @throws MappingException in case there is a configuration error
*/
public static void bindClass(
XClass annotatedClass,
Map<XClass, InheritanceState> inheritanceStatePerClass,
ClassDetails classDetails,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext context) throws MappingException {
detectMappedSuperclassProblems( annotatedClass );
detectMappedSuperclassProblems( classDetails );
bindQueries( annotatedClass, context );
handleImport( annotatedClass, context );
//bindFilterDefs( annotatedClass, context );
bindTypeDescriptorRegistrations( annotatedClass, context );
bindEmbeddableInstantiatorRegistrations( annotatedClass, context );
bindUserTypeRegistrations( annotatedClass, context );
bindCompositeUserTypeRegistrations( annotatedClass, context );
bindConverterRegistrations( annotatedClass, context );
bindQueries( classDetails, context );
handleImport( classDetails, context );
//bindFilterDefs( classDetails, context );
bindTypeDescriptorRegistrations( classDetails, context );
bindEmbeddableInstantiatorRegistrations( classDetails, context );
bindUserTypeRegistrations( classDetails, context );
bindCompositeUserTypeRegistrations( classDetails, context );
bindConverterRegistrations( classDetails, context );
// try to find class level generators
final Map<String, IdentifierGeneratorDefinition> generators = buildGenerators( annotatedClass, context );
if ( context.getMetadataCollector().getClassType( annotatedClass ) == ENTITY ) {
EntityBinder.bindEntityClass( annotatedClass, inheritanceStatePerClass, generators, context );
final Map<String, IdentifierGeneratorDefinition> generators = buildGenerators( classDetails, context );
if ( context.getMetadataCollector().getClassType( classDetails ) == ENTITY ) {
EntityBinder.bindEntityClass( classDetails, inheritanceStatePerClass, generators, context );
}
}
private static void handleImport(XClass annotatedClass, MetadataBuildingContext context) {
if ( annotatedClass.isAnnotationPresent( Imported.class ) ) {
String qualifiedName = annotatedClass.getName();
String name = unqualify( qualifiedName );
String rename = annotatedClass.getAnnotation( Imported.class ).rename();
private static void handleImport(ClassDetails annotatedClass, MetadataBuildingContext context) {
if ( annotatedClass.hasAnnotationUsage( Imported.class ) ) {
final String qualifiedName = annotatedClass.getName();
final String name = unqualify( qualifiedName );
final String rename = annotatedClass.getAnnotationUsage( Imported.class ).getString( "rename" );
context.getMetadataCollector().addImport( rename.isEmpty() ? name : rename, qualifiedName );
}
}
private static void detectMappedSuperclassProblems(XClass annotatedClass) {
if ( annotatedClass.isAnnotationPresent( MappedSuperclass.class ) ) {
private static void detectMappedSuperclassProblems(ClassDetails annotatedClass) {
if ( annotatedClass.hasAnnotationUsage( MappedSuperclass.class ) ) {
//@Entity and @MappedSuperclass on the same class leads to a NPE down the road
if ( annotatedClass.isAnnotationPresent( Entity.class ) ) {
if ( annotatedClass.hasAnnotationUsage( Entity.class ) ) {
throw new AnnotationException( "Type '" + annotatedClass.getName()
+ "' is annotated both '@Entity' and '@MappedSuperclass'" );
}
if ( annotatedClass.isAnnotationPresent( Table.class ) ) {
if ( annotatedClass.hasAnnotationUsage( Table.class ) ) {
throw new AnnotationException( "Mapped superclass '" + annotatedClass.getName()
+ "' may not specify a '@Table'" );
}
if ( annotatedClass.isAnnotationPresent( Inheritance.class ) ) {
if ( annotatedClass.hasAnnotationUsage( Inheritance.class ) ) {
throw new AnnotationException( "Mapped superclass '" + annotatedClass.getName()
+ "' may not specify an '@Inheritance' mapping strategy" );
}
}
}
private static void bindTypeDescriptorRegistrations(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
private static void bindTypeDescriptorRegistrations(
AnnotationTarget annotatedElement,
MetadataBuildingContext context) {
final ManagedBeanRegistry managedBeanRegistry = context.getBootstrapContext()
.getServiceRegistry()
.getService( ManagedBeanRegistry.class );
final JavaTypeRegistration javaTypeRegistration =
annotatedElement.getAnnotation( JavaTypeRegistration.class );
if ( javaTypeRegistration != null ) {
handleJavaTypeRegistration( context, managedBeanRegistry, javaTypeRegistration );
}
else {
final JavaTypeRegistrations javaTypeRegistrations =
annotatedElement.getAnnotation( JavaTypeRegistrations.class );
if ( javaTypeRegistrations != null ) {
for ( JavaTypeRegistration registration : javaTypeRegistrations.value() ) {
handleJavaTypeRegistration( context, managedBeanRegistry, registration );
}
}
}
annotatedElement.forEachAnnotationUsage( JavaTypeRegistration.class, (usage) -> {
handleJavaTypeRegistration( context, managedBeanRegistry, usage );
} );
final JdbcTypeRegistration jdbcTypeRegistration =
annotatedElement.getAnnotation( JdbcTypeRegistration.class );
if ( jdbcTypeRegistration != null ) {
handleJdbcTypeRegistration( context, managedBeanRegistry, jdbcTypeRegistration );
}
else {
final JdbcTypeRegistrations jdbcTypeRegistrations =
annotatedElement.getAnnotation( JdbcTypeRegistrations.class );
if ( jdbcTypeRegistrations != null ) {
for ( JdbcTypeRegistration registration : jdbcTypeRegistrations.value() ) {
handleJdbcTypeRegistration( context, managedBeanRegistry, registration );
}
}
}
annotatedElement.forEachAnnotationUsage( JdbcTypeRegistration.class, (usage) -> {
handleJdbcTypeRegistration( context, managedBeanRegistry, usage );
} );
final CollectionTypeRegistration collectionTypeRegistration =
annotatedElement.getAnnotation( CollectionTypeRegistration.class );
if ( collectionTypeRegistration != null ) {
context.getMetadataCollector().addCollectionTypeRegistration( collectionTypeRegistration );
}
final CollectionTypeRegistrations collectionTypeRegistrations =
annotatedElement.getAnnotation( CollectionTypeRegistrations.class );
if ( collectionTypeRegistrations != null ) {
for ( CollectionTypeRegistration registration : collectionTypeRegistrations.value() ) {
context.getMetadataCollector().addCollectionTypeRegistration( registration );
}
}
annotatedElement.forEachAnnotationUsage( CollectionTypeRegistration.class, (usage) -> {
context.getMetadataCollector().addCollectionTypeRegistration( usage );
} );
}
private static void handleJdbcTypeRegistration(
MetadataBuildingContext context,
ManagedBeanRegistry managedBeanRegistry,
JdbcTypeRegistration annotation) {
final Class<? extends JdbcType> jdbcTypeClass = annotation.value();
AnnotationUsage<JdbcTypeRegistration> annotation) {
final Class<? extends JdbcType> jdbcTypeClass = annotation.getClassDetails( "value" ).toJavaClass();
final JdbcType jdbcType = !context.getBuildingOptions().isAllowExtensionsInCdi()
? FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( jdbcTypeClass )
: managedBeanRegistry.getBean( jdbcTypeClass ).getBeanInstance();
final int typeCode = annotation.registrationCode() == Integer.MIN_VALUE
final Integer registrationCode = annotation.getInteger( "registrationCode" );
final int typeCode = registrationCode == Integer.MIN_VALUE
? jdbcType.getDefaultSqlTypeCode()
: annotation.registrationCode();
: registrationCode;
context.getMetadataCollector().addJdbcTypeRegistration( typeCode, jdbcType );
}
private static void handleJavaTypeRegistration(
MetadataBuildingContext context,
ManagedBeanRegistry managedBeanRegistry,
JavaTypeRegistration annotation) {
final Class<? extends BasicJavaType<?>> javaTypeClass = annotation.descriptorClass();
final BasicJavaType<?> javaType =
!context.getBuildingOptions().isAllowExtensionsInCdi()
? FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass )
: managedBeanRegistry.getBean( javaTypeClass ).getBeanInstance();
context.getMetadataCollector().addJavaTypeRegistration( annotation.javaType(), javaType );
AnnotationUsage<JavaTypeRegistration> annotation) {
final Class<? extends BasicJavaType<?>> javaTypeClass = annotation.getClassDetails( "descriptorClass" ).toJavaClass();
final BasicJavaType<?> javaType = !context.getBuildingOptions().isAllowExtensionsInCdi()
? FallbackBeanInstanceProducer.INSTANCE.produceBeanInstance( javaTypeClass )
: managedBeanRegistry.getBean( javaTypeClass ).getBeanInstance();
context.getMetadataCollector().addJavaTypeRegistration(
annotation.getClassDetails( "javaType" ).toJavaClass(),
javaType
);
}
private static void bindEmbeddableInstantiatorRegistrations(
XAnnotatedElement annotatedElement,
AnnotationTarget annotatedElement,
MetadataBuildingContext context) {
final EmbeddableInstantiatorRegistration embeddableInstantiatorRegistration =
annotatedElement.getAnnotation( EmbeddableInstantiatorRegistration.class );
if ( embeddableInstantiatorRegistration != null ) {
handleEmbeddableInstantiatorRegistration( context, embeddableInstantiatorRegistration );
}
else {
final EmbeddableInstantiatorRegistrations embeddableInstantiatorRegistrations =
annotatedElement.getAnnotation( EmbeddableInstantiatorRegistrations.class );
if ( embeddableInstantiatorRegistrations != null ) {
for ( EmbeddableInstantiatorRegistration registration : embeddableInstantiatorRegistrations.value() ) {
handleEmbeddableInstantiatorRegistration( context, registration );
}
}
}
annotatedElement.forEachAnnotationUsage( EmbeddableInstantiatorRegistration.class, (usage) -> {
handleEmbeddableInstantiatorRegistration( context, usage );
} );
}
private static void handleEmbeddableInstantiatorRegistration(
MetadataBuildingContext context,
EmbeddableInstantiatorRegistration annotation) {
AnnotationUsage<EmbeddableInstantiatorRegistration> annotation) {
context.getMetadataCollector().registerEmbeddableInstantiator(
annotation.embeddableClass(),
annotation.instantiator()
annotation.getClassDetails( "embeddableClass" ).toJavaClass(),
annotation.getClassDetails( "instantiator" ).toJavaClass()
);
}
private static void bindCompositeUserTypeRegistrations(
XAnnotatedElement annotatedElement,
AnnotationTarget annotatedElement,
MetadataBuildingContext context) {
final CompositeTypeRegistration compositeTypeRegistration =
annotatedElement.getAnnotation( CompositeTypeRegistration.class );
if ( compositeTypeRegistration != null ) {
handleCompositeUserTypeRegistration( context, compositeTypeRegistration );
}
else {
final CompositeTypeRegistrations compositeTypeRegistrations =
annotatedElement.getAnnotation( CompositeTypeRegistrations.class );
if ( compositeTypeRegistrations != null ) {
for ( CompositeTypeRegistration registration : compositeTypeRegistrations.value() ) {
handleCompositeUserTypeRegistration( context, registration );
}
}
}
annotatedElement.forEachAnnotationUsage( CompositeTypeRegistration.class, (usage) -> {
handleCompositeUserTypeRegistration( context, usage );
} );
}
private static void bindUserTypeRegistrations(
XAnnotatedElement annotatedElement,
AnnotationTarget annotatedElement,
MetadataBuildingContext context) {
final TypeRegistration typeRegistration =
annotatedElement.getAnnotation( TypeRegistration.class );
if ( typeRegistration != null ) {
handleUserTypeRegistration( context, typeRegistration );
}
else {
final TypeRegistrations typeRegistrations =
annotatedElement.getAnnotation( TypeRegistrations.class );
if ( typeRegistrations != null ) {
for ( TypeRegistration registration : typeRegistrations.value() ) {
handleUserTypeRegistration( context, registration );
}
}
}
annotatedElement.forEachAnnotationUsage( TypeRegistration.class, (usage) -> {
handleUserTypeRegistration( context, usage );
} );
}
private static void handleUserTypeRegistration(
MetadataBuildingContext context,
TypeRegistration compositeTypeRegistration) {
AnnotationUsage<TypeRegistration> compositeTypeRegistration) {
// TODO: check that the two classes agree, i.e. that
// the user type knows how to handle the type
context.getMetadataCollector().registerUserType(
compositeTypeRegistration.basicClass(),
compositeTypeRegistration.userType()
compositeTypeRegistration.getClassDetails( "basicClass" ).toJavaClass(),
compositeTypeRegistration.getClassDetails( "userType" ).toJavaClass()
);
}
private static void handleCompositeUserTypeRegistration(
MetadataBuildingContext context,
CompositeTypeRegistration compositeTypeRegistration) {
AnnotationUsage<CompositeTypeRegistration> compositeTypeRegistration) {
// TODO: check that the two classes agree, i.e. that
// the user type knows how to handle the type
context.getMetadataCollector().registerCompositeUserType(
compositeTypeRegistration.embeddableClass(),
compositeTypeRegistration.userType()
compositeTypeRegistration.getClassDetails( "embeddableClass" ).toJavaClass(),
compositeTypeRegistration.getClassDetails( "userType" ).toJavaClass()
);
}
private static void bindConverterRegistrations(XAnnotatedElement container, MetadataBuildingContext context) {
final ConverterRegistration converterRegistration = container.getAnnotation( ConverterRegistration.class );
if ( converterRegistration != null ) {
handleConverterRegistration( converterRegistration, context );
}
else {
final ConverterRegistrations converterRegistrations = container.getAnnotation( ConverterRegistrations.class );
if ( converterRegistrations != null ) {
for ( ConverterRegistration registration : converterRegistrations.value() ) {
handleConverterRegistration( registration, context );
}
}
}
private static void bindConverterRegistrations(AnnotationTarget container, MetadataBuildingContext context) {
container.forEachAnnotationUsage( ConverterRegistration.class, (usage) -> {
handleConverterRegistration( usage, context );
} );
}
private static void handleConverterRegistration(ConverterRegistration registration, MetadataBuildingContext context) {
context.getMetadataCollector().getConverterRegistry()
.addRegisteredConversion(
new RegisteredConversion(
registration.domainType(),
registration.converter(),
registration.autoApply(),
context
)
);
private static void handleConverterRegistration(AnnotationUsage<ConverterRegistration> registration, MetadataBuildingContext context) {
context.getMetadataCollector().getConverterRegistry().addRegisteredConversion( new RegisteredConversion(
registration.getClassDetails( "domainType" ).toJavaClass(),
registration.getClassDetails( "converter" ).toJavaClass(),
registration.getBoolean( "autoApply" ),
context
) );
}
public static void bindFetchProfilesForClass(XClass annotatedClass, MetadataBuildingContext context) {
public static void bindFetchProfilesForClass(AnnotationTarget annotatedClass, MetadataBuildingContext context) {
bindFetchProfiles( annotatedClass, context );
}
public static void bindFetchProfilesForPackage(ClassLoaderService cls, String packageName, MetadataBuildingContext context) {
final Package pack = cls.packageForNameOrNull( packageName );
if ( pack != null ) {
final ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();
bindFetchProfiles( reflectionManager.toXPackage( pack ), context );
final ClassDetails packageInfoClassDetails = context
.getMetadataCollector()
.getClassDetailsRegistry()
.findClassDetails( packageName + ".package-info" );
if ( packageInfoClassDetails != null ) {
bindFetchProfiles( packageInfoClassDetails, context );
}
}
private static void bindFetchProfiles(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
final FetchProfile fetchProfile = annotatedElement.getAnnotation( FetchProfile.class );
final FetchProfiles fetchProfiles = annotatedElement.getAnnotation( FetchProfiles.class );
if ( fetchProfile != null ) {
bindFetchProfile( fetchProfile, context );
}
if ( fetchProfiles != null ) {
for ( FetchProfile profile : fetchProfiles.value() ) {
bindFetchProfile( profile, context );
}
}
private static void bindFetchProfiles(AnnotationTarget annotatedElement, MetadataBuildingContext context) {
annotatedElement.forEachAnnotationUsage( FetchProfile.class, (usage) -> {
bindFetchProfile( usage, context );
} );
}
private static void bindFetchProfile(FetchProfile fetchProfile, MetadataBuildingContext context) {
final String name = fetchProfile.name();
private static void bindFetchProfile(AnnotationUsage<FetchProfile> fetchProfile, MetadataBuildingContext context) {
final String name = fetchProfile.getString( "name" );
if ( reuseOrCreateFetchProfile( context, name ) ) {
for ( FetchOverride fetch : fetchProfile.fetchOverrides() ) {
if ( fetch.fetch() == FetchType.LAZY && fetch.mode() == FetchMode.JOIN ) {
throw new AnnotationException( "Fetch profile '" + name
+ "' has a '@FetchOverride' with 'fetch=LAZY' and 'mode=JOIN'"
+ " (join fetching is eager by nature)");
final List<AnnotationUsage<FetchOverride>> fetchOverrides = fetchProfile.getList( "fetchOverrides" );
for ( AnnotationUsage<FetchOverride> fetchOverride : fetchOverrides ) {
final FetchType type = fetchOverride.getEnum( "fetch" );
final FetchMode mode = fetchOverride.getEnum( "mode" );
if ( type == FetchType.LAZY && mode == FetchMode.JOIN ) {
throw new AnnotationException(
"Fetch profile '" + name
+ "' has a '@FetchOverride' with 'fetch=LAZY' and 'mode=JOIN'"
+ " (join fetching is eager by nature)"
);
}
context.getMetadataCollector()
.addSecondPass( new FetchOverrideSecondPass( name, fetch, context ) );
context.getMetadataCollector().addSecondPass( new FetchOverrideSecondPass( name, fetchOverride, context ) );
}
}
// otherwise, it's a fetch profile defined in XML, and it overrides
@ -686,17 +468,15 @@ public final class AnnotationBinder {
*
* @return A map of {@code InheritanceState}s keyed against their {@code XClass}.
*/
public static Map<XClass, InheritanceState> buildInheritanceStates(
List<XClass> orderedClasses,
public static Map<ClassDetails, InheritanceState> buildInheritanceStates(
List<ClassDetails> orderedClasses,
MetadataBuildingContext buildingContext) {
final Map<XClass, InheritanceState> inheritanceStatePerClass = new HashMap<>( orderedClasses.size() );
for ( XClass clazz : orderedClasses ) {
final InheritanceState superclassState =
getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
final InheritanceState state =
new InheritanceState( clazz, inheritanceStatePerClass, buildingContext );
final Map<ClassDetails, InheritanceState> inheritanceStatePerClass = new HashMap<>( orderedClasses.size() );
for ( ClassDetails clazz : orderedClasses ) {
final InheritanceState superclassState = getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
final InheritanceState state = new InheritanceState( clazz, inheritanceStatePerClass, buildingContext );
final AnnotatedClassType classType = buildingContext.getMetadataCollector().getClassType( clazz );
if ( classType == EMBEDDABLE && !clazz.isAnnotationPresent( Imported.class ) ) {
if ( classType == EMBEDDABLE && !clazz.hasAnnotationUsage( Imported.class ) ) {
final String className = clazz.getName();
buildingContext.getMetadataCollector().addImport( unqualify( className ), className );
}
@ -709,7 +489,7 @@ public final class AnnotationBinder {
state.setHasParents( true );
if ( classType == EMBEDDABLE ) {
buildingContext.getMetadataCollector().registerEmbeddableSubclass(
superEntityState.getClazz(),
superEntityState.getClassDetails(),
clazz
);
}
@ -729,12 +509,12 @@ public final class AnnotationBinder {
return inheritanceStatePerClass;
}
private static void logMixedInheritance(XClass clazz, InheritanceState superclassState, InheritanceState state) {
private static void logMixedInheritance(ClassDetails classDetails, InheritanceState superclassState, InheritanceState state) {
if ( state.getType() != null && superclassState.getType() != null ) {
final boolean nonDefault = InheritanceType.SINGLE_TABLE != state.getType();
final boolean mixingStrategy = state.getType() != superclassState.getType();
if ( nonDefault && mixingStrategy ) {
throw new AnnotationException( "Entity '" + clazz.getName()
throw new AnnotationException( "Entity '" + classDetails.getName()
+ "' may not override the inheritance mapping strategy '" + superclassState.getType()
+ "' of its hierarchy"
+ "' (each entity hierarchy has a single inheritance mapping strategy)" );

View File

@ -0,0 +1,28 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.model.internal;
import java.util.HashMap;
import java.util.List;
import org.hibernate.annotations.Parameter;
import org.hibernate.models.spi.AnnotationUsage;
import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize;
/**
* @author Steve Ebersole
*/
public class AnnotationHelper {
public static HashMap<String, String> extractParameterMap(List<AnnotationUsage<Parameter>> parameters) {
final HashMap<String,String> paramMap = mapOfSize( parameters.size() );
parameters.forEach( (usage) -> {
paramMap.put( usage.getString( "name" ), usage.getString( "value" ) );
} );
return paramMap;
}
}

View File

@ -6,9 +6,8 @@
*/
package org.hibernate.boot.model.internal;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinTable;
import java.util.Locale;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.annotations.Cascade;
@ -16,14 +15,17 @@ import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.Property;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MemberDetails;
import java.util.Locale;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinTable;
import static org.hibernate.boot.model.internal.BinderHelper.getCascadeStrategy;
import static org.hibernate.boot.model.internal.BinderHelper.getOverridableAnnotation;
@ -38,12 +40,12 @@ public class AnyBinder {
EntityBinder entityBinder,
boolean isIdentifierMapper,
MetadataBuildingContext context,
XProperty property,
MemberDetails property,
AnnotatedJoinColumns joinColumns,
boolean forcePersist) {
//check validity
if ( property.isAnnotationPresent( Columns.class ) ) {
if ( property.hasAnnotationUsage( Columns.class ) ) {
throw new AnnotationException(
String.format(
Locale.ROOT,
@ -54,9 +56,9 @@ public class AnyBinder {
);
}
final Cascade hibernateCascade = property.getAnnotation( Cascade.class );
final OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
final JoinTable assocTable = propertyHolder.getJoinTable(property);
final AnnotationUsage<Cascade> hibernateCascade = property.getAnnotationUsage( Cascade.class );
final AnnotationUsage<OnDelete> onDeleteAnn = property.getAnnotationUsage( OnDelete.class );
final AnnotationUsage<JoinTable> assocTable = propertyHolder.getJoinTable( property );
if ( assocTable != null ) {
final Join join = propertyHolder.addJoin( assocTable, false );
for ( AnnotatedJoinColumn joinColumn : joinColumns.getJoinColumns() ) {
@ -67,7 +69,7 @@ public class AnyBinder {
getCascadeStrategy( null, hibernateCascade, false, forcePersist ),
//@Any has no cascade attribute
joinColumns,
onDeleteAnn == null ? null : onDeleteAnn.action(),
onDeleteAnn == null ? null : onDeleteAnn.getEnum( "action" ),
nullability,
propertyHolder,
inferredData,
@ -87,16 +89,16 @@ public class AnyBinder {
EntityBinder entityBinder,
boolean isIdentifierMapper,
MetadataBuildingContext context) {
final XProperty property = inferredData.getProperty();
final org.hibernate.annotations.Any any = property.getAnnotation( org.hibernate.annotations.Any.class );
final MemberDetails property = inferredData.getAttributeMember();
final AnnotationUsage<org.hibernate.annotations.Any> any = property.getAnnotationUsage( org.hibernate.annotations.Any.class );
if ( any == null ) {
throw new AssertionFailure( "Missing @Any annotation: " + getPath( propertyHolder, inferredData ) );
}
final boolean lazy = any.fetch() == FetchType.LAZY;
final boolean optional = any.optional();
final boolean lazy = any.getEnum( "fetch" ) == FetchType.LAZY;
final boolean optional = any.getBoolean( "optional" );
final Any value = BinderHelper.buildAnyValue(
property.getAnnotation( Column.class ),
property.getAnnotationUsage( Column.class ),
getOverridableAnnotation( property, Formula.class, context ),
columns,
inferredData,
@ -127,7 +129,7 @@ public class AnyBinder {
Property prop = binder.makeProperty();
prop.setOptional( optional && value.isNullable() );
//composite FK columns are in the same table, so it's OK
propertyHolder.addProperty( prop, columns, inferredData.getDeclaringClass() );
propertyHolder.addProperty( prop, inferredData.getAttributeMember(), columns, inferredData.getDeclaringClass() );
binder.callAttributeBindersInSecondPass( prop );
}
}

View File

@ -6,7 +6,9 @@
*/
package org.hibernate.boot.model.internal;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Convert;
@ -22,30 +24,28 @@ import jakarta.persistence.Convert;
public class AttributeConversionInfo {
private final Class<? extends AttributeConverter<?,?>> converterClass;
private final boolean conversionDisabled;
private final String attributeName;
private final XAnnotatedElement source;
private final AnnotationTarget source;
@SuppressWarnings({ "unchecked", "rawtypes" })
public AttributeConversionInfo(
Class<? extends AttributeConverter> converterClass,
boolean conversionDisabled,
String attributeName,
XAnnotatedElement source) {
AnnotationTarget source) {
this.converterClass = (Class<? extends AttributeConverter<?, ?>>) converterClass;
this.conversionDisabled = conversionDisabled;
this.attributeName = attributeName;
this.source = source;
}
@SuppressWarnings("unchecked")
public AttributeConversionInfo(Convert convertAnnotation, XAnnotatedElement xAnnotatedElement) {
public AttributeConversionInfo(AnnotationUsage<Convert> convertAnnotation, AnnotationTarget source) {
this(
convertAnnotation.converter(),
convertAnnotation.disableConversion(),
convertAnnotation.attributeName(),
xAnnotatedElement
convertAnnotation.getClassDetails( "converter" ).toJavaClass(),
convertAnnotation.getBoolean( "disableConversion" ),
convertAnnotation.getString( "attributeName" ),
source
);
}
@ -75,7 +75,7 @@ public class AttributeConversionInfo {
/**
* The annotated element
*/
public XAnnotatedElement getSource() {
public AnnotationTarget getSource() {
return source;
}
}

View File

@ -7,20 +7,15 @@
package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation;
import java.lang.annotation.Repeatable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Consumer;
import java.util.stream.Stream;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
@ -30,20 +25,13 @@ import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyDiscriminatorValues;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.DialectOverride;
import org.hibernate.annotations.Formula;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.SqlFragmentAlias;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XPackage;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.dialect.DatabaseVersion;
import org.hibernate.dialect.Dialect;
import org.hibernate.mapping.Any;
import org.hibernate.mapping.AttributeContainer;
import org.hibernate.mapping.BasicValue;
@ -59,6 +47,13 @@ import org.hibernate.mapping.SyntheticProperty;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
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.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.models.spi.TypeDetailsHelper;
import org.hibernate.type.descriptor.java.JavaType;
import jakarta.persistence.ConstraintMode;
@ -72,7 +67,6 @@ import jakarta.persistence.OneToOne;
import static jakarta.persistence.ConstraintMode.NO_CONSTRAINT;
import static jakarta.persistence.ConstraintMode.PROVIDER_DEFAULT;
import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnOrFormulaFromAnnotation;
import static org.hibernate.boot.model.internal.HCANNHelper.findAnnotation;
import static org.hibernate.internal.util.StringHelper.isEmpty;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
import static org.hibernate.internal.util.StringHelper.qualifier;
@ -749,8 +743,8 @@ public class BinderHelper {
}
public static Any buildAnyValue(
jakarta.persistence.Column discriminatorColumn,
Formula discriminatorFormula,
AnnotationUsage<jakarta.persistence.Column> discriminatorColumn,
AnnotationUsage<Formula> discriminatorFormula,
AnnotatedJoinColumns keyColumns,
PropertyData inferredData,
OnDeleteAction onDeleteAction,
@ -760,7 +754,7 @@ public class BinderHelper {
EntityBinder entityBinder,
boolean optional,
MetadataBuildingContext context) {
final XProperty property = inferredData.getProperty();
final MemberDetails property = inferredData.getAttributeMember();
final Any value = new Any( context, keyColumns.getTable(), true );
value.setLazy( lazy );
@ -803,10 +797,10 @@ public class BinderHelper {
final Map<Object,Class<?>> discriminatorValueMappings = new HashMap<>();
processAnyDiscriminatorValues(
inferredData.getProperty(),
inferredData.getAttributeMember(),
valueMapping -> discriminatorValueMappings.put(
discriminatorJavaType.wrap( valueMapping.discriminator(), null ),
valueMapping.entity()
discriminatorJavaType.wrap( valueMapping.getString( "discriminator" ), null ),
valueMapping.getClassDetails( "entity" ).toJavaClass()
)
);
value.setDiscriminatorValueMappings( discriminatorValueMappings );
@ -831,25 +825,23 @@ public class BinderHelper {
}
private static void processAnyDiscriminatorValues(
XProperty property,
Consumer<AnyDiscriminatorValue> consumer) {
final AnyDiscriminatorValue valueAnn = findAnnotation( property, AnyDiscriminatorValue.class );
MemberDetails property,
Consumer<AnnotationUsage<AnyDiscriminatorValue>> consumer) {
final AnnotationUsage<AnyDiscriminatorValue> valueAnn = property.locateAnnotationUsage( AnyDiscriminatorValue.class );
if ( valueAnn != null ) {
consumer.accept( valueAnn );
return;
}
final AnyDiscriminatorValues valuesAnn = findAnnotation( property, AnyDiscriminatorValues.class );
final AnnotationUsage<AnyDiscriminatorValues> valuesAnn = property.locateAnnotationUsage( AnyDiscriminatorValues.class );
if ( valuesAnn != null ) {
for ( AnyDiscriminatorValue discriminatorValue : valuesAnn.value() ) {
consumer.accept( discriminatorValue );
}
final List<AnnotationUsage<AnyDiscriminatorValue>> nestedList = valuesAnn.getList( "value" );
nestedList.forEach( consumer );
}
}
public static MappedSuperclass getMappedSuperclassOrNull(
XClass declaringClass,
Map<XClass, InheritanceState> inheritanceStatePerClass,
ClassDetails declaringClass,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext context) {
boolean retrieve = false;
if ( declaringClass != null ) {
@ -865,9 +857,7 @@ public class BinderHelper {
}
if ( retrieve ) {
return context.getMetadataCollector().getMappedSuperclass(
context.getBootstrapContext().getReflectionManager().toClass( declaringClass )
);
return context.getMetadataCollector().getMappedSuperclass( declaringClass.toJavaClass() );
}
else {
return null;
@ -883,138 +873,51 @@ public class BinderHelper {
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext buildingContext) {
final XClass mappedClass = buildingContext.getBootstrapContext().getReflectionManager()
.toXClass( propertyHolder.getPersistentClass().getMappedClass() );
final ClassDetailsRegistry classDetailsRegistry = buildingContext.getMetadataCollector()
.getSourceModelBuildingContext()
.getClassDetailsRegistry();
final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails( propertyHolder.getPersistentClass().getClassName() );
final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector();
if ( propertyHolder.isInIdClass() ) {
final PropertyData data = metadataCollector.getPropertyAnnotatedWithIdAndToOne( mappedClass, propertyName );
final PropertyData data = metadataCollector.getPropertyAnnotatedWithIdAndToOne( classDetails, propertyName );
if ( data != null ) {
return data;
}
// TODO: is this branch even necessary?
else if ( buildingContext.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) {
return metadataCollector.getPropertyAnnotatedWithMapsId( mappedClass, propertyName );
return metadataCollector.getPropertyAnnotatedWithMapsId( classDetails, propertyName );
}
}
return metadataCollector.getPropertyAnnotatedWithMapsId( mappedClass, isId ? "" : propertyName );
return metadataCollector.getPropertyAnnotatedWithMapsId( classDetails, isId ? "" : propertyName );
}
public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
public static Map<String,String> toAliasTableMap(List<AnnotationUsage<SqlFragmentAlias>> aliases){
final Map<String,String> ret = new HashMap<>();
for ( SqlFragmentAlias alias : aliases ) {
if ( isNotEmpty( alias.table() ) ) {
ret.put( alias.alias(), alias.table() );
for ( AnnotationUsage<SqlFragmentAlias> aliasAnnotation : aliases ) {
final String table = aliasAnnotation.getString( "table" );
if ( isNotEmpty( table ) ) {
ret.put( aliasAnnotation.getString( "alias" ), table );
}
}
return ret;
}
public static Map<String,String> toAliasEntityMap(SqlFragmentAlias[] aliases){
public static Map<String,String> toAliasEntityMap(List<AnnotationUsage<SqlFragmentAlias>> aliases){
final Map<String,String> result = new HashMap<>();
for ( SqlFragmentAlias alias : aliases ) {
if ( alias.entity() != void.class ) {
result.put( alias.alias(), alias.entity().getName() );
for ( AnnotationUsage<SqlFragmentAlias> aliasAnnotation : aliases ) {
final ClassDetails entityClassDetails = aliasAnnotation.getClassDetails( "entity" );
if ( entityClassDetails != ClassDetails.VOID_CLASS_DETAILS ) {
result.put( aliasAnnotation.getString( "alias" ), entityClassDetails.getName() );
}
}
return result;
}
public static boolean hasToOneAnnotation(XAnnotatedElement property) {
return property.isAnnotationPresent(ManyToOne.class)
|| property.isAnnotationPresent(OneToOne.class);
public static boolean hasToOneAnnotation(AnnotationTarget property) {
return property.hasAnnotationUsage(ManyToOne.class)
|| property.hasAnnotationUsage(OneToOne.class);
}
public static <T extends Annotation> T getOverridableAnnotation(
XAnnotatedElement element,
Class<T> annotationType,
MetadataBuildingContext context) {
final Dialect dialect = context.getMetadataCollector().getDatabase().getDialect();
final Iterator<Annotation> annotations =
Arrays.stream( element.getAnnotations() )
.flatMap( annotation -> {
final Method valueExtractor = isRepeableAndDialectOverride( annotation );
if ( valueExtractor != null ) {
try {
return Stream.of( (Annotation[]) valueExtractor.invoke( annotation ) );
}
catch (Exception e) {
throw new AssertionFailure("could not read @DialectOverride annotation", e);
}
}
return Stream.of( annotation );
} ).iterator();
while ( annotations.hasNext() ) {
final Annotation annotation = annotations.next();
final Class<? extends Annotation> type = annotation.annotationType();
final DialectOverride.OverridesAnnotation overridesAnnotation =
type.getAnnotation(DialectOverride.OverridesAnnotation.class);
if ( overridesAnnotation != null
&& overridesAnnotation.value().equals(annotationType) ) {
try {
//noinspection unchecked
final Class<? extends Dialect> overrideDialect = (Class<? extends Dialect>)
type.getDeclaredMethod("dialect").invoke(annotation);
if ( overrideDialect.isAssignableFrom( dialect.getClass() ) ) {
final DialectOverride.Version before = (DialectOverride.Version)
type.getDeclaredMethod("before").invoke(annotation);
final DialectOverride.Version sameOrAfter = (DialectOverride.Version)
type.getDeclaredMethod("sameOrAfter").invoke(annotation);
DatabaseVersion version = dialect.getVersion();
if ( version.isBefore( before.major(), before.minor() )
&& version.isSameOrAfter( sameOrAfter.major(), sameOrAfter.minor() ) ) {
//noinspection unchecked
return (T) type.getDeclaredMethod("override").invoke(annotation);
}
}
}
catch (Exception e) {
throw new AssertionFailure("could not read @DialectOverride annotation", e);
}
}
}
return element.getAnnotation( annotationType );
}
//Wondering: should we make this cache non-static and store in the metadata context so that we can clear it after bootstrap?
//(not doing it now as it would make the patch more invasive - and there might be drawbacks to consider, such as
//needing to re-initialize this cache again during hot-reload scenarios: it might be nice to be able to plug the
//cache, so that runtimes can choose the most fitting strategy)
private static final ClassValue<AnnotationCacheValue> annotationMetaCacheForRepeatableDialectOverride = new ClassValue() {
@Override
protected Object computeValue(final Class type) {
final Method valueMethod;
try {
valueMethod = type.getDeclaredMethod( "value" );
}
catch ( NoSuchMethodException e ) {
return NOT_REPEATABLE;
}
final Class<?> returnType = valueMethod.getReturnType();
if ( returnType.isArray()
&& returnType.getComponentType().isAnnotationPresent( Repeatable.class )
&& returnType.getComponentType().isAnnotationPresent( DialectOverride.OverridesAnnotation.class ) ) {
return new AnnotationCacheValue( valueMethod );
}
else {
return NOT_REPEATABLE;
}
}
};
private static final AnnotationCacheValue NOT_REPEATABLE = new AnnotationCacheValue( null );
private static class AnnotationCacheValue {
final Method valueMethod;
private AnnotationCacheValue(final Method valueMethod) {
//null is intentionally allowed: it means this annotations was NOT a Repeatable & DialectOverride.OverridesAnnotation annotation,
//which is also an information we want to cache (negative caching).
this.valueMethod = valueMethod;
}
}
private static Method isRepeableAndDialectOverride(final Annotation annotation) {
return annotationMetaCacheForRepeatableDialectOverride.get( annotation.annotationType() ).valueMethod;
}
public static FetchMode getFetchMode(FetchType fetch) {
switch ( fetch ) {
@ -1046,7 +949,7 @@ public class BinderHelper {
}
}
private static EnumSet<CascadeType> convertToHibernateCascadeType(jakarta.persistence.CascadeType[] ejbCascades) {
private static EnumSet<CascadeType> convertToHibernateCascadeType(List<jakarta.persistence.CascadeType> ejbCascades) {
final EnumSet<CascadeType> cascadeTypes = EnumSet.noneOf( CascadeType.class );
if ( ejbCascades != null ) {
for ( jakarta.persistence.CascadeType cascade: ejbCascades ) {
@ -1057,14 +960,16 @@ public class BinderHelper {
}
public static String getCascadeStrategy(
jakarta.persistence.CascadeType[] ejbCascades,
Cascade hibernateCascadeAnnotation,
List<jakarta.persistence.CascadeType> ejbCascades,
AnnotationUsage<Cascade> hibernateCascadeAnnotation,
boolean orphanRemoval,
boolean forcePersist) {
final EnumSet<CascadeType> cascadeTypes = convertToHibernateCascadeType( ejbCascades );
final CascadeType[] hibernateCascades = hibernateCascadeAnnotation == null ? null : hibernateCascadeAnnotation.value();
if ( hibernateCascades != null && hibernateCascades.length > 0 ) {
cascadeTypes.addAll( Arrays.asList( hibernateCascades ) );
final List<CascadeType> hibernateCascades = hibernateCascadeAnnotation == null
? null
: hibernateCascadeAnnotation.getList( "value" );
if ( hibernateCascades != null && !hibernateCascades.isEmpty() ) {
cascadeTypes.addAll( hibernateCascades );
}
if ( orphanRemoval ) {
cascadeTypes.add( CascadeType.DELETE_ORPHAN );
@ -1120,13 +1025,18 @@ public class BinderHelper {
return context.getBootstrapContext().getJpaCompliance().isGlobalGeneratorScopeEnabled();
}
static boolean isCompositeId(XClass entityClass, XProperty idProperty) {
return entityClass.isAnnotationPresent( Embeddable.class )
|| idProperty.isAnnotationPresent( EmbeddedId.class );
static boolean isCompositeId(ClassDetails entityClass, MemberDetails idProperty) {
return entityClass.hasAnnotationUsage( Embeddable.class )
|| idProperty.hasAnnotationUsage( EmbeddedId.class );
}
public static boolean isDefault(XClass clazz, MetadataBuildingContext context) {
return context.getBootstrapContext().getReflectionManager().equals( clazz, void.class );
public static boolean isDefault(ClassDetails clazz, MetadataBuildingContext context) {
return clazz == ClassDetails.VOID_CLASS_DETAILS;
}
public static boolean isDefault(TypeDetails clazz, MetadataBuildingContext context) {
final ClassDetails rawClassDetails = TypeDetailsHelper.resolveRawClass( clazz );
return rawClassDetails == ClassDetails.VOID_CLASS_DETAILS;
}
public static void checkMappedByType(
@ -1176,12 +1086,12 @@ public class BinderHelper {
return false;
}
public static boolean noConstraint(ForeignKey foreignKey, boolean noConstraintByDefault) {
public static boolean noConstraint(AnnotationUsage<ForeignKey> foreignKey, boolean noConstraintByDefault) {
if ( foreignKey == null ) {
return false;
}
else {
final ConstraintMode mode = foreignKey.value();
final ConstraintMode mode = foreignKey.getEnum( "value" );
return mode == NO_CONSTRAINT
|| mode == PROVIDER_DEFAULT && noConstraintByDefault;
}
@ -1191,14 +1101,14 @@ public class BinderHelper {
* Extract an annotation from the package-info for the package the given class is defined in
*
* @param annotationType The type of annotation to return
* @param xClass The class in the package
* @param classDetails The class in the package
* @param context The processing context
*
* @return The annotation or {@code null}
*/
public static <A extends Annotation> A extractFromPackage(
public static <A extends Annotation> AnnotationUsage<A> extractFromPackage(
Class<A> annotationType,
XClass xClass,
ClassDetails classDetails,
MetadataBuildingContext context) {
// todo (soft-delete) : or if we want caching of this per package
@ -1208,19 +1118,23 @@ public class BinderHelper {
// where context.getMetadataCollector() can cache some of this - either the annotations themselves
// or even just the XPackage resolutions
final String declaringClassName = xClass.getName();
final String declaringClassName = classDetails.getName();
final String packageName = qualifier( declaringClassName );
if ( isNotEmpty( packageName ) ) {
final ClassLoaderService classLoaderService =
context.getBootstrapContext().getServiceRegistry()
.requireService( ClassLoaderService.class );
final Package declaringClassPackage = classLoaderService.packageForNameOrNull( packageName );
if ( declaringClassPackage != null ) {
// will be null when there is no `package-info.class`
final XPackage xPackage = context.getBootstrapContext().getReflectionManager().toXPackage( declaringClassPackage );
return xPackage.getAnnotation( annotationType );
}
if ( isEmpty( packageName ) ) {
return null;
}
final ClassDetailsRegistry classDetailsRegistry = context.getMetadataCollector()
.getSourceModelBuildingContext()
.getClassDetailsRegistry();
final String packageInfoName = packageName + ".package-info";
try {
final ClassDetails packageInfoClassDetails = classDetailsRegistry.resolveClassDetails( packageInfoName );
return packageInfoClassDetails.getAnnotationUsage( annotationType );
}
catch (ClassLoadingException ignore) {}
return null;
}
}

View File

@ -16,8 +16,6 @@ import java.util.function.Consumer;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.PropertyNotFoundException;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.boot.spi.SecondPass;
@ -34,9 +32,14 @@ import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
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 org.hibernate.models.spi.TypeDetails;
import jakarta.persistence.Convert;
import jakarta.persistence.Converts;
import jakarta.persistence.JoinTable;
import static org.hibernate.internal.util.StringHelper.isEmpty;
@ -50,16 +53,16 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
private Map<String, Join> joins;
private transient Map<String, Join> joinsPerRealTableName;
private EntityBinder entityBinder;
private final Map<XClass, InheritanceState> inheritanceStatePerClass;
private final Map<ClassDetails, InheritanceState> inheritanceStatePerClass;
private final Map<String,AttributeConversionInfo> attributeConversionInfoMap;
public ClassPropertyHolder(
PersistentClass persistentClass,
XClass entityXClass,
ClassDetails entityXClass,
Map<String, Join> joins,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
Map<ClassDetails, InheritanceState> inheritanceStatePerClass) {
super( persistentClass.getEntityName(), null, entityXClass, context );
this.persistentClass = persistentClass;
this.joins = joins;
@ -70,10 +73,10 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
public ClassPropertyHolder(
PersistentClass persistentClass,
XClass entityXClass,
ClassDetails entityXClass,
EntityBinder entityBinder,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
Map<ClassDetails, InheritanceState> inheritanceStatePerClass) {
this( persistentClass, entityXClass, entityBinder.getSecondaryTables(), context, inheritanceStatePerClass );
this.entityBinder = entityBinder;
}
@ -88,54 +91,39 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
return getEntityName() + '.' + attributeName;
}
protected Map<String, AttributeConversionInfo> buildAttributeConversionInfoMap(XClass entityXClass) {
protected Map<String, AttributeConversionInfo> buildAttributeConversionInfoMap(ClassDetails entityClassDetails) {
final HashMap<String, AttributeConversionInfo> map = new HashMap<>();
collectAttributeConversionInfo( map, entityXClass );
collectAttributeConversionInfo( map, entityClassDetails );
return map;
}
private void collectAttributeConversionInfo(Map<String, AttributeConversionInfo> infoMap, XClass xClass) {
if ( xClass == null ) {
private void collectAttributeConversionInfo(Map<String, AttributeConversionInfo> infoMap, ClassDetails entityClassDetails) {
if ( entityClassDetails == null ) {
// typically indicates we have reached the end of the inheritance hierarchy
return;
}
// collect superclass info first
collectAttributeConversionInfo( infoMap, xClass.getSuperclass() );
collectAttributeConversionInfo( infoMap, entityClassDetails.getSuperClass() );
final boolean canContainConvert = xClass.isAnnotationPresent( jakarta.persistence.Entity.class )
|| xClass.isAnnotationPresent( jakarta.persistence.MappedSuperclass.class )
|| xClass.isAnnotationPresent( jakarta.persistence.Embeddable.class );
final boolean canContainConvert = entityClassDetails.hasAnnotationUsage( jakarta.persistence.Entity.class )
|| entityClassDetails.hasAnnotationUsage( jakarta.persistence.MappedSuperclass.class )
|| entityClassDetails.hasAnnotationUsage( jakarta.persistence.Embeddable.class );
if ( ! canContainConvert ) {
return;
}
{
final Convert convertAnnotation = xClass.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, xClass );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Convert placed on @Entity/@MappedSuperclass must define attributeName" );
}
infoMap.put( info.getAttributeName(), info );
entityClassDetails.forEachAnnotationUsage( Convert.class, (usage) -> {
final AttributeConversionInfo info = new AttributeConversionInfo( usage, entityClassDetails );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Convert placed on @Entity/@MappedSuperclass must define attributeName" );
}
}
{
final Converts convertsAnnotation = xClass.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, xClass );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Converts placed on @Entity/@MappedSuperclass must define attributeName" );
}
infoMap.put( info.getAttributeName(), info );
}
}
}
infoMap.put( info.getAttributeName(), info );
} );
}
@Override
public void startingProperty(XProperty property) {
public void startingProperty(MemberDetails property) {
if ( property == null ) {
return;
}
@ -145,39 +133,20 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
return;
}
{
// @Convert annotation on the Embeddable attribute
final Convert convertAnnotation = property.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
if ( isEmpty( info.getAttributeName() ) ) {
attributeConversionInfoMap.put( propertyName, info );
}
else {
attributeConversionInfoMap.put( propertyName + '.' + info.getAttributeName(), info );
}
property.forEachAnnotationUsage( Convert.class, (usage) -> {
final AttributeConversionInfo info = new AttributeConversionInfo( usage, property );
if ( isEmpty( info.getAttributeName() ) ) {
attributeConversionInfoMap.put( propertyName, info );
}
}
{
// @Converts annotation on the Embeddable attribute
final Converts convertsAnnotation = property.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
if ( isEmpty( info.getAttributeName() ) ) {
attributeConversionInfoMap.put( propertyName, info );
}
else {
attributeConversionInfoMap.put( propertyName + '.' + info.getAttributeName(), info );
}
}
else {
attributeConversionInfoMap.put( propertyName + '.' + info.getAttributeName(), info );
}
}
} );
}
@Override
protected AttributeConversionInfo locateAttributeConversionInfo(XProperty property) {
return locateAttributeConversionInfo( property.getName() );
protected AttributeConversionInfo locateAttributeConversionInfo(MemberDetails attributeMember) {
return locateAttributeConversionInfo( attributeMember.getName() );
}
@Override
@ -191,53 +160,53 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
}
@Override
public void addProperty(Property prop, AnnotatedColumns columns, XClass declaringClass) {
public void addProperty(Property prop, MemberDetails memberDetails, AnnotatedColumns columns, ClassDetails declaringClass) {
//AnnotatedColumn.checkPropertyConsistency( ); //already called earlier
if ( columns != null ) {
if ( columns.isSecondary() ) {
addPropertyToJoin( prop, declaringClass, columns.getJoin() );
addPropertyToJoin( prop, memberDetails, declaringClass, columns.getJoin() );
}
else {
addProperty( prop, declaringClass );
addProperty( prop, memberDetails, declaringClass );
}
}
else {
addProperty( prop, declaringClass );
addProperty( prop, memberDetails, declaringClass );
}
}
@Override
public void addProperty(Property prop, XClass declaringClass) {
public void addProperty(Property prop, MemberDetails memberDetails, ClassDetails declaringClass) {
if ( prop.getValue() instanceof Component ) {
//TODO handle quote and non quote table comparison
String tableName = prop.getValue().getTable().getName();
if ( getJoinsPerRealTableName().containsKey( tableName ) ) {
final Join join = getJoinsPerRealTableName().get( tableName );
addPropertyToJoin( prop, declaringClass, join );
addPropertyToJoin( prop, memberDetails, declaringClass, join );
}
else {
addPropertyToPersistentClass( prop, declaringClass );
addPropertyToPersistentClass( prop, memberDetails, declaringClass );
}
}
else {
addPropertyToPersistentClass( prop, declaringClass );
addPropertyToPersistentClass( prop, memberDetails, declaringClass );
}
}
@Override
public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
final Join join = entityBinder.addJoin( joinTableAnn, this, noDelayInPkColumnCreation );
public Join addJoin(AnnotationUsage<JoinTable> joinTableAnn, boolean noDelayInPkColumnCreation) {
final Join join = entityBinder.addJoinTable( joinTableAnn, this, noDelayInPkColumnCreation );
joins = entityBinder.getSecondaryTables();
return join;
}
@Override
public Join addJoin(JoinTable joinTable, Table table, boolean noDelayInPkColumnCreation) {
public Join addJoin(AnnotationUsage<JoinTable> joinTable, Table table, boolean noDelayInPkColumnCreation) {
final Join join = entityBinder.createJoin(
this,
noDelayInPkColumnCreation,
false,
joinTable.joinColumns(),
joinTable.getList( "joinColumns" ),
table.getQualifiedTableName(),
table
);
@ -251,7 +220,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
* as generic, to later be able to resolve its concrete type, and creating a new component
* with correctly typed sub-properties for the metamodel.
*/
public static void handleGenericComponentProperty(Property property, MetadataBuildingContext context) {
public static void handleGenericComponentProperty(Property property, MemberDetails memberDetails, MetadataBuildingContext context) {
final Value value = property.getValue();
if ( value instanceof Component ) {
final Component component = (Component) value;
@ -265,7 +234,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
for ( Property prop : component.getProperties() ) {
prepareActualProperty(
prop,
component.getComponentClass(),
memberDetails,
true,
context,
copy::addProperty
@ -276,8 +245,8 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
}
}
private void addPropertyToPersistentClass(Property property, XClass declaringClass) {
handleGenericComponentProperty( property, getContext() );
private void addPropertyToPersistentClass(Property property, MemberDetails memberDetails, ClassDetails declaringClass) {
handleGenericComponentProperty( property, memberDetails, getContext() );
if ( declaringClass != null ) {
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
if ( inheritanceState == null ) {
@ -287,7 +256,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
}
if ( inheritanceState.isEmbeddableSuperclass() ) {
persistentClass.addMappedSuperclassProperty( property );
addPropertyToMappedSuperclass( property, declaringClass, getContext() );
addPropertyToMappedSuperclass( property, memberDetails, declaringClass, getContext() );
}
else {
persistentClass.addProperty( property );
@ -298,143 +267,178 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
}
}
static void addPropertyToMappedSuperclass(Property prop, XClass declaringClass, MetadataBuildingContext context) {
final Class<?> type = context.getBootstrapContext().getReflectionManager().toClass( declaringClass );
final MappedSuperclass superclass = context.getMetadataCollector().getMappedSuperclass( type );
prepareActualProperty( prop, type, true, context, superclass::addDeclaredProperty );
private void addPropertyToMappedSuperclass(Property prop, MemberDetails memberDetails, ClassDetails declaringClass) {
final MappedSuperclass superclass = getContext().getMetadataCollector().getMappedSuperclass( declaringClass.toJavaClass() );
prepareActualProperty( prop, memberDetails, true, getContext(), superclass::addDeclaredProperty );
}
static void prepareActualProperty(
Property prop,
Class<?> type,
MemberDetails memberDetails,
boolean allowCollections,
MetadataBuildingContext context,
Consumer<Property> propertyConsumer) {
if ( type.getTypeParameters().length == 0 ) {
final ClassDetails declaringType = memberDetails.getDeclaringType();
if ( CollectionHelper.isEmpty( declaringType.getTypeParameters() ) ) {
propertyConsumer.accept( prop );
return;
}
else {
// If the type has type parameters, we have to look up the XClass and actual property again
// because the given XClass has a TypeEnvironment based on the type variable assignments of a subclass
// and that might result in a wrong property type being used for a property which uses a type variable
final XClass actualDeclaringClass = context.getBootstrapContext().getReflectionManager().toXClass( type );
for ( XProperty declaredProperty : getDeclaredProperties( actualDeclaringClass, prop.getPropertyAccessorName() ) ) {
if ( prop.getName().equals( declaredProperty.getName() ) ) {
final PropertyData inferredData = new PropertyInferredData(
actualDeclaringClass,
declaredProperty,
null,
context.getBootstrapContext().getReflectionManager()
);
if ( declaredProperty.isTypeResolved() ) {
// Avoid copying when the property doesn't depend on a type variable
propertyConsumer.accept( prop );
return;
}
// If the property depends on a type variable, we have to copy it and the Value
final Property actualProperty = prop.copy();
actualProperty.setGeneric( true );
actualProperty.setReturnedClassName( inferredData.getTypeName() );
final Value value = actualProperty.getValue().copy();
if ( value instanceof Collection ) {
if ( !allowCollections ) {
throw new AssertionFailure( "Collections are not allowed as identifier properties" );
}
final Collection collection = (Collection) value;
// The owner is a MappedSuperclass which is not a PersistentClass, so set it to null
// no idea what this code should be doing
final TypeDetails typeDetails = memberDetails.getType();
if ( typeDetails.getTypeKind() == TypeDetails.Kind.PARAMETERIZED_TYPE ) {
}
else if ( typeDetails.getTypeKind() == TypeDetails.Kind.TYPE_VARIABLE ) {
}
applyGenerics2( prop, memberDetails, typeDetails, allowCollections, propertyConsumer, context );
//applyGenerics( prop, typeDetails, allowCollections, propertyConsumer, context );
}
private static void applyGenerics2(
Property prop,
MemberDetails memberDetails,
TypeDetails typeDetails,
boolean allowCollections,
Consumer<Property> propertyConsumer,
MetadataBuildingContext context) {
if ( typeDetails.determineRawClass().getTypeParameters().isEmpty() ) {
propertyConsumer.accept( prop );
return;
}
final ClassDetails declaringClassDetails = memberDetails.getDeclaringType();
final List<MemberDetails> declaredAttributeMembers = getDeclaredAttributeMembers( declaringClassDetails, prop.getPropertyAccessorName() );
members_loop: for ( MemberDetails attributeMember : declaredAttributeMembers ) {
if ( !prop.getName().equals( attributeMember.resolveAttributeName() ) ) {
continue;
}
final PropertyData inferredData = new PropertyInferredData(
declaringClassDetails,
attributeMember,
null,
context
);
final Value originalValue = prop.getValue();
// If the property depends on a type variable, we have to copy it and the Value
final Property actualProperty = prop.copy();
actualProperty.setGeneric( true );
actualProperty.setReturnedClassName( inferredData.getTypeName() );
final Value value = actualProperty.getValue().copy();
if ( value instanceof Collection collection ) {
if ( !allowCollections ) {
throw new AssertionFailure( "Collections are not allowed as identifier properties" );
}
// The owner is a MappedSuperclass which is not a PersistentClass, so set it to null
// collection.setOwner( null );
collection.setRole( type.getName() + "." + prop.getName() );
// To copy the element and key values, we need to defer setting the type name until the CollectionBinder ran
final Value originalValue = prop.getValue();
context.getMetadataCollector().addSecondPass(
new SecondPass() {
@Override
public void doSecondPass(Map persistentClasses) throws MappingException {
final Collection initializedCollection = (Collection) originalValue;
final Value element = initializedCollection.getElement().copy();
setTypeName( element, inferredData.getProperty().getElementClass().getName() );
if ( initializedCollection instanceof IndexedCollection ) {
final Value index = ( (IndexedCollection) initializedCollection ).getIndex().copy();
if ( inferredData.getProperty().getMapKey() != null ) {
setTypeName( index, inferredData.getProperty().getMapKey().getName() );
}
( (IndexedCollection) collection ).setIndex( index );
}
collection.setElement( element );
collection.setRole( typeDetails.getName() + "." + prop.getName() );
// To copy the element and key values, we need to defer setting the type name until the CollectionBinder ran
final Value originalValue = prop.getValue();context.getMetadataCollector().addSecondPass(
new SecondPass() {
@Override
public void doSecondPass(Map persistentClasses) throws MappingException {
final Collection initializedCollection = (Collection) originalValue;
final Value element = initializedCollection.getElement().copy();
setTypeName( element, inferredData.getAttributeMember().getElementType().getName() );
if ( initializedCollection instanceof IndexedCollection ) {
final Value index = ( (IndexedCollection) initializedCollection ).getIndex().copy();
if ( inferredData.getAttributeMember().getMapKeyType() != null ) {
setTypeName( index, inferredData.getAttributeMember().getMapKeyType().getName() );
}
( (IndexedCollection) collection ).setIndex( index );
}
);
collection.setElement( element );
}
}
);
}
else {
setTypeName( value, inferredData.getTypeName() );
}
if ( value instanceof Component component ) {
final Class<?> componentClass = component.getComponentClass();
if ( component.isGeneric() ) {
actualProperty.setValue( context.getMetadataCollector().getGenericComponent( componentClass ) );
}
else {
if ( componentClass == Object.class ) {
// Object is not a valid component class, but that is what we get when using a type variable
component.clearProperties();
}
else {
setTypeName( value, inferredData.getTypeName() );
}
if ( value instanceof Component ) {
final Component component = ( (Component) value );
final Class<?> componentClass = component.getComponentClass();
if ( component.isGeneric() ) {
actualProperty.setValue( context.getMetadataCollector().getGenericComponent( componentClass ) );
}
else {
if ( componentClass == Object.class ) {
// Object is not a valid component class, but that is what we get when using a type variable
component.clearProperties();
final Iterator<Property> propertyIterator = component.getProperties().iterator();
while ( propertyIterator.hasNext() ) {
try {
propertyIterator.next().getGetter( componentClass );
}
else {
final Iterator<Property> propertyIterator = component.getPropertyIterator();
while ( propertyIterator.hasNext() ) {
try {
propertyIterator.next().getGetter( componentClass );
}
catch (PropertyNotFoundException e) {
propertyIterator.remove();
}
}
catch (PropertyNotFoundException e) {
propertyIterator.remove();
}
}
}
actualProperty.setValue( value );
propertyConsumer.accept( actualProperty );
break;
}
}
actualProperty.setValue( value );
propertyConsumer.accept( actualProperty );
// avoid the rest of the iteration
//noinspection UnnecessaryLabelOnBreakStatement
break members_loop;
}
}
private static List<XProperty> getDeclaredProperties(XClass declaringClass, String accessType) {
final List<XProperty> properties = new ArrayList<>();
XClass superclass = declaringClass;
private static List<MemberDetails> getDeclaredAttributeMembers(
ClassDetails declaringType,
String propertyAccessorName) {
final List<MemberDetails> members = new ArrayList<>();
ClassDetails superclass = declaringType;
while ( superclass != null ) {
properties.addAll( superclass.getDeclaredProperties( accessType ) );
superclass = superclass.getSuperclass();
applyAttributeMembers( superclass, propertyAccessorName, members );
superclass = superclass.getSuperClass();
}
return properties;
return members;
}
private static String getTypeName(Property property) {
final String typeName = getTypeName( property.getValue() );
return typeName != null ? typeName : property.getReturnedClassName();
}
private static String getTypeName(Value value) {
if ( value instanceof Component ) {
final Component component = (Component) value;
final String typeName = component.getTypeName();
if ( typeName != null ) {
return typeName;
public static final String ACCESS_PROPERTY = "property";
public static final String ACCESS_FIELD = "field";
public static final String ACCESS_RECORD = "record";
@SuppressWarnings("RedundantLabeledSwitchRuleCodeBlock")
private static void applyAttributeMembers(ClassDetails classDetails, String accessType, List<MemberDetails> members) {
switch ( accessType ) {
case ACCESS_FIELD -> {
for ( FieldDetails field : classDetails.getFields() ) {
if ( field.isPersistable() ) {
members.add( field );
}
}
}
case ACCESS_PROPERTY -> {
for ( MethodDetails methodDetails : classDetails.getMethods() ) {
if ( methodDetails.isPersistable() ) {
members.add( methodDetails );
}
}
}
case ACCESS_RECORD -> {
members.addAll( classDetails.getRecordComponents() );
}
return component.getComponentClassName();
}
return ( (SimpleValue) value ).getTypeName();
throw new IllegalArgumentException( "Unknown access type " + accessType );
}
private static void setTypeName(Value value, String typeName) {
if ( value instanceof ToOne ) {
final ToOne toOne = (ToOne) value;
if ( value instanceof ToOne toOne ) {
toOne.setReferencedEntityName( typeName );
toOne.setTypeName( typeName );
}
else if ( value instanceof Component ) {
final Component component = (Component) value;
else if ( value instanceof Component component ) {
// Avoid setting type name for generic components
if ( !component.isGeneric() ) {
component.setComponentClassName( typeName );
@ -448,7 +452,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
}
}
private void addPropertyToJoin(Property property, XClass declaringClass, Join join) {
private void addPropertyToJoin(Property property, MemberDetails memberDetails, ClassDetails declaringClass, Join join) {
if ( declaringClass != null ) {
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
if ( inheritanceState == null ) {
@ -458,7 +462,7 @@ public class ClassPropertyHolder extends AbstractPropertyHolder {
}
if ( inheritanceState.isEmbeddableSuperclass() ) {
join.addMappedSuperclassProperty( property );
addPropertyToMappedSuperclass( property, declaringClass, getContext() );
addPropertyToMappedSuperclass( property, memberDetails, declaringClass, getContext() );
}
else {
join.addProperty( property );

View File

@ -14,8 +14,6 @@ import org.hibernate.AssertionFailure;
import org.hibernate.annotations.CollectionType;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.CoreLogging;
@ -26,9 +24,12 @@ import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import jakarta.persistence.Convert;
import jakarta.persistence.Converts;
import jakarta.persistence.Enumerated;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
@ -60,8 +61,8 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
public CollectionPropertyHolder(
Collection collection,
String path,
XClass clazzToProcess,
XProperty property,
ClassDetails clazzToProcess,
MemberDetails property,
PropertyHolder parentPropertyHolder,
MetadataBuildingContext context) {
super( path, parentPropertyHolder, clazzToProcess, context );
@ -77,7 +78,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
private void buildAttributeConversionInfoMaps(
XProperty collectionProperty,
MemberDetails collectionProperty,
boolean isComposite,
Map<String,AttributeConversionInfo> elementAttributeConversionInfoMap,
Map<String,AttributeConversionInfo> keyAttributeConversionInfoMap) {
@ -86,38 +87,20 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
return;
}
{
final Convert convertAnnotation = collectionProperty.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
applyLocalConvert(
convertAnnotation,
collectionProperty,
isComposite,
elementAttributeConversionInfoMap,
keyAttributeConversionInfoMap
);
}
}
{
final Converts convertsAnnotation = collectionProperty.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
applyLocalConvert(
convertAnnotation,
collectionProperty,
isComposite,
elementAttributeConversionInfoMap,
keyAttributeConversionInfoMap
);
}
}
}
collectionProperty.forEachAnnotationUsage( Convert.class, (usage) -> {
applyLocalConvert(
usage,
collectionProperty,
isComposite,
elementAttributeConversionInfoMap,
keyAttributeConversionInfoMap
);
} );
}
private void applyLocalConvert(
Convert convertAnnotation,
XProperty collectionProperty,
AnnotationUsage<Convert> convertAnnotation,
MemberDetails collectionProperty,
boolean isComposite,
Map<String,AttributeConversionInfo> elementAttributeConversionInfoMap,
Map<String,AttributeConversionInfo> keyAttributeConversionInfoMap) {
@ -241,12 +224,12 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
@Override
public void startingProperty(XProperty property) {
public void startingProperty(MemberDetails property) {
// for now, nothing to do...
}
@Override
protected AttributeConversionInfo locateAttributeConversionInfo(XProperty property) {
protected AttributeConversionInfo locateAttributeConversionInfo(MemberDetails attributeMember) {
// nothing to do
return null;
}
@ -282,7 +265,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
@Override
public void addProperty(Property prop, XClass declaringClass) {
public void addProperty(Property prop, MemberDetails memberDetails, ClassDetails declaringClass) {
throw new AssertionFailure( "Cannot add property to a collection" );
}
@ -322,18 +305,18 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
@Override
public void addProperty(Property prop, AnnotatedColumns columns, XClass declaringClass) {
public void addProperty(Property prop, MemberDetails memberDetails, AnnotatedColumns columns, ClassDetails declaringClass) {
//Ejb3Column.checkPropertyConsistency( ); //already called earlier
throw new AssertionFailure( "addProperty to a join table of a collection: does it make sense?" );
}
@Override
public Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation) {
public Join addJoin(AnnotationUsage<JoinTable> joinTableAnn, boolean noDelayInPkColumnCreation) {
throw new AssertionFailure( "Add join in a second pass" );
}
@Override
public Join addJoin(JoinTable joinTableAnn, Table table, boolean noDelayInPkColumnCreation) {
public Join addJoin(AnnotationUsage<JoinTable> joinTableAnn, Table table, boolean noDelayInPkColumnCreation) {
throw new AssertionFailure( "Add join in a second pass" );
}
@ -344,7 +327,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
boolean prepared;
public void prepare(XProperty collectionProperty, boolean isComposite) {
public void prepare(MemberDetails collectionProperty, boolean isComposite) {
// fugly
if ( prepared ) {
return;
@ -357,16 +340,16 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
prepared = true;
if ( collection.isMap() ) {
if ( collectionProperty.isAnnotationPresent( MapKeyEnumerated.class ) ) {
if ( collectionProperty.hasAnnotationUsage( MapKeyEnumerated.class ) ) {
canKeyBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( MapKeyTemporal.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( MapKeyTemporal.class ) ) {
canKeyBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( MapKeyClass.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( MapKeyClass.class ) ) {
canKeyBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( MapKeyType.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( MapKeyType.class ) ) {
canKeyBeConverted = false;
}
}
@ -374,22 +357,22 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
canKeyBeConverted = false;
}
if ( collectionProperty.isAnnotationPresent( ManyToAny.class ) ) {
if ( collectionProperty.hasAnnotationUsage( ManyToAny.class ) ) {
canElementBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( OneToMany.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( OneToMany.class ) ) {
canElementBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( ManyToMany.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( ManyToMany.class ) ) {
canElementBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( Enumerated.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( Enumerated.class ) ) {
canElementBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( Temporal.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( Temporal.class ) ) {
canElementBeConverted = false;
}
else if ( collectionProperty.isAnnotationPresent( CollectionType.class ) ) {
else if ( collectionProperty.hasAnnotationUsage( CollectionType.class ) ) {
canElementBeConverted = false;
}
@ -405,7 +388,9 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
}
}
public ConverterDescriptor resolveElementAttributeConverterDescriptor(XProperty collectionXProperty, XClass elementXClass) {
public ConverterDescriptor resolveElementAttributeConverterDescriptor(
MemberDetails memberDetails,
ClassDetails classDetails) {
AttributeConversionInfo info = locateAttributeConversionInfo( "element" );
if ( info != null ) {
if ( info.isConversionDisabled() ) {
@ -431,10 +416,12 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
return getContext().getMetadataCollector()
.getConverterRegistry()
.getAttributeConverterAutoApplyHandler()
.findAutoApplyConverterForCollectionElement( collectionXProperty, getContext() );
.findAutoApplyConverterForCollectionElement( memberDetails, getContext() );
}
public ConverterDescriptor mapKeyAttributeConverterDescriptor(XProperty mapXProperty, XClass keyXClass) {
public ConverterDescriptor mapKeyAttributeConverterDescriptor(
MemberDetails memberDetails,
TypeDetails keyTypeDetails) {
AttributeConversionInfo info = locateAttributeConversionInfo( "key" );
if ( info != null ) {
if ( info.isConversionDisabled() ) {
@ -460,7 +447,7 @@ public class CollectionPropertyHolder extends AbstractPropertyHolder {
return getContext().getMetadataCollector()
.getConverterRegistry()
.getAttributeConverterAutoApplyHandler()
.findAutoApplyConverterForMapKey( mapXProperty, getContext() );
.findAutoApplyConverterForMapKey( memberDetails, getContext() );
}
}

View File

@ -18,9 +18,10 @@ import org.hibernate.annotations.FractionalSeconds;
import org.hibernate.annotations.JoinColumnOrFormula;
import org.hibernate.annotations.JoinColumnsOrFormulas;
import org.hibernate.annotations.JoinFormula;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
@ -33,6 +34,7 @@ import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import java.lang.annotation.Annotation;
import java.util.List;
import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnFromAnnotation;
import static org.hibernate.boot.model.internal.AnnotatedColumn.buildColumnFromNoAnnotation;
@ -54,7 +56,7 @@ class ColumnsBuilder {
private final PropertyHolder propertyHolder;
private final Nullability nullability;
private final XProperty property;
private final MemberDetails property;
private final PropertyData inferredData;
private final EntityBinder entityBinder;
private final MetadataBuildingContext buildingContext;
@ -64,7 +66,7 @@ class ColumnsBuilder {
public ColumnsBuilder(
PropertyHolder propertyHolder,
Nullability nullability,
XProperty property,
MemberDetails property,
PropertyData inferredData,
EntityBinder entityBinder,
MetadataBuildingContext buildingContext) {
@ -88,12 +90,13 @@ class ColumnsBuilder {
columns = null;
joinColumns = buildExplicitJoinColumns( property, inferredData );
// Comment comment = property.getAnnotation(Comment.class);
if ( property.isAnnotationPresent( Column.class ) ) {
final AnnotationUsage<Column> columnAnn = property.getAnnotationUsage( Column.class );
final AnnotationUsage<Formula> formulaAnn = property.getAnnotationUsage( Formula.class );
final AnnotationUsage<Columns> columnsAnn = property.getAnnotationUsage( Columns.class );
if ( columnAnn != null ) {
columns = buildColumnFromAnnotation(
property.getAnnotation( Column.class ),
property.getAnnotation( FractionalSeconds.class ),
property.getAnnotationUsage( Column.class ),
property.getAnnotationUsage( FractionalSeconds.class ),
// comment,
nullability,
propertyHolder,
@ -102,10 +105,9 @@ class ColumnsBuilder {
buildingContext
);
}
else if ( property.isAnnotationPresent( Formula.class ) ) {
else if ( formulaAnn != null ) {
columns = buildFormulaFromAnnotation(
getOverridableAnnotation( property, Formula.class, buildingContext ),
// comment,
nullability,
propertyHolder,
inferredData,
@ -113,11 +115,10 @@ class ColumnsBuilder {
buildingContext
);
}
else if ( property.isAnnotationPresent( Columns.class ) ) {
else if ( columnsAnn != null ) {
columns = buildColumnsFromAnnotations(
property.getAnnotation( Columns.class ).columns(),
columnsAnn.getList( "columns" ),
null,
// comment,
nullability,
propertyHolder,
inferredData,
@ -128,18 +129,18 @@ class ColumnsBuilder {
//set default values if needed
if ( joinColumns == null
&& ( property.isAnnotationPresent( ManyToOne.class )
|| property.isAnnotationPresent( OneToOne.class ) ) ) {
&& ( property.hasAnnotationUsage( ManyToOne.class )
|| property.hasAnnotationUsage( OneToOne.class ) ) ) {
joinColumns = buildDefaultJoinColumnsForToOne( property, inferredData );
}
else if ( joinColumns == null
&& ( property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( ElementCollection.class ) ) ) {
OneToMany oneToMany = property.getAnnotation( OneToMany.class );
&& ( property.hasAnnotationUsage( OneToMany.class )
|| property.hasAnnotationUsage( ElementCollection.class ) ) ) {
AnnotationUsage<OneToMany> oneToMany = property.getAnnotationUsage( OneToMany.class );
joinColumns = AnnotatedJoinColumns.buildJoinColumns(
null,
// comment,
oneToMany == null ? null : nullIfEmpty( oneToMany.mappedBy() ),
oneToMany == null ? null : nullIfEmpty( oneToMany.getString( "mappedBy" ) ),
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData,
@ -147,14 +148,14 @@ class ColumnsBuilder {
);
}
else if ( joinColumns == null
&& property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
&& property.hasAnnotationUsage( org.hibernate.annotations.Any.class ) ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData )
+ "' is annotated '@Any' and must declare at least one '@JoinColumn'" );
}
if ( columns == null && !property.isAnnotationPresent( ManyToMany.class ) ) {
if ( columns == null && !property.hasAnnotationUsage( ManyToMany.class ) ) {
//useful for collection of embedded elements
columns = buildColumnFromNoAnnotation(
property.getAnnotation( FractionalSeconds.class ),
property.getAnnotationUsage( FractionalSeconds.class ),
// comment,
nullability,
propertyHolder,
@ -173,12 +174,14 @@ class ColumnsBuilder {
return this;
}
private AnnotatedJoinColumns buildDefaultJoinColumnsForToOne(XProperty property, PropertyData inferredData) {
final JoinTable joinTableAnn = propertyHolder.getJoinTable( property );
private AnnotatedJoinColumns buildDefaultJoinColumnsForToOne(
MemberDetails property,
PropertyData inferredData) {
final AnnotationUsage<JoinTable> joinTableAnn = propertyHolder.getJoinTable( property );
// final Comment comment = property.getAnnotation(Comment.class);
if ( joinTableAnn != null ) {
return AnnotatedJoinColumns.buildJoinColumns(
joinTableAnn.inverseJoinColumns(),
joinTableAnn.getList( "inverseJoinColumns" ),
// comment,
null,
entityBinder.getSecondaryTables(),
@ -188,11 +191,11 @@ class ColumnsBuilder {
);
}
else {
final OneToOne oneToOneAnn = property.getAnnotation( OneToOne.class );
final AnnotationUsage<OneToOne> oneToOneAnn = property.getAnnotationUsage( OneToOne.class );
return AnnotatedJoinColumns.buildJoinColumns(
null,
// comment,
oneToOneAnn == null ? null : nullIfEmpty( oneToOneAnn.mappedBy() ),
oneToOneAnn == null ? null : nullIfEmpty( oneToOneAnn.getString( "mappedBy" ) ),
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData,
@ -201,13 +204,12 @@ class ColumnsBuilder {
}
}
private AnnotatedJoinColumns buildExplicitJoinColumns(XProperty property, PropertyData inferredData) {
private AnnotatedJoinColumns buildExplicitJoinColumns(MemberDetails property, PropertyData inferredData) {
// process @JoinColumns before @Columns to handle collection of entities properly
final JoinColumn[] joinColumnAnnotations = getJoinColumnAnnotations( property, inferredData );
final List<AnnotationUsage<JoinColumn>> joinColumnAnnotations = getJoinColumnAnnotations( property, inferredData );
if ( joinColumnAnnotations != null ) {
return AnnotatedJoinColumns.buildJoinColumns(
joinColumnAnnotations,
// property.getAnnotation( Comment.class ),
null,
entityBinder.getSecondaryTables(),
propertyHolder,
@ -216,7 +218,7 @@ class ColumnsBuilder {
);
}
final JoinColumnOrFormula[] joinColumnOrFormulaAnnotations = joinColumnOrFormulaAnnotations( property, inferredData );
final List<AnnotationUsage<JoinColumnOrFormula>> joinColumnOrFormulaAnnotations = joinColumnOrFormulaAnnotations( property, inferredData );
if ( joinColumnOrFormulaAnnotations != null ) {
return AnnotatedJoinColumns.buildJoinColumnsOrFormulas(
joinColumnOrFormulaAnnotations,
@ -228,8 +230,8 @@ class ColumnsBuilder {
);
}
if ( property.isAnnotationPresent( JoinFormula.class) ) {
final JoinFormula joinFormula = getOverridableAnnotation( property, JoinFormula.class, buildingContext );
if ( property.hasAnnotationUsage( JoinFormula.class) ) {
final AnnotationUsage<JoinFormula> joinFormula = getOverridableAnnotation( property, JoinFormula.class, buildingContext );
return AnnotatedJoinColumns.buildJoinColumnsWithFormula(
joinFormula,
entityBinder.getSecondaryTables(),
@ -242,58 +244,63 @@ class ColumnsBuilder {
return null;
}
private JoinColumnOrFormula[] joinColumnOrFormulaAnnotations(XProperty property, PropertyData inferredData) {
if ( property.isAnnotationPresent( JoinColumnOrFormula.class ) ) {
return new JoinColumnOrFormula[] { property.getAnnotation( JoinColumnOrFormula.class ) };
private List<AnnotationUsage<JoinColumnOrFormula>> joinColumnOrFormulaAnnotations(MemberDetails property, PropertyData inferredData) {
if ( property.hasAnnotationUsage( JoinColumnOrFormula.class ) ) {
return List.of( property.getAnnotationUsage( JoinColumnOrFormula.class ) );
}
else if ( property.isAnnotationPresent( JoinColumnsOrFormulas.class ) ) {
final JoinColumnsOrFormulas joinColumnsOrFormulas = property.getAnnotation( JoinColumnsOrFormulas.class );
final JoinColumnOrFormula[] joinColumnOrFormula = joinColumnsOrFormulas.value();
if ( joinColumnOrFormula.length == 0 ) {
if ( property.hasAnnotationUsage( JoinColumnsOrFormulas.class ) ) {
final AnnotationUsage<JoinColumnsOrFormulas> joinColumnsOrFormulas = property.getAnnotationUsage( JoinColumnsOrFormulas.class );
final List<AnnotationUsage<JoinColumnOrFormula>> joinColumnOrFormulaList = joinColumnsOrFormulas.getList( "value" );
if ( joinColumnOrFormulaList.isEmpty() ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData)
+ "' has an empty '@JoinColumnsOrFormulas' annotation" );
}
return joinColumnOrFormula;
}
else {
return null;
return joinColumnOrFormulaList;
}
return null;
}
private JoinColumn[] getJoinColumnAnnotations(XProperty property, PropertyData inferredData) {
if ( property.isAnnotationPresent( JoinColumn.class ) ) {
return new JoinColumn[] { property.getAnnotation( JoinColumn.class ) };
private List<AnnotationUsage<JoinColumn>> getJoinColumnAnnotations(MemberDetails property, PropertyData inferredData) {
if ( property.hasAnnotationUsage( JoinColumn.class ) ) {
return List.of( property.getAnnotationUsage( JoinColumn.class ) );
}
else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
final JoinColumns joinColumns = property.getAnnotation( JoinColumns.class );
final JoinColumn[] joinColumn = joinColumns.value();
if ( joinColumn.length == 0 ) {
if ( property.hasAnnotationUsage( JoinColumns.class ) ) {
final AnnotationUsage<JoinColumns> joinColumns = property.getAnnotationUsage( JoinColumns.class );
final List<AnnotationUsage<JoinColumn>> joinColumnsList = joinColumns.getList( "value" );
if ( joinColumnsList.isEmpty() ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData)
+ "' has an empty '@JoinColumns' annotation" );
}
return joinColumn;
return joinColumnsList;
}
else if ( property.isAnnotationPresent( MapsId.class ) ) {
if ( property.hasAnnotationUsage( MapsId.class ) ) {
// inelegant solution to HHH-16463, let the PrimaryKeyJoinColumn
// masquerade as a regular JoinColumn (when a @OneToOne maps to
// the primary key of the child table, it's more elegant and more
// spec-compliant to map the association with @PrimaryKeyJoinColumn)
if ( property.isAnnotationPresent( PrimaryKeyJoinColumn.class ) ) {
final PrimaryKeyJoinColumn column = property.getAnnotation( PrimaryKeyJoinColumn.class );
return new JoinColumn[] { new JoinColumnAdapter( column ) };
if ( property.hasAnnotationUsage( PrimaryKeyJoinColumn.class ) ) {
// final AnnotationUsage<PrimaryKeyJoinColumn> nested = property.getAnnotationUsage( PrimaryKeyJoinColumn.class );
// return new JoinColumn[] { new JoinColumnAdapter( column ) };
throw new UnsupportedOperationException( "Not yet implemented" );
}
else if ( property.isAnnotationPresent( PrimaryKeyJoinColumns.class ) ) {
final PrimaryKeyJoinColumns primaryKeyJoinColumns = property.getAnnotation( PrimaryKeyJoinColumns.class );
final JoinColumn[] joinColumns = new JoinColumn[primaryKeyJoinColumns.value().length];
final PrimaryKeyJoinColumn[] columns = primaryKeyJoinColumns.value();
if ( columns.length == 0 ) {
throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData)
+ "' has an empty '@PrimaryKeyJoinColumns' annotation" );
}
for ( int i = 0; i < columns.length; i++ ) {
joinColumns[i] = new JoinColumnAdapter( columns[i] );
}
return joinColumns;
else if ( property.hasAnnotationUsage( PrimaryKeyJoinColumns.class ) ) {
// final AnnotationUsage<PrimaryKeyJoinColumns> primaryKeyJoinColumns = property.getAnnotationUsage( PrimaryKeyJoinColumns.class );
// final List<PrimaryKeyJoinColumn> nested = primaryKeyJoinColumns.getList( "value" );
// final JoinColumn[] joinColumns = new JoinColumn[primaryKeyJoinColumns.value().length];
// final PrimaryKeyJoinColumn[] columns = primaryKeyJoinColumns.value();
// if ( columns.length == 0 ) {
// throw new AnnotationException( "Property '" + getPath( propertyHolder, inferredData)
// + "' has an empty '@PrimaryKeyJoinColumns' annotation" );
// }
// for ( int i = 0; i < columns.length; i++ ) {
// joinColumns[i] = new JoinColumnAdapter( columns[i] );
// }
// return joinColumns;
throw new UnsupportedOperationException( "Not yet implemented" );
}
else {
return null;
@ -311,9 +318,9 @@ class ColumnsBuilder {
final PropertyData override =
getPropertyOverriddenByMapperOrMapsId( isId, propertyHolder, property.getName(), buildingContext );
if ( override != null ) {
final AnnotatedJoinColumns joinColumns = buildExplicitJoinColumns( override.getProperty(), override );
final AnnotatedJoinColumns joinColumns = buildExplicitJoinColumns( override.getAttributeMember(), override );
return joinColumns == null
? buildDefaultJoinColumnsForToOne( override.getProperty(), override )
? buildDefaultJoinColumnsForToOne( override.getAttributeMember(), override )
: joinColumns;
}
else {
@ -374,11 +381,6 @@ class ColumnsBuilder {
return column.options();
}
@Override
public CheckConstraint[] check() {
return new CheckConstraint[0];
}
@Override
public String comment() {
return "";
@ -389,6 +391,11 @@ class ColumnsBuilder {
return column.foreignKey();
}
@Override
public CheckConstraint[] check() {
return new CheckConstraint[0];
}
@Override
public Class<? extends Annotation> annotationType() {
return JoinColumn.class;

View File

@ -6,12 +6,12 @@
*/
package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.AggregateColumn;
@ -21,13 +21,15 @@ import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Converts;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import static org.hibernate.boot.model.internal.ClassPropertyHolder.addPropertyToMappedSuperclass;
@ -82,26 +84,44 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
PropertyHolder parent,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
super( path, parent, inferredData.getPropertyClass(), context );
final XProperty embeddedXProperty = inferredData.getProperty();
setCurrentProperty( embeddedXProperty );
super( path, parent, inferredData.getPropertyType().determineRawClass(), context );
final MemberDetails embeddedMemberDetails = inferredData.getAttributeMember();
setCurrentProperty( embeddedMemberDetails );
this.component = component;
this.isOrWithinEmbeddedId = parent.isOrWithinEmbeddedId()
|| hasAnnotation( embeddedXProperty, Id.class, EmbeddedId.class );
this.isWithinElementCollection = parent.isWithinElementCollection() ||
parent instanceof CollectionPropertyHolder;
|| hasAnnotation( embeddedMemberDetails, Id.class, EmbeddedId.class );
this.isWithinElementCollection = parent.isWithinElementCollection()
|| parent instanceof CollectionPropertyHolder;
this.inheritanceStatePerClass = inheritanceStatePerClass;
if ( embeddedXProperty != null ) {
this.embeddedAttributeName = embeddedXProperty.getName();
this.attributeConversionInfoMap = processAttributeConversions( embeddedXProperty );
if ( embeddedMemberDetails != null ) {
this.embeddedAttributeName = embeddedMemberDetails.getName();
this.attributeConversionInfoMap = processAttributeConversions( embeddedMemberDetails );
}
else {
this.embeddedAttributeName = "";
this.attributeConversionInfoMap = processAttributeConversions( inferredData.getClassOrPluralElement() );
this.attributeConversionInfoMap = processAttributeConversions( inferredData.getClassOrElementType() );
}
}
private boolean hasAnnotation(
MemberDetails memberDetails,
Class<? extends Annotation> annotationType) {
if ( memberDetails == null ) {
return false;
}
return memberDetails.hasAnnotationUsage( annotationType );
}
private boolean hasAnnotation(
MemberDetails memberDetails,
Class<? extends Annotation> annotationType1,
Class<? extends Annotation> annotationType2) {
return hasAnnotation( memberDetails, annotationType1 )
|| hasAnnotation( memberDetails, annotationType2 );
}
/**
* This is called from our constructor and handles (in order):<ol>
* <li>@Convert annotation at the Embeddable class level</li>
@ -115,78 +135,45 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
* {@literal @Convert/@Converts} annotations at the Embeddable attribute level are handled in the calls to
* {@link #startingProperty}. Duplicates are simply ignored there.
*
* @param embeddedXProperty The property that is the composite being described by this ComponentPropertyHolder
* @param embeddedMemberDetails The property that is the composite being described by this ComponentPropertyHolder
*/
private Map<String,AttributeConversionInfo> processAttributeConversions(XProperty embeddedXProperty) {
private Map<String,AttributeConversionInfo> processAttributeConversions(MemberDetails embeddedMemberDetails) {
final Map<String,AttributeConversionInfo> infoMap = new HashMap<>();
final XClass embeddableXClass = embeddedXProperty.getType();
final TypeDetails embeddableTypeDetails = embeddedMemberDetails.getType();
// as a baseline, we want to apply conversions from the Embeddable and then overlay conversions
// from the Embedded
// first apply conversions from the Embeddable...
processAttributeConversions( embeddableXClass, infoMap );
processAttributeConversions( embeddableTypeDetails, infoMap );
// then we can overlay any conversions from the Embedded attribute
{
// @Convert annotation on the Embedded attribute
final Convert convertAnnotation = embeddedXProperty.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "Convert placed on Embedded attribute must define (sub)attributeName" );
}
infoMap.put( info.getAttributeName(), info );
embeddedMemberDetails.forEachAnnotationUsage( Convert.class, (usage) -> {
final AttributeConversionInfo info = new AttributeConversionInfo( usage, embeddedMemberDetails );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "Convert placed on Embedded attribute must define (sub)attributeName" );
}
}
{
// @Converts annotation on the Embedded attribute
final Converts convertsAnnotation = embeddedXProperty.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "Convert placed on Embedded attribute must define (sub)attributeName" );
}
infoMap.put( info.getAttributeName(), info );
}
}
}
infoMap.put( info.getAttributeName(), info );
} );
return infoMap;
}
private void processAttributeConversions(XClass embeddableXClass, Map<String, AttributeConversionInfo> infoMap) {
{
// @Convert annotation on the Embeddable class level
final Convert convertAnnotation = embeddableXClass.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Convert placed on @Embeddable must define attributeName" );
}
infoMap.put( info.getAttributeName(), info );
private void processAttributeConversions(TypeDetails embeddableTypeDetails, Map<String, AttributeConversionInfo> infoMap) {
final ClassDetails embeddableClassDetails = embeddableTypeDetails.determineRawClass();
embeddableClassDetails.forEachAnnotationUsage( Convert.class, (usage) -> {
final AttributeConversionInfo info = new AttributeConversionInfo( usage, embeddableClassDetails );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Convert placed on @Embeddable must define attributeName" );
}
}
{
// @Converts annotation on the Embeddable class level
final Converts convertsAnnotation = embeddableXClass.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, embeddableXClass );
if ( isEmpty( info.getAttributeName() ) ) {
throw new IllegalStateException( "@Converts placed on @Embeddable must define attributeName" );
}
infoMap.put( info.getAttributeName(), info );
}
}
}
infoMap.put( info.getAttributeName(), info );
} );
}
private Map<String,AttributeConversionInfo> processAttributeConversions(XClass embeddableXClass) {
private Map<String,AttributeConversionInfo> processAttributeConversions(TypeDetails embeddableTypeDetails) {
final Map<String,AttributeConversionInfo> infoMap = new HashMap<>();
processAttributeConversions( embeddableXClass, infoMap );
processAttributeConversions( embeddableTypeDetails, infoMap );
return infoMap;
}
@ -201,8 +188,8 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
}
@Override
public void startingProperty(XProperty property) {
if ( property == null ) {
public void startingProperty(MemberDetails propertyMemberDetails) {
if ( propertyMemberDetails == null ) {
return;
}
@ -212,35 +199,21 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
// technically we should only do this for properties of "basic type"
final String path = embeddedAttributeName + '.' + property.getName();
final String path = embeddedAttributeName + '.' + propertyMemberDetails.getName();
if ( attributeConversionInfoMap.containsKey( path ) ) {
return;
}
{
// @Convert annotation on the Embeddable attribute
final Convert convertAnnotation = property.getAnnotation( Convert.class );
if ( convertAnnotation != null ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
attributeConversionInfoMap.put( property.getName(), info );
}
}
{
// @Converts annotation on the Embeddable attribute
final Converts convertsAnnotation = property.getAnnotation( Converts.class );
if ( convertsAnnotation != null ) {
for ( Convert convertAnnotation : convertsAnnotation.value() ) {
final AttributeConversionInfo info = new AttributeConversionInfo( convertAnnotation, property );
attributeConversionInfoMap.put( property.getName(), info );
}
}
}
propertyMemberDetails.forEachAnnotationUsage( Convert.class, (usage) -> {
final AttributeConversionInfo info = new AttributeConversionInfo( usage, propertyMemberDetails );
attributeConversionInfoMap.put( propertyMemberDetails.getName(), info );
} );
}
@Override
protected AttributeConversionInfo locateAttributeConversionInfo(XProperty property) {
protected AttributeConversionInfo locateAttributeConversionInfo(MemberDetails attributeMember) {
// conversions on parent would have precedence
return locateAttributeConversionInfo( property.getName() );
return locateAttributeConversionInfo( attributeMember.getName() );
}
@Override
@ -265,7 +238,7 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
}
@Override
public void addProperty(Property property, AnnotatedColumns columns, XClass declaringClass) {
public void addProperty(Property property, MemberDetails attributeMemberDetails, AnnotatedColumns columns, ClassDetails declaringClass) {
//Ejb3Column.checkPropertyConsistency( ); //already called earlier
// Check table matches between the component and the columns
// if not, change the component table if no properties are set
@ -285,16 +258,16 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
}
}
}
addProperty( property, declaringClass );
addProperty( property, attributeMemberDetails, declaringClass );
}
@Override
public Join addJoin(JoinTable joinTable, boolean noDelayInPkColumnCreation) {
public Join addJoin(AnnotationUsage<JoinTable> joinTable, boolean noDelayInPkColumnCreation) {
return parent.addJoin( joinTable, noDelayInPkColumnCreation );
}
@Override
public Join addJoin(JoinTable joinTable, Table table, boolean noDelayInPkColumnCreation) {
public Join addJoin(AnnotationUsage<JoinTable> joinTable, Table table, boolean noDelayInPkColumnCreation) {
return parent.addJoin( joinTable, table, noDelayInPkColumnCreation );
}
@ -319,7 +292,7 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
}
@Override
public void addProperty(Property prop, XClass declaringClass) {
public void addProperty(Property prop, MemberDetails attributeMemberDetails, ClassDetails declaringClass) {
handleGenericComponentProperty( prop, getContext() );
if ( declaringClass != null ) {
final InheritanceState inheritanceState = inheritanceStatePerClass.get( declaringClass );
@ -366,17 +339,17 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
}
@Override
public Column[] getOverriddenColumn(String propertyName) {
public List<AnnotationUsage<Column>> getOverriddenColumn(String propertyName) {
//FIXME this is yukky
Column[] result = super.getOverriddenColumn( propertyName );
List<AnnotationUsage<Column>> result = super.getOverriddenColumn( propertyName );
if ( result == null ) {
String userPropertyName = extractUserPropertyName( "id", propertyName );
final String userPropertyName = extractUserPropertyName( "id", propertyName );
if ( userPropertyName != null ) {
result = super.getOverriddenColumn( userPropertyName );
}
}
if ( result == null ) {
String userPropertyName = extractUserPropertyName( IDENTIFIER_MAPPER_PROPERTY, propertyName );
final String userPropertyName = extractUserPropertyName( IDENTIFIER_MAPPER_PROPERTY, propertyName );
if ( userPropertyName != null ) {
result = super.getOverriddenColumn( userPropertyName );
}
@ -397,11 +370,6 @@ public class ComponentPropertyHolder extends AbstractPropertyHolder {
return null;
}
@Override
public JoinColumn[] getOverriddenJoinColumn(String propertyName) {
return super.getOverriddenJoinColumn( propertyName );
}
@Override
public String toString() {
return getClass().getSimpleName() + "(" + parent.normalizeCompositePathForLogging( embeddedAttributeName ) + ")";

View File

@ -19,16 +19,11 @@ import org.hibernate.AnnotationException;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.Instantiator;
import org.hibernate.annotations.TypeBinderType;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XMethod;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.binder.TypeBinder;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.mapping.AggregateColumn;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Property;
@ -36,6 +31,12 @@ import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.SingleTableSubclass;
import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
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 org.hibernate.models.spi.TypeDetails;
import org.hibernate.property.access.internal.PropertyAccessStrategyCompositeUserTypeImpl;
import org.hibernate.property.access.internal.PropertyAccessStrategyMixedImpl;
import org.hibernate.property.access.spi.PropertyAccessStrategy;
@ -70,7 +71,6 @@ import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNa
import static org.hibernate.boot.model.internal.GeneratorBinder.buildGenerators;
import static org.hibernate.boot.model.internal.GeneratorBinder.generatorType;
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
import static org.hibernate.boot.model.internal.HCANNHelper.findContainingAnnotations;
import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClass;
import static org.hibernate.boot.model.internal.PropertyBinder.processElementAnnotations;
import static org.hibernate.boot.model.internal.PropertyHolderBuilder.buildPropertyHolder;
@ -94,10 +94,10 @@ public class EmbeddableBinder {
boolean isIdentifierMapper,
boolean isComponentEmbedded,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
XProperty property,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MemberDetails property,
AnnotatedColumns columns,
XClass returnedClass,
ClassDetails returnedClass,
PropertyBinder propertyBinder,
boolean isOverridden,
Class<? extends CompositeUserType<?>> compositeUserType) {
@ -159,10 +159,19 @@ public class EmbeddableBinder {
);
}
static boolean isEmbedded(XProperty property, XClass returnedClass) {
return property.isAnnotationPresent( Embedded.class )
|| property.isAnnotationPresent( EmbeddedId.class )
|| returnedClass.isAnnotationPresent( Embeddable.class ) && !property.isAnnotationPresent( Convert.class );
static boolean isEmbedded(MemberDetails property, ClassDetails returnedClass) {
return property.hasAnnotationUsage( Embedded.class )
|| property.hasAnnotationUsage( EmbeddedId.class )
|| returnedClass.hasAnnotationUsage( Embeddable.class ) && !property.hasAnnotationUsage( Convert.class );
}
static boolean isEmbedded(MemberDetails property, TypeDetails returnedClass) {
if ( property.hasAnnotationUsage( Embedded.class ) || property.hasAnnotationUsage( EmbeddedId.class ) ) {
return true;
}
final ClassDetails returnClassDetails = returnedClass.determineRawClass();
return returnClassDetails.hasAnnotationUsage( Embeddable.class ) && !property.hasAnnotationUsage( Convert.class );
}
public static Component bindEmbeddable(
@ -174,7 +183,7 @@ public class EmbeddableBinder {
MetadataBuildingContext context,
boolean isComponentEmbedded,
boolean isId, //is an identifier
Map<XClass, InheritanceState> inheritanceStatePerClass,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
String referencedEntityName, //is a component who is overridden by a @MapsId
String propertyName,
Class<? extends EmbeddableInstantiator> customInstantiatorImpl,
@ -220,19 +229,22 @@ public class EmbeddableBinder {
component.setKey( true );
checkEmbeddedId( inferredData, propertyHolder, referencedEntityName, component );
}
callTypeBinders( component, context, inferredData.getPropertyClass() );
callTypeBinders( component, context, inferredData.getPropertyType() );
return component;
}
private static void callTypeBinders(Component component, MetadataBuildingContext context, XClass annotatedClass ) {
for ( Annotation containingAnnotation : findContainingAnnotations( annotatedClass, TypeBinderType.class) ) {
final TypeBinderType binderType = containingAnnotation.annotationType().getAnnotation( TypeBinderType.class );
private static void callTypeBinders(Component component, MetadataBuildingContext context, TypeDetails annotatedClass ) {
final List<AnnotationUsage<?>> metaAnnotatedAnnotations = annotatedClass.determineRawClass().getMetaAnnotated( TypeBinderType.class );
for ( AnnotationUsage<?> metaAnnotated : metaAnnotatedAnnotations ) {
final AnnotationUsage<TypeBinderType> binderType = metaAnnotated.getAnnotationDescriptor().getAnnotationUsage( TypeBinderType.class );
try {
final TypeBinder binder = binderType.binder().newInstance();
binder.bind( containingAnnotation, context, component );
final ClassDetails binderImpl = binderType.getClassDetails( "binder" );
final Class<? extends TypeBinder<Annotation>> binderJavaType = binderImpl.toJavaClass();
final TypeBinder<Annotation> binder = binderJavaType.getDeclaredConstructor().newInstance();
binder.bind( metaAnnotated.toAnnotation(), context, component );
}
catch ( Exception e ) {
throw new AnnotationException( "error processing @TypeBinderType annotation '" + containingAnnotation + "'", e );
throw new AnnotationException( "error processing @TypeBinderType annotation '" + metaAnnotated + "'", e );
}
}
}
@ -244,13 +256,13 @@ public class EmbeddableBinder {
MetadataBuildingContext context,
boolean isComponentEmbedded,
boolean isId,
Map<XClass, InheritanceState> inheritanceStatePerClass,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
Component component) {
final PropertyBinder binder = new PropertyBinder();
binder.setDeclaringClass( inferredData.getDeclaringClass() );
binder.setName( inferredData.getPropertyName() );
binder.setValue(component);
binder.setProperty( inferredData.getProperty() );
binder.setMemberDetails( inferredData.getAttributeMember() );
binder.setAccessType( inferredData.getDefaultAccess() );
binder.setEmbedded(isComponentEmbedded);
binder.setHolder(propertyHolder);
@ -298,7 +310,7 @@ public class EmbeddableBinder {
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
AnnotatedColumns columns,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
Map<ClassDetails, InheritanceState> inheritanceStatePerClass) {
return fillEmbeddable(
propertyHolder,
inferredData,
@ -332,7 +344,7 @@ public class EmbeddableBinder {
Class<? extends CompositeUserType<?>> compositeUserTypeClass,
AnnotatedColumns columns,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
boolean isIdClass) {
// inSecondPass can only be used to apply right away the second pass of a composite-element
// Because it's a value type, there is no bidirectional association, hence second pass
@ -359,19 +371,18 @@ public class EmbeddableBinder {
// propertyHolder here is the owner of the component property.
// Tell it we are about to start the component...
propertyHolder.startingProperty( inferredData.getProperty() );
propertyHolder.startingProperty( inferredData.getAttributeMember() );
final CompositeUserType<?> compositeUserType;
final XClass returnedClassOrElement;
final ClassDetails returnedClassOrElement;
if ( compositeUserTypeClass == null ) {
compositeUserType = null;
returnedClassOrElement = inferredData.getClassOrPluralElement();
returnedClassOrElement = inferredData.getClassOrElementType().determineRawClass();
}
else {
compositeUserType = compositeUserType( compositeUserTypeClass, context );
component.setTypeName( compositeUserTypeClass.getName() );
returnedClassOrElement = context.getBootstrapContext().getReflectionManager()
.toXClass( compositeUserType.embeddable() );
returnedClassOrElement = context.getMetadataCollector().getSourceModelBuildingContext().getClassDetailsRegistry().resolveClassDetails( compositeUserType.embeddable().getName() );
}
AggregateComponentBinder.processAggregate(
component,
@ -397,15 +408,13 @@ public class EmbeddableBinder {
);
}
final XClass annotatedClass = inferredData.getPropertyClass();
final Map<String, String> subclassToSuperclass = component.isPolymorphic() ? new HashMap<>() : null;
final TypeDetails annotatedType = inferredData.getPropertyType();
final List<PropertyData> classElements = collectClassElements(
propertyAccessor,
context,
returnedClassOrElement,
annotatedClass,
isIdClass,
subclassToSuperclass
annotatedType,
isIdClass
);
if ( component.isPolymorphic() ) {
@ -429,10 +438,10 @@ public class EmbeddableBinder {
}
final List<PropertyData> baseClassElements =
collectBaseClassElements( baseInferredData, propertyAccessor, context, annotatedClass );
collectBaseClassElements( baseInferredData, propertyAccessor, context, annotatedType );
if ( baseClassElements != null
//useful to avoid breaking pre JPA 2 mappings
&& !hasAnnotationsOnIdClass( annotatedClass ) ) {
&& !hasAnnotationsOnIdClass( annotatedType ) ) {
processIdClassElements( propertyHolder, baseInferredData, classElements, baseClassElements );
}
for ( PropertyData propertyAnnotatedElement : classElements ) {
@ -452,8 +461,8 @@ public class EmbeddableBinder {
inheritanceStatePerClass
);
final XProperty property = propertyAnnotatedElement.getProperty();
if ( property.isAnnotationPresent( GeneratedValue.class ) ) {
final MemberDetails property = propertyAnnotatedElement.getAttributeMember();
if ( property.hasAnnotationUsage( GeneratedValue.class ) ) {
if ( isIdClass || subholder.isOrWithinEmbeddedId() ) {
processGeneratedId( context, component, property );
}
@ -489,7 +498,7 @@ public class EmbeddableBinder {
private static void bindDiscriminator(
Component component,
XClass componentClass,
ClassDetails componentClass,
PropertyHolder parentHolder,
PropertyHolder holder,
PropertyData propertyData,
@ -512,14 +521,14 @@ public class EmbeddableBinder {
}
private static AnnotatedDiscriminatorColumn processEmbeddableDiscriminatorProperties(
XClass annotatedClass,
ClassDetails annotatedClass,
PropertyData propertyData,
PropertyHolder parentHolder,
PropertyHolder holder,
InheritanceState inheritanceState,
MetadataBuildingContext context) {
final DiscriminatorColumn discriminatorColumn = annotatedClass.getAnnotation( DiscriminatorColumn.class );
final DiscriminatorFormula discriminatorFormula = getOverridableAnnotation(
final AnnotationUsage<DiscriminatorColumn> discriminatorColumn = annotatedClass.getAnnotationUsage( DiscriminatorColumn.class );
final AnnotationUsage<DiscriminatorFormula> discriminatorFormula = getOverridableAnnotation(
annotatedClass,
DiscriminatorFormula.class,
context
@ -528,7 +537,7 @@ public class EmbeddableBinder {
if ( inheritanceState.hasSiblings() ) {
final String path = qualify( holder.getPath(), EntityDiscriminatorMapping.DISCRIMINATOR_ROLE_NAME );
final String columnPrefix;
final Column[] overrides;
final List<AnnotationUsage<Column>> overrides;
if ( holder.isWithinElementCollection() ) {
columnPrefix = unqualify( parentHolder.getPath() );
overrides = parentHolder.getOverriddenColumn( path );
@ -540,7 +549,7 @@ public class EmbeddableBinder {
return buildDiscriminatorColumn(
discriminatorColumn,
discriminatorFormula,
overrides == null ? null : overrides[0],
overrides == null ? null : overrides.get(0),
columnPrefix + "_" + DEFAULT_DISCRIMINATOR_COLUMN_NAME,
context
);
@ -608,8 +617,8 @@ public class EmbeddableBinder {
private static List<PropertyData> collectClassElements(
AccessType propertyAccessor,
MetadataBuildingContext context,
XClass returnedClassOrElement,
XClass annotatedClass,
ClassDetails returnedClassOrElement,
TypeDetails annotatedClass,
boolean isIdClass,
Map<String, String> subclassToSuperclass) {
final List<PropertyData> classElements = new ArrayList<>();
@ -618,12 +627,12 @@ public class EmbeddableBinder {
new PropertyContainer( returnedClassOrElement, annotatedClass, propertyAccessor );
addElementsOfClass( classElements, container, context);
//add elements of the embeddable's mapped superclasses
XClass subclass = returnedClassOrElement;
ClassDetails subClass = returnedClassOrElement;
XClass superClass;
while ( isValidSuperclass( superClass = subclass.getSuperclass(), isIdClass ) ) {
//FIXME: proper support of type variables incl var resolved at upper levels
final PropertyContainer superContainer =
new PropertyContainer( superClass, annotatedClass, propertyAccessor );
new PropertyContainer( superClass, annotatedClass.determineRawClass(), propertyAccessor );
addElementsOfClass( classElements, superContainer, context );
if ( subclassToSuperclass != null ) {
subclassToSuperclass.put( subclass.getName(), superClass.getName() );
@ -636,12 +645,12 @@ public class EmbeddableBinder {
private static void collectSubclassElements(
AccessType propertyAccessor,
MetadataBuildingContext context,
XClass superclass,
ClassDetails superclass,
List<PropertyData> classElements,
BasicType<?> discriminatorType,
Map<Object, String> discriminatorValues,
Map<String, String> subclassToSuperclass) {
for ( final XClass subclass : context.getMetadataCollector().getEmbeddableSubclasses( superclass ) ) {
for ( final ClassDetails subclass : context.getMetadataCollector().getEmbeddableSubclasses( superclass ) ) {
// collect the discriminator value details
final String old = collectDiscriminatorValue( subclass, discriminatorType, discriminatorValues );
if ( old != null ) {
@ -670,11 +679,11 @@ public class EmbeddableBinder {
}
private static String collectDiscriminatorValue(
XClass annotatedClass,
ClassDetails annotatedClass,
BasicType<?> discriminatorType,
Map<Object, String> discriminatorValues) {
final String explicitValue = annotatedClass.isAnnotationPresent( DiscriminatorValue.class )
? annotatedClass.getAnnotation( DiscriminatorValue.class ).value()
final String explicitValue = annotatedClass.hasAnnotationUsage( DiscriminatorValue.class )
? annotatedClass.getAnnotationUsage( DiscriminatorValue.class ).getString( "value" )
: null;
final String discriminatorValue;
if ( isEmpty( explicitValue ) ) {
@ -702,12 +711,12 @@ public class EmbeddableBinder {
}
private static boolean isValidSuperclass(XClass superClass, boolean isIdClass) {
private static boolean isValidSuperclass(ClassDetails superClass, boolean isIdClass) {
if ( superClass == null ) {
return false;
}
return superClass.isAnnotationPresent( MappedSuperclass.class )
return superClass.hasAnnotationUsage( MappedSuperclass.class )
|| ( isIdClass
&& !superClass.getName().equals( Object.class.getName() )
&& !superClass.getName().equals( "java.lang.Record" ) );
@ -717,17 +726,20 @@ public class EmbeddableBinder {
PropertyData baseInferredData,
AccessType propertyAccessor,
MetadataBuildingContext context,
XClass annotatedClass) {
TypeDetails annotatedClass) {
if ( baseInferredData != null ) {
final List<PropertyData> baseClassElements = new ArrayList<>();
// iterate from base returned class up hierarchy to handle cases where the @Id attributes
// might be spread across the subclasses and super classes.
XClass baseReturnedClassOrElement = baseInferredData.getClassOrElement();
TypeDetails baseReturnedClassOrElement = baseInferredData.getClassOrElementType();
while ( !Object.class.getName().equals( baseReturnedClassOrElement.getName() ) ) {
final PropertyContainer container =
new PropertyContainer( baseReturnedClassOrElement, annotatedClass, propertyAccessor );
final PropertyContainer container = new PropertyContainer(
baseReturnedClassOrElement,
annotatedClass,
propertyAccessor
);
addElementsOfClass( baseClassElements, container, context );
baseReturnedClassOrElement = baseReturnedClassOrElement.getSuperclass();
baseReturnedClassOrElement = baseReturnedClassOrElement.determineRawClass().getGenericSuperType();
}
return baseClassElements;
}
@ -758,13 +770,16 @@ public class EmbeddableBinder {
}
}
private static boolean hasAnnotationsOnIdClass(XClass idClass) {
for ( XProperty property : idClass.getDeclaredProperties( XClass.ACCESS_FIELD ) ) {
if ( hasTriggeringAnnotation( property ) ) {
private static boolean hasAnnotationsOnIdClass(TypeDetails idClassType) {
return hasAnnotationsOnIdClass( idClassType.determineRawClass() );
}
private static boolean hasAnnotationsOnIdClass(ClassDetails idClass) {
for ( FieldDetails field : idClass.getFields() ) {
if ( hasTriggeringAnnotation( field ) ) {
return true;
}
}
for ( XMethod method : idClass.getDeclaredMethods() ) {
for ( MethodDetails method : idClass.getMethods() ) {
if ( hasTriggeringAnnotation( method ) ) {
return true;
}
@ -772,22 +787,22 @@ public class EmbeddableBinder {
return false;
}
private static boolean hasTriggeringAnnotation(XAnnotatedElement property) {
return property.isAnnotationPresent(Column.class)
|| property.isAnnotationPresent(OneToMany.class)
|| property.isAnnotationPresent(ManyToOne.class)
|| property.isAnnotationPresent(Id.class)
|| property.isAnnotationPresent(GeneratedValue.class)
|| property.isAnnotationPresent(OneToOne.class)
|| property.isAnnotationPresent(ManyToMany.class);
private static boolean hasTriggeringAnnotation(MemberDetails property) {
return property.hasAnnotationUsage(Column.class)
|| property.hasAnnotationUsage(OneToMany.class)
|| property.hasAnnotationUsage(ManyToOne.class)
|| property.hasAnnotationUsage(Id.class)
|| property.hasAnnotationUsage(GeneratedValue.class)
|| property.hasAnnotationUsage(OneToOne.class)
|| property.hasAnnotationUsage(ManyToMany.class);
}
private static void processGeneratedId(MetadataBuildingContext context, Component component, XProperty property) {
final GeneratedValue generatedValue = property.getAnnotation( GeneratedValue.class );
private static void processGeneratedId(MetadataBuildingContext context, Component component, MemberDetails property) {
final AnnotationUsage<GeneratedValue> generatedValue = property.getAnnotationUsage( GeneratedValue.class );
final String generatorType = generatedValue != null
? generatorType( generatedValue, property.getType(), context )
? generatorType( generatedValue, property.getType().determineRawClass(), context )
: DEFAULT_ID_GEN_STRATEGY;
final String generator = generatedValue != null ? generatedValue.generator() : "";
final String generator = generatedValue != null ? generatedValue.getString( "generator" ) : "";
if ( isGlobalGeneratorNameGlobal( context ) ) {
buildGenerators( property, context );
@ -835,12 +850,12 @@ public class EmbeddableBinder {
throw new AnnotationException(
"Property '" + getPath(propertyHolder, idClassPropertyData )
+ "' belongs to an '@IdClass' but has no matching property in entity class '"
+ baseInferredData.getPropertyClass().getName()
+ baseInferredData.getPropertyType().getName()
+ "' (every property of the '@IdClass' must have a corresponding persistent property in the '@Entity' class)"
);
}
if ( hasToOneAnnotation( entityPropertyData.getProperty() )
&& !entityPropertyData.getClassOrElement().equals( idClassPropertyData.getClassOrElement() ) ) {
if ( hasToOneAnnotation( entityPropertyData.getAttributeMember() )
&& !entityPropertyData.getClassOrElementType().equals( idClassPropertyData.getClassOrElementType() ) ) {
//don't replace here as we need to use the actual original return type
//the annotation overriding will be dealt with by a mechanism similar to @MapsId
continue;
@ -861,19 +876,15 @@ public class EmbeddableBinder {
component.setEmbedded( isComponentEmbedded );
//yuk
component.setTable( propertyHolder.getTable() );
final XClass embeddableClass;
//FIXME shouldn't identifier mapper use getClassOrElementName? Need to be checked.
if ( isIdentifierMapper
|| isComponentEmbedded && inferredData.getPropertyName() == null ) {
component.setComponentClassName( component.getOwner().getClassName() );
embeddableClass = inferredData.getClassOrElement();
}
else {
embeddableClass = inferredData.getClassOrPluralElement();
component.setComponentClassName( embeddableClass.getName() );
component.setComponentClassName( inferredData.getClassOrElementType().getName() );
}
component.setCustomInstantiator( customInstantiatorImpl );
final Constructor<?> constructor = resolveInstantiator( embeddableClass, context );
final Constructor<?> constructor = resolveInstantiator( inferredData.getClassOrElementType(), context );
if ( constructor != null ) {
component.setInstantiator( constructor, constructor.getAnnotation( Instantiator.class ).value() );
}
@ -884,11 +895,13 @@ public class EmbeddableBinder {
return component;
}
private static Constructor<?> resolveInstantiator(XClass embeddableClass, MetadataBuildingContext buildingContext) {
private static Constructor<?> resolveInstantiator(TypeDetails embeddableClass, MetadataBuildingContext buildingContext) {
return embeddableClass == null ? null : resolveInstantiator( embeddableClass.determineRawClass(), buildingContext );
}
private static Constructor<?> resolveInstantiator(ClassDetails embeddableClass, MetadataBuildingContext buildingContext) {
if ( embeddableClass != null ) {
final Constructor<?>[] declaredConstructors = buildingContext.getBootstrapContext().getReflectionManager()
.toClass( embeddableClass )
.getDeclaredConstructors();
final Constructor<?>[] declaredConstructors = embeddableClass.toJavaClass().getDeclaredConstructors();
Constructor<?> constructor = null;
for ( Constructor<?> declaredConstructor : declaredConstructors ) {
if ( declaredConstructor.isAnnotationPresent( Instantiator.class ) ) {
@ -905,29 +918,28 @@ public class EmbeddableBinder {
}
public static Class<? extends EmbeddableInstantiator> determineCustomInstantiator(
XProperty property,
XClass returnedClass,
MemberDetails property,
ClassDetails returnedClass,
MetadataBuildingContext context) {
if ( property.isAnnotationPresent( EmbeddedId.class ) ) {
if ( property.hasAnnotationUsage( EmbeddedId.class ) ) {
// we don't allow custom instantiators for composite ids
return null;
}
final org.hibernate.annotations.EmbeddableInstantiator propertyAnnotation =
property.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
final AnnotationUsage<org.hibernate.annotations.EmbeddableInstantiator> propertyAnnotation =
property.getAnnotationUsage( org.hibernate.annotations.EmbeddableInstantiator.class );
if ( propertyAnnotation != null ) {
return propertyAnnotation.value();
return propertyAnnotation.getClassDetails( "value" ).toJavaClass();
}
final org.hibernate.annotations.EmbeddableInstantiator classAnnotation =
returnedClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class );
final AnnotationUsage<org.hibernate.annotations.EmbeddableInstantiator> classAnnotation =
returnedClass.getAnnotationUsage( org.hibernate.annotations.EmbeddableInstantiator.class );
if ( classAnnotation != null ) {
return classAnnotation.value();
return classAnnotation.getClassDetails( "value" ).toJavaClass();
}
final Class<?> embeddableClass = context.getBootstrapContext().getReflectionManager().toClass( returnedClass );
if ( embeddableClass != null ) {
return context.getMetadataCollector().findRegisteredEmbeddableInstantiator( embeddableClass );
if ( returnedClass.getClassName() != null ) {
return context.getMetadataCollector().findRegisteredEmbeddableInstantiator( returnedClass.toJavaClass() );
}
return null;

View File

@ -12,6 +12,8 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.mapping.FetchProfile;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import java.util.Map;
@ -20,12 +22,12 @@ import java.util.Map;
*/
public class FetchOverrideSecondPass implements SecondPass {
private final String fetchProfileName;
private final FetchOverride fetch;
private final AnnotationUsage<FetchOverride> fetch;
private final MetadataBuildingContext buildingContext;
public FetchOverrideSecondPass(
String fetchProfileName,
FetchOverride fetch,
AnnotationUsage<FetchOverride> fetch,
MetadataBuildingContext buildingContext) {
this.fetchProfileName = fetchProfileName;
this.fetch = fetch;
@ -34,20 +36,21 @@ public class FetchOverrideSecondPass implements SecondPass {
@Override
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
final ClassDetails entityClassDetails = fetch.getClassDetails( "entity" );
final String attributeName = fetch.getString( "association" );
// throws MappingException in case the property does not exist
buildingContext.getMetadataCollector()
.getEntityBinding( fetch.entity().getName() )
.getProperty( fetch.association() );
.getEntityBinding( entityClassDetails.getName() )
.getProperty( attributeName );
final FetchProfile profile = buildingContext.getMetadataCollector().getFetchProfile( fetchProfileName );
// we already know that the FetchProfile exists and is good to use
profile.addFetch(
new FetchProfile.Fetch(
fetch.entity().getName(),
fetch.association(),
fetch.mode(),
fetch.fetch()
)
);
profile.addFetch( new FetchProfile.Fetch(
entityClassDetails.getName(),
attributeName,
fetch.getEnum( "mode" ),
fetch.getEnum( "fetch" )
) );
}
}

View File

@ -15,6 +15,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.mapping.FetchProfile;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.models.spi.AnnotationUsage;
import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.mapping.MetadataSource.ANNOTATIONS;
@ -23,13 +24,13 @@ import static org.hibernate.mapping.MetadataSource.ANNOTATIONS;
* @author Gavin King
*/
public class FetchSecondPass implements SecondPass {
private final FetchProfileOverride fetch;
private final AnnotationUsage<FetchProfileOverride> fetch;
private final PropertyHolder propertyHolder;
private final String propertyName;
private final MetadataBuildingContext buildingContext;
public FetchSecondPass(
FetchProfileOverride fetch,
AnnotationUsage<FetchProfileOverride> fetch,
PropertyHolder propertyHolder,
String propertyName,
MetadataBuildingContext buildingContext) {
@ -41,21 +42,20 @@ public class FetchSecondPass implements SecondPass {
@Override
public void doSecondPass(Map<String, PersistentClass> persistentClasses) throws MappingException {
final FetchProfile profile = buildingContext.getMetadataCollector().getFetchProfile( fetch.profile() );
final String profileName = fetch.getString( "profile" );
final FetchProfile profile = buildingContext.getMetadataCollector().getFetchProfile( profileName );
if ( profile == null ) {
throw new AnnotationException( "Property '" + qualify( propertyHolder.getPath(), propertyName )
+ "' refers to an unknown fetch profile named '" + fetch.profile() + "'" );
+ "' refers to an unknown fetch profile named '" + profileName + "'" );
}
if ( profile.getSource() == ANNOTATIONS ) {
profile.addFetch(
new FetchProfile.Fetch(
propertyHolder.getEntityName(),
propertyName,
fetch.mode(),
fetch.fetch()
)
);
profile.addFetch( new FetchProfile.Fetch(
propertyHolder.getEntityName(),
propertyName,
fetch.getEnum( "mode" ),
fetch.getEnum( "fetch" )
) );
}
// otherwise, it's a fetch profile defined in XML, and it overrides
// the annotations, so we simply ignore this annotation completely

View File

@ -6,13 +6,17 @@
*/
package org.hibernate.boot.model.internal;
import jakarta.persistence.AttributeConverter;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.MappingException;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.FilterDefs;
import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
@ -20,6 +24,9 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
@ -34,10 +41,7 @@ import org.hibernate.type.internal.ConvertedBasicTypeImpl;
import org.hibernate.type.spi.TypeConfiguration;
import org.hibernate.usertype.UserType;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import jakarta.persistence.AttributeConverter;
import java.util.function.Supplier;
import static java.util.Collections.emptyMap;
@ -53,48 +57,53 @@ class FilterDefBinder {
private static final CoreMessageLogger LOG = messageLogger( FilterDefBinder.class );
public static void bindFilterDef(FilterDef filterDef, MetadataBuildingContext context) {
final String name = filterDef.name();
public static void bindFilterDef(AnnotationUsage<FilterDef> filterDef, MetadataBuildingContext context) {
final String name = filterDef.getString( "name" );
if ( context.getMetadataCollector().getFilterDefinition( name ) != null ) {
throw new AnnotationException( "Multiple '@FilterDef' annotations define a filter named '" + name + "'" );
}
final Map<String, JdbcMapping> explicitParamJaMappings;
final Map<String, JdbcMapping> paramJdbcMappings;
final Map<String, ManagedBean<? extends Supplier<?>>> parameterResolvers;
if ( filterDef.parameters().length == 0 ) {
explicitParamJaMappings = emptyMap();
final List<AnnotationUsage<ParamDef>> explicitParameters = filterDef.getList( "parameters" );
if ( explicitParameters.isEmpty() ) {
paramJdbcMappings = emptyMap();
parameterResolvers = emptyMap();
}
else {
explicitParamJaMappings = new HashMap<>();
paramJdbcMappings = new HashMap<>();
parameterResolvers = new HashMap<>();
for ( ParamDef paramDef : filterDef.parameters() ) {
final JdbcMapping jdbcMapping = resolveFilterParamType( paramDef.type(), context );
for ( AnnotationUsage<ParamDef> explicitParameter : explicitParameters ) {
final String parameterName = explicitParameter.getString( "name" );
final ClassDetails typeClassDetails = explicitParameter.getClassDetails( "type" );
final JdbcMapping jdbcMapping = resolveFilterParamType( typeClassDetails.toJavaClass(), context );
if ( jdbcMapping == null ) {
throw new MappingException(
String.format(
Locale.ROOT,
"Unable to resolve type specified for parameter (%s) defined for @FilterDef (%s)",
paramDef.name(),
parameterName,
name
)
);
}
explicitParamJaMappings.put( paramDef.name(), jdbcMapping );
paramJdbcMappings.put( parameterName, jdbcMapping );
if ( paramDef.resolver() != Supplier.class ) {
parameterResolvers.put( paramDef.name(), resolveParamResolver( paramDef, context ) );
}
}
}
final FilterDefinition filterDefinition = new FilterDefinition(
name,
filterDef.defaultCondition(),
explicitParamJaMappings,
filterDef.getString( "defaultCondition" ),
paramJdbcMappings,
parameterResolvers,
filterDef.autoEnabled(),
filterDef.getBoolean( "autoEnabled" ),
filterDef.applyToLoadByKey()
);
LOG.debugf( "Binding filter definition: %s", filterDefinition.getFilterName() );
context.getMetadataCollector().addFilterDefinition( filterDefinition );
}
@ -245,14 +254,15 @@ class FilterDefBinder {
}
}
public static void bindFilterDefs(XAnnotatedElement annotatedElement, MetadataBuildingContext context) {
final FilterDef filterDef = annotatedElement.getAnnotation( FilterDef.class );
final FilterDefs filterDefs = getOverridableAnnotation( annotatedElement, FilterDefs.class, context );
public static void bindFilterDefs(AnnotationTarget annotatedElement, MetadataBuildingContext context) {
final AnnotationUsage<FilterDef> filterDef = annotatedElement.getAnnotationUsage( FilterDef.class );
final AnnotationUsage<FilterDefs> filterDefs = getOverridableAnnotation( annotatedElement, FilterDefs.class, context );
if ( filterDef != null ) {
bindFilterDef( filterDef, context );
}
if ( filterDefs != null ) {
for ( FilterDef def : filterDefs.value() ) {
final List<AnnotationUsage<FilterDef>> nestedDefs = filterDefs.getList( "value" );
for ( AnnotationUsage<FilterDef> def : nestedDefs ) {
bindFilterDef( def, context );
}
}

View File

@ -18,11 +18,7 @@ import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.IdGeneratorType;
import org.hibernate.annotations.Parameter;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.internal.GenerationStrategyInterpreter;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.relational.ExportableProducer;
@ -42,20 +38,24 @@ import org.hibernate.internal.CoreLogging;
import org.hibernate.mapping.GeneratorCreator;
import org.hibernate.mapping.IdentifierGeneratorCreator;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.mapping.Value;
import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.spi.BeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.jboss.logging.Logger;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.SequenceGenerators;
import jakarta.persistence.TableGenerator;
import jakarta.persistence.TableGenerators;
import jakarta.persistence.Version;
import static org.hibernate.boot.model.internal.AnnotationHelper.extractParameterMap;
import static org.hibernate.boot.model.internal.BinderHelper.isCompositeId;
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
import static org.hibernate.id.factory.internal.IdentifierGeneratorUtil.collectParameters;
@ -71,12 +71,12 @@ public class GeneratorBinder {
*/
public static void makeIdGenerator(
SimpleValue id,
XProperty property,
MemberDetails idAttributeMember,
String generatorType,
String generatorName,
MetadataBuildingContext buildingContext,
Map<String, IdentifierGeneratorDefinition> localGenerators) {
LOG.debugf( "#makeIdGenerator(%s, %s, %s, %s, ...)", id, property, generatorType, generatorName );
LOG.debugf( "#makeIdGenerator(%s, %s, %s, %s, ...)", id, idAttributeMember, generatorType, generatorName );
//generator settings
id.setIdentifierGeneratorStrategy( generatorType );
@ -97,7 +97,7 @@ public class GeneratorBinder {
//we have a named generator
final IdentifierGeneratorDefinition definition = makeIdentifierGeneratorDefinition(
generatorName,
property,
idAttributeMember,
localGenerators,
buildingContext
);
@ -131,7 +131,7 @@ public class GeneratorBinder {
*/
public static void makeIdGenerator(
SimpleValue id,
XProperty idXProperty,
MemberDetails idAttributeMember,
String generatorType,
String generatorName,
MetadataBuildingContext buildingContext,
@ -141,12 +141,12 @@ public class GeneratorBinder {
localIdentifiers = new HashMap<>();
localIdentifiers.put( foreignKGeneratorDefinition.getName(), foreignKGeneratorDefinition );
}
makeIdGenerator( id, idXProperty, generatorType, generatorName, buildingContext, localIdentifiers );
makeIdGenerator( id, idAttributeMember, generatorType, generatorName, buildingContext, localIdentifiers );
}
private static IdentifierGeneratorDefinition makeIdentifierGeneratorDefinition(
String name,
XProperty idProperty,
MemberDetails idAttributeMember,
Map<String, IdentifierGeneratorDefinition> localGenerators,
MetadataBuildingContext buildingContext) {
if ( localGenerators != null ) {
@ -164,7 +164,7 @@ public class GeneratorBinder {
LOG.debugf( "Could not resolve explicit IdentifierGeneratorDefinition - using implicit interpretation (%s)", name );
final GeneratedValue generatedValue = idProperty.getAnnotation( GeneratedValue.class );
final AnnotationUsage<GeneratedValue> generatedValue = idAttributeMember.getAnnotationUsage( GeneratedValue.class );
if ( generatedValue == null ) {
// this should really never happen, but it's easy to protect against it...
return new IdentifierGeneratorDefinition( DEFAULT_ID_GEN_STRATEGY, DEFAULT_ID_GEN_STRATEGY );
@ -172,80 +172,54 @@ public class GeneratorBinder {
return IdentifierGeneratorDefinition.createImplicit(
name,
buildingContext
.getBootstrapContext()
.getReflectionManager()
.toClass( idProperty.getType() ),
generatedValue.generator(),
idAttributeMember.getType().determineRawClass().toJavaClass(),
generatedValue.getString( "generator" ),
interpretGenerationType( generatedValue )
);
}
private static GenerationType interpretGenerationType(GeneratedValue generatedValueAnn) {
return generatedValueAnn.strategy() == null ? GenerationType.AUTO : generatedValueAnn.strategy();
private static GenerationType interpretGenerationType(AnnotationUsage<GeneratedValue> generatedValueAnn) {
// todo (jpa32) : when can this ever be null?
final GenerationType strategy = generatedValueAnn.getEnum( "strategy" );
return strategy == null ? GenerationType.AUTO : strategy;
}
public static Map<String, IdentifierGeneratorDefinition> buildGenerators(
XAnnotatedElement annotatedElement,
AnnotationTarget annotatedElement,
MetadataBuildingContext context) {
final InFlightMetadataCollector metadataCollector = context.getMetadataCollector();
final Map<String, IdentifierGeneratorDefinition> generators = new HashMap<>();
final TableGenerators tableGenerators = annotatedElement.getAnnotation( TableGenerators.class );
if ( tableGenerators != null ) {
for ( TableGenerator tableGenerator : tableGenerators.value() ) {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( tableGenerator, context );
generators.put(
idGenerator.getName(),
idGenerator
);
metadataCollector.addIdentifierGenerator( idGenerator );
}
}
annotatedElement.forEachAnnotationUsage( TableGenerator.class, (usage) -> {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( usage, context );
generators.put(
idGenerator.getName(),
idGenerator
);
metadataCollector.addIdentifierGenerator( idGenerator );
} );
final SequenceGenerators sequenceGenerators = annotatedElement.getAnnotation( SequenceGenerators.class );
if ( sequenceGenerators != null ) {
for ( SequenceGenerator sequenceGenerator : sequenceGenerators.value() ) {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( sequenceGenerator, context );
generators.put(
idGenerator.getName(),
idGenerator
);
metadataCollector.addIdentifierGenerator( idGenerator );
}
}
final TableGenerator tableGenerator = annotatedElement.getAnnotation( TableGenerator.class );
if ( tableGenerator != null ) {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( tableGenerator, context );
annotatedElement.forEachAnnotationUsage( SequenceGenerator.class, (usage) -> {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( usage, context );
generators.put( idGenerator.getName(), idGenerator );
metadataCollector.addIdentifierGenerator( idGenerator );
} );
}
final SequenceGenerator sequenceGenerator = annotatedElement.getAnnotation( SequenceGenerator.class );
if ( sequenceGenerator != null ) {
IdentifierGeneratorDefinition idGenerator = buildIdGenerator( sequenceGenerator, context );
annotatedElement.forEachAnnotationUsage( GenericGenerator.class, (usage) -> {
final IdentifierGeneratorDefinition idGenerator = buildIdGenerator( usage, context );
generators.put( idGenerator.getName(), idGenerator );
metadataCollector.addIdentifierGenerator( idGenerator );
}
final GenericGenerator genericGenerator = annotatedElement.getAnnotation( GenericGenerator.class );
if ( genericGenerator != null ) {
final IdentifierGeneratorDefinition idGenerator = buildIdGenerator( genericGenerator, context );
generators.put( idGenerator.getName(), idGenerator );
metadataCollector.addIdentifierGenerator( idGenerator );
}
} );
return generators;
}
static String generatorType(
MetadataBuildingContext context,
XClass entityXClass,
ClassDetails entityXClass,
boolean isComponent,
GeneratedValue generatedValue) {
AnnotationUsage<GeneratedValue> generatedValue) {
if ( isComponent ) {
//a component must not have any generator
return DEFAULT_ID_GEN_STRATEGY;
@ -255,62 +229,67 @@ public class GeneratorBinder {
}
}
static String generatorType(GeneratedValue generatedValue, final XClass javaClass, MetadataBuildingContext context) {
static String generatorType(
AnnotationUsage<GeneratedValue> generatedValue,
final ClassDetails javaClass,
MetadataBuildingContext context) {
return GenerationStrategyInterpreter.STRATEGY_INTERPRETER.determineGeneratorName(
generatedValue.strategy(),
generatedValue.getEnum( "strategy" ),
new GenerationStrategyInterpreter.GeneratorNameDeterminationContext() {
Class<?> javaType = null;
@Override
public Class<?> getIdType() {
if ( javaType == null ) {
javaType = context.getBootstrapContext().getReflectionManager().toClass( javaClass );
javaType = javaClass.toJavaClass();
}
return javaType;
}
@Override
public String getGeneratedValueGeneratorName() {
return generatedValue.generator();
return generatedValue.getString( "generator" );
}
}
);
}
static IdentifierGeneratorDefinition buildIdGenerator(
Annotation generatorAnnotation,
AnnotationUsage<?> generatorAnnotation,
MetadataBuildingContext context) {
if ( generatorAnnotation == null ) {
return null;
}
final IdentifierGeneratorDefinition.Builder definitionBuilder = new IdentifierGeneratorDefinition.Builder();
if ( generatorAnnotation instanceof TableGenerator ) {
if ( TableGenerator.class.isAssignableFrom( generatorAnnotation.getAnnotationType() ) ) {
//noinspection unchecked
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretTableGenerator(
(TableGenerator) generatorAnnotation,
(AnnotationUsage<TableGenerator>) generatorAnnotation,
definitionBuilder
);
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add table generator with name: {0}", definitionBuilder.getName() );
}
}
else if ( generatorAnnotation instanceof SequenceGenerator ) {
else if ( SequenceGenerator.class.isAssignableFrom( generatorAnnotation.getAnnotationType() ) ) {
//noinspection unchecked
GenerationStrategyInterpreter.STRATEGY_INTERPRETER.interpretSequenceGenerator(
(SequenceGenerator) generatorAnnotation,
(AnnotationUsage<SequenceGenerator>) generatorAnnotation,
definitionBuilder
);
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add sequence generator with name: {0}", definitionBuilder.getName() );
}
}
else if ( generatorAnnotation instanceof GenericGenerator ) {
final GenericGenerator genericGenerator = (GenericGenerator) generatorAnnotation;
definitionBuilder.setName( genericGenerator.name() );
final String strategy = genericGenerator.type().equals(Generator.class)
? genericGenerator.strategy()
: genericGenerator.type().getName();
else if ( GenericGenerator.class.isAssignableFrom( generatorAnnotation.getAnnotationType() ) ) {
//noinspection unchecked
final AnnotationUsage<GenericGenerator> genericGenerator = (AnnotationUsage<GenericGenerator>) generatorAnnotation;
definitionBuilder.setName( genericGenerator.getString( "name" ) );
final Class<? extends Generator> generatorClass = genericGenerator.getClassDetails( "type" ).toJavaClass();
final String strategy = generatorClass.equals(Generator.class)
? genericGenerator.getString( "strategy" )
: generatorClass.getName();
definitionBuilder.setStrategy( strategy );
for ( Parameter parameter : genericGenerator.parameters() ) {
definitionBuilder.addParam( parameter.name(), parameter.value() );
}
definitionBuilder.addParams( extractParameterMap( genericGenerator.getList( "parameters" ) ) );
if ( LOG.isTraceEnabled() ) {
LOG.tracev( "Add generic generator with name: {0}", definitionBuilder.getName() );
}
@ -349,9 +328,11 @@ public class GeneratorBinder {
* Instantiates the given generator annotation type, initializing it with the given instance of the corresponding
* generator annotation and the property's type.
*/
static GeneratorCreator generatorCreator(XProperty property, Annotation annotation) {
final Member member = HCANNHelper.getUnderlyingMember( property );
final Class<? extends Annotation> annotationType = annotation.annotationType();
static GeneratorCreator generatorCreator(
MemberDetails memberDetails,
AnnotationUsage<?> annotation,
MetadataBuildingContext buildingContext) {
final Class<? extends Annotation> annotationType = annotation.getAnnotationType();
final ValueGenerationType generatorAnnotation = annotationType.getAnnotation( ValueGenerationType.class );
if ( generatorAnnotation == null ) {
return null;
@ -360,42 +341,39 @@ public class GeneratorBinder {
checkGeneratorClass( generatorClass );
checkGeneratorInterfaces( generatorClass );
return creationContext -> {
final Generator generator =
instantiateGenerator(
annotation,
member,
annotationType,
creationContext,
GeneratorCreationContext.class,
generatorClass
);
callInitialize( annotation, member, creationContext, generator );
checkVersionGenerationAlways( property, generator );
final Generator generator = instantiateGenerator(
annotation,
memberDetails,
annotationType,
GeneratorCreationContext.class,
generatorClass,
creationContext
);
callInitialize( annotation, memberDetails, creationContext, generator );
checkVersionGenerationAlways( memberDetails, generator );
return generator;
};
}
static IdentifierGeneratorCreator identifierGeneratorCreator(
XProperty idProperty,
Annotation annotation,
MemberDetails idAttributeMember,
AnnotationUsage<? extends Annotation> annotation,
BeanContainer beanContainer) {
final Member member = HCANNHelper.getUnderlyingMember( idProperty );
final Class<? extends Annotation> annotationType = annotation.annotationType();
final Class<? extends Annotation> annotationType = annotation.getAnnotationType();
final IdGeneratorType idGeneratorType = annotationType.getAnnotation( IdGeneratorType.class );
assert idGeneratorType != null;
return creationContext -> {
final Class<? extends Generator> generatorClass = idGeneratorType.value();
checkGeneratorClass( generatorClass );
final Generator generator =
instantiateGenerator(
annotation,
beanContainer,
creationContext,
generatorClass,
member,
annotationType
);
callInitialize( annotation, member, creationContext, generator );
final Generator generator = instantiateGenerator(
annotation,
beanContainer,
creationContext,
generatorClass,
idAttributeMember,
annotationType
);
callInitialize( annotation, idAttributeMember, creationContext, generator );
callConfigure( creationContext, generator );
checkIdGeneratorTiming( annotationType, generator );
return generator;
@ -403,11 +381,11 @@ public class GeneratorBinder {
}
private static Generator instantiateGenerator(
Annotation annotation,
AnnotationUsage<? extends Annotation> annotation,
BeanContainer beanContainer,
CustomIdGeneratorCreationContext creationContext,
Class<? extends Generator> generatorClass,
Member member,
MemberDetails memberDetails,
Class<? extends Annotation> annotationType) {
if ( beanContainer != null ) {
return instantiateGeneratorAsBean(
@ -415,28 +393,28 @@ public class GeneratorBinder {
beanContainer,
creationContext,
generatorClass,
member,
memberDetails,
annotationType
);
}
else {
return instantiateGenerator(
annotation,
member,
memberDetails,
annotationType,
creationContext,
CustomIdGeneratorCreationContext.class,
generatorClass
generatorClass,
creationContext
);
}
}
private static Generator instantiateGeneratorAsBean(
Annotation annotation,
AnnotationUsage<? extends Annotation> annotation,
BeanContainer beanContainer,
CustomIdGeneratorCreationContext creationContext,
Class<? extends Generator> generatorClass,
Member member,
MemberDetails memberDetails,
Class<? extends Annotation> annotationType) {
return beanContainer.getBean( generatorClass,
new BeanContainer.LifecycleOptions() {
@ -455,11 +433,11 @@ public class GeneratorBinder {
public <B> B produceBeanInstance(Class<B> beanType) {
return (B) instantiateGenerator(
annotation,
member,
memberDetails,
annotationType,
creationContext,
CustomIdGeneratorCreationContext.class,
generatorClass
generatorClass,
creationContext
);
}
@Override
@ -471,30 +449,30 @@ public class GeneratorBinder {
}
private static <C, G extends Generator> G instantiateGenerator(
Annotation annotation,
Member member,
AnnotationUsage<?> annotation,
MemberDetails memberDetails,
Class<? extends Annotation> annotationType,
C creationContext,
Class<C> contextClass,
Class<? extends G> generatorClass) {
Class<? extends G> generatorClass,
C creationContext) {
try {
try {
return generatorClass
.getConstructor( annotationType, Member.class, contextClass )
.newInstance( annotation, member, creationContext);
.newInstance( annotation.toAnnotation(), memberDetails.toJavaMember(), creationContext);
}
catch (NoSuchMethodException ignore) {
try {
return generatorClass
.getConstructor( annotationType )
.newInstance( annotation );
.newInstance( annotation.toAnnotation() );
}
catch (NoSuchMethodException i) {
return generatorClass.newInstance();
}
}
}
catch (InvocationTargetException | InstantiationException | IllegalAccessException e) {
catch (InvocationTargetException | InstantiationException | IllegalAccessException | IllegalArgumentException e ) {
throw new HibernateException(
"Could not instantiate generator of type '" + generatorClass.getName() + "'",
e
@ -503,8 +481,8 @@ public class GeneratorBinder {
}
private static <A extends Annotation> void callInitialize(
A annotation,
Member member,
AnnotationUsage<A> annotation,
MemberDetails memberDetails,
GeneratorCreationContext creationContext,
Generator generator) {
if ( generator instanceof AnnotationBasedGenerator ) {
@ -513,12 +491,12 @@ public class GeneratorBinder {
// check this explicitly; If required, this could be done e.g. using ClassMate
@SuppressWarnings("unchecked")
final AnnotationBasedGenerator<A> generation = (AnnotationBasedGenerator<A>) generator;
generation.initialize( annotation, member, creationContext );
generation.initialize( annotation.toAnnotation(), memberDetails.toJavaMember(), creationContext );
}
}
private static void checkVersionGenerationAlways(XProperty property, Generator generator) {
if ( property.isAnnotationPresent(Version.class) ) {
private static void checkVersionGenerationAlways(MemberDetails property, Generator generator) {
if ( property.hasAnnotationUsage(Version.class) ) {
if ( !generator.generatesOnInsert() ) {
throw new AnnotationException("Property '" + property.getName()
+ "' is annotated '@Version' but has a 'Generator' which does not generate on inserts"
@ -560,18 +538,18 @@ public class GeneratorBinder {
SimpleValue idValue,
Map<String, IdentifierGeneratorDefinition> classGenerators,
MetadataBuildingContext context,
XClass entityClass,
XProperty idProperty) {
ClassDetails entityClass,
MemberDetails idAttributeMember) {
//manage composite related metadata
//guess if its a component and find id data access (property, field etc)
final GeneratedValue generatedValue = idProperty.getAnnotation( GeneratedValue.class );
final String generatorType = generatorType( context, entityClass, isCompositeId( entityClass, idProperty ), generatedValue );
final String generatorName = generatedValue == null ? "" : generatedValue.generator();
final AnnotationUsage<GeneratedValue> generatedValue = idAttributeMember.getAnnotationUsage( GeneratedValue.class );
final String generatorType = generatorType( context, entityClass, isCompositeId( entityClass, idAttributeMember ), generatedValue );
final String generatorName = generatedValue == null ? "" : generatedValue.getString( "generator" );
if ( isGlobalGeneratorNameGlobal( context ) ) {
buildGenerators( idProperty, context );
buildGenerators( idAttributeMember, context );
context.getMetadataCollector().addSecondPass( new IdGeneratorResolverSecondPass(
idValue,
idProperty,
idAttributeMember,
generatorType,
generatorName,
context
@ -580,8 +558,8 @@ public class GeneratorBinder {
else {
//clone classGenerator and override with local values
final Map<String, IdentifierGeneratorDefinition> generators = new HashMap<>( classGenerators );
generators.putAll( buildGenerators( idProperty, context ) );
makeIdGenerator( idValue, idProperty, generatorType, generatorName, context, generators );
generators.putAll( buildGenerators( idAttributeMember, context ) );
makeIdGenerator( idValue, idAttributeMember, generatorType, generatorName, context, generators );
}
}

View File

@ -9,8 +9,6 @@ package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.util.ArrayList;
import java.util.List;
import org.hibernate.Internal;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
@ -117,61 +115,4 @@ public final class HCANNHelper {
return member.getMember();
}
/**
* Return an annotation of the given type which annotates the given
* annotated program element, or which meta-annotates an annotation
* of the given annotated program element.
*
* @implNote Searches only one level deep
*/
public static <T extends Annotation> T findAnnotation(
XAnnotatedElement annotatedElement,
Class<T> annotationType) {
// first, see if we can find it directly...
final T direct = annotatedElement.getAnnotation( annotationType );
if ( direct != null ) {
return direct;
}
// or as composed...
final Annotation[] annotations = annotatedElement.getAnnotations();
for ( Annotation annotation : annotations ) {
if ( annotationType.equals( annotation.getClass() ) ) {
// we would have found this on the direct search, so no need
// to check its meta-annotations
continue;
}
// we only check one level deep
final T metaAnnotation = annotation.annotationType().getAnnotation( annotationType );
if ( metaAnnotation != null ) {
return metaAnnotation;
}
}
return null;
}
/**
* Return an annotation of the given annotated program element which
* is annotated by the given type of meta-annotation.
*
* @implNote Searches only one level deep
*/
public static List<Annotation> findContainingAnnotations(
XAnnotatedElement annotatedElement,
Class<? extends Annotation> annotationType) {
final List<Annotation> result = new ArrayList<>();
final Annotation[] annotations = annotatedElement.getAnnotations();
for ( Annotation annotation : annotations ) {
final Annotation metaAnn = annotation.annotationType().getAnnotation( annotationType );
if ( metaAnn != null ) {
result.add( annotation );
}
}
return result;
}
}

View File

@ -7,6 +7,7 @@
package org.hibernate.boot.model.internal;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
@ -21,11 +22,10 @@ import org.hibernate.mapping.IdentifierBag;
import org.hibernate.mapping.IdentifierCollection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.usertype.UserCollectionType;
import jakarta.persistence.Column;
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
/**
@ -51,7 +51,7 @@ public class IdBagBinder extends BagBinder {
protected boolean bindStarToManySecondPass(Map<String, PersistentClass> persistentClasses) {
boolean result = super.bindStarToManySecondPass( persistentClasses );
final CollectionId collectionIdAnn = property.getAnnotation( CollectionId.class );
final AnnotationUsage<CollectionId> collectionIdAnn = property.getAnnotationUsage( CollectionId.class );
if ( collectionIdAnn == null ) {
throw new MappingException( "idbag mapping missing '@CollectionId' annotation" );
}
@ -62,13 +62,13 @@ public class IdBagBinder extends BagBinder {
property,
//default access should not be useful
null,
buildingContext.getBootstrapContext().getReflectionManager()
buildingContext
),
"id"
);
final AnnotatedColumns idColumns = AnnotatedColumn.buildColumnsFromAnnotations(
new Column[] { collectionIdAnn.column() },
List.of( collectionIdAnn.getNestedUsage( "column" ) ),
// null,
null,
Nullability.FORCED_NOT_NULL,
@ -100,7 +100,7 @@ public class IdBagBinder extends BagBinder {
final BasicValue id = valueBinder.make();
( (IdentifierCollection) collection ).setIdentifier( id );
final String namedGenerator = collectionIdAnn.generator();
final String namedGenerator = collectionIdAnn.getString( "generator" );
switch (namedGenerator) {
case "identity": {

View File

@ -9,12 +9,12 @@ package org.hibernate.boot.model.internal;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.models.spi.MemberDetails;
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
@ -23,7 +23,7 @@ import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
*/
public class IdGeneratorResolverSecondPass implements SecondPass {
private final SimpleValue id;
private final XProperty idXProperty;
private final MemberDetails idAttributeMember;
private final String generatorType;
private final String generatorName;
private final MetadataBuildingContext buildingContext;
@ -31,12 +31,12 @@ public class IdGeneratorResolverSecondPass implements SecondPass {
public IdGeneratorResolverSecondPass(
SimpleValue id,
XProperty idXProperty,
MemberDetails idAttributeMember,
String generatorType,
String generatorName,
MetadataBuildingContext buildingContext) {
this.id = id;
this.idXProperty = idXProperty;
this.idAttributeMember = idAttributeMember;
this.generatorType = generatorType;
this.generatorName = generatorName;
this.buildingContext = buildingContext;
@ -44,17 +44,17 @@ public class IdGeneratorResolverSecondPass implements SecondPass {
public IdGeneratorResolverSecondPass(
SimpleValue id,
XProperty idXProperty,
MemberDetails idAttributeMember,
String generatorType,
String generatorName,
MetadataBuildingContext buildingContext,
IdentifierGeneratorDefinition localIdentifierGeneratorDefinition) {
this(id,idXProperty,generatorType,generatorName,buildingContext);
this( id, idAttributeMember, generatorType, generatorName, buildingContext );
this.localIdentifierGeneratorDefinition = localIdentifierGeneratorDefinition;
}
@Override
public void doSecondPass(Map<String, PersistentClass> idGeneratorDefinitionMap) throws MappingException {
makeIdGenerator( id, idXProperty, generatorType, generatorName, buildingContext, localIdentifierGeneratorDefinition );
makeIdGenerator( id, idAttributeMember, generatorType, generatorName, buildingContext, localIdentifierGeneratorDefinition );
}
}

View File

@ -12,10 +12,12 @@ import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.ManyToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import java.util.Map;
@ -36,7 +38,7 @@ public class ImplicitToOneJoinTableSecondPass implements SecondPass {
private final PropertyData inferredData;
private final MetadataBuildingContext context;
private final AnnotatedJoinColumns joinColumns;
private final JoinTable joinTable;
private final AnnotationUsage<JoinTable> joinTable;
private final NotFoundAction notFoundAction;
private final ManyToOne value;
@ -45,7 +47,7 @@ public class ImplicitToOneJoinTableSecondPass implements SecondPass {
PropertyData inferredData,
MetadataBuildingContext context,
AnnotatedJoinColumns joinColumns,
JoinTable joinTable,
AnnotationUsage<JoinTable> joinTable,
NotFoundAction notFoundAction,
ManyToOne value) {
this.propertyHolder = propertyHolder;
@ -86,17 +88,25 @@ public class ImplicitToOneJoinTableSecondPass implements SecondPass {
private TableBinder createTableBinder() {
final TableBinder tableBinder = new TableBinder();
tableBinder.setBuildingContext( context );
if ( !joinTable.schema().isEmpty() ) {
tableBinder.setSchema( joinTable.schema() );
final String schema = joinTable.getString( "schema" );
if ( StringHelper.isNotEmpty( schema ) ) {
tableBinder.setSchema( schema );
}
if ( !joinTable.catalog().isEmpty() ) {
tableBinder.setCatalog( joinTable.catalog() );
final String catalog = joinTable.getString( "catalog" );
if ( StringHelper.isNotEmpty( catalog ) ) {
tableBinder.setCatalog( catalog );
}
if ( !joinTable.name().isEmpty() ) {
tableBinder.setName( joinTable.name() );
final String tableName = joinTable.getString( "name" );
if ( StringHelper.isNotEmpty( tableName ) ) {
tableBinder.setName( tableName );
}
tableBinder.setUniqueConstraints( joinTable.uniqueConstraints() );
tableBinder.setJpaIndex( joinTable.indexes() );
tableBinder.setUniqueConstraints( joinTable.getList( "uniqueConstraints" ) );
tableBinder.setJpaIndex( joinTable.getList( "indexes" ) );
return tableBinder;
}

View File

@ -6,7 +6,11 @@
*/
package org.hibernate.boot.model.internal;
import jakarta.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import org.hibernate.AnnotationException;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitIndexNameSource;
@ -16,17 +20,16 @@ import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Formula;
import org.hibernate.mapping.Index;
import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.models.spi.AnnotationUsage;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import jakarta.persistence.UniqueConstraint;
import static java.util.Collections.emptyList;
import static org.hibernate.boot.model.naming.Identifier.toIdentifier;
@ -112,8 +115,8 @@ class IndexBinder {
return new Column( physicalName );
}
private Selectable[] selectables(Table table, String name, final String[] columnNames) {
final int size = columnNames.length;
private Selectable[] selectables(Table table, String name, List<String> columnNames) {
final int size = columnNames.size();
if ( size == 0 ) {
throw new AnnotationException( "Index"
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
@ -121,7 +124,7 @@ class IndexBinder {
}
final Selectable[] columns = new Selectable[size];
for ( int index = 0; index < size; index++ ) {
final String columnName = columnNames[index];
final String columnName = columnNames.get( index );
if ( isEmpty( columnName ) ) {
throw new AnnotationException( "Index"
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
@ -132,8 +135,8 @@ class IndexBinder {
return columns;
}
private Column[] columns(Table table, String name, final String[] columnNames) {
final int size = columnNames.length;
private Column[] columns(Table table, String name, final List<String> columnNames) {
final int size = columnNames.size();
if ( size == 0 ) {
throw new AnnotationException( "Unique constraint"
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
@ -141,7 +144,7 @@ class IndexBinder {
}
final Column[] columns = new Column[size];
for ( int index = 0; index < size; index++ ) {
final String columnName = columnNames[index];
final String columnName = columnNames.get( index );
if ( isEmpty( columnName ) ) {
throw new AnnotationException( "Unique constraint"
+ ( isEmpty( name ) ? "" : " '" + name + "'" )
@ -156,7 +159,7 @@ class IndexBinder {
Table table,
String originalKeyName,
boolean nameExplicit,
String[] columnNames,
List<String> columnNames,
String[] orderings,
boolean unique,
Selectable[] columns) {
@ -187,9 +190,9 @@ class IndexBinder {
}
}
void bindIndexes(Table table, jakarta.persistence.Index[] indexes) {
for ( jakarta.persistence.Index index : indexes ) {
final StringTokenizer tokenizer = new StringTokenizer( index.columnList(), "," );
void bindIndexes(Table table, List<AnnotationUsage<jakarta.persistence.Index>> indexes) {
for ( AnnotationUsage<jakarta.persistence.Index> index : indexes ) {
final StringTokenizer tokenizer = new StringTokenizer( index.getString( "columnList" ), "," );
final List<String> parsed = new ArrayList<>();
while ( tokenizer.hasMoreElements() ) {
final String trimmed = tokenizer.nextToken().trim();
@ -197,39 +200,53 @@ class IndexBinder {
parsed.add( trimmed ) ;
}
}
final String[] columnExpressions = new String[parsed.size()];
final List<String> columnExpressions = CollectionHelper.populatedArrayList( parsed.size(), null );
final String[] ordering = new String[parsed.size()];
initializeColumns( columnExpressions, ordering, parsed );
final String name = index.name();
final boolean unique = index.unique();
createIndexOrUniqueKey( table, name, !name.isEmpty(), columnExpressions, ordering, unique,
selectables( table, name, columnExpressions ) );
final String name = index.getString( "name" );
final boolean unique = index.getBoolean( "unique" );
createIndexOrUniqueKey(
table,
name,
!name.isEmpty(),
columnExpressions,
ordering,
unique,
selectables( table, name, columnExpressions )
);
}
}
void bindUniqueConstraints(Table table, UniqueConstraint[] constraints) {
for ( UniqueConstraint constraint : constraints ) {
final String name = constraint.name();
final String[] columnNames = constraint.columnNames();
createIndexOrUniqueKey( table, name, !name.isEmpty(), columnNames, null, true,
columns( table, name, columnNames ) );
void bindUniqueConstraints(Table table, List<AnnotationUsage<UniqueConstraint>> constraints) {
for ( AnnotationUsage<UniqueConstraint> constraint : constraints ) {
final String name = constraint.getString( "name" );
final List<String> columnNames = constraint.getList( "columnNames" );
createIndexOrUniqueKey(
table,
name,
!name.isEmpty(),
columnNames,
null,
true,
columns( table, name, columnNames )
);
}
}
private void initializeColumns(String[] columns, String[] ordering, List<String> list) {
private void initializeColumns(List<String> columns, String[] ordering, List<String> list) {
for ( int i = 0, size = list.size(); i < size; i++ ) {
final String description = list.get( i );
final String tmp = description.toLowerCase(Locale.ROOT);
if ( tmp.endsWith( " desc" ) ) {
columns[i] = description.substring( 0, description.length() - 5 );
columns.set( i, description.substring( 0, description.length() - 5 ) );
ordering[i] = "desc";
}
else if ( tmp.endsWith( " asc" ) ) {
columns[i] = description.substring( 0, description.length() - 4 );
columns.set( i, description.substring( 0, description.length() - 4 ) );
ordering[i] = "asc";
}
else {
columns[i] = description;
columns.set( i, description );
ordering[i] = null;
}
}
@ -238,10 +255,10 @@ class IndexBinder {
private class IndexOrUniqueKeyNameSource implements ImplicitIndexNameSource, ImplicitUniqueKeyNameSource {
private final MetadataBuildingContext buildingContext;
private final Table table;
private final String[] columnNames;
private final List<String> columnNames;
private final String originalKeyName;
public IndexOrUniqueKeyNameSource(MetadataBuildingContext buildingContext, Table table, String[] columnNames, String originalKeyName) {
public IndexOrUniqueKeyNameSource(MetadataBuildingContext buildingContext, Table table, List<String> columnNames, String originalKeyName) {
this.buildingContext = buildingContext;
this.table = table;
this.columnNames = columnNames;
@ -275,12 +292,12 @@ class IndexBinder {
}
}
private List<Identifier> toIdentifiers(String[] names) {
private List<Identifier> toIdentifiers(List<String> names) {
if ( names == null ) {
return emptyList();
}
final List<Identifier> columnNames = arrayList( names.length );
final List<Identifier> columnNames = arrayList( names.size() );
for ( String name : names ) {
columnNames.add( getDatabase().toIdentifier( name ) );
}

View File

@ -12,6 +12,7 @@ import org.hibernate.annotations.ListIndexBase;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.Join;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.OrderColumn;
@ -32,20 +33,20 @@ public class IndexColumn extends AnnotatedColumn {
}
public static IndexColumn fromAnnotations(
OrderColumn orderColumn,
org.hibernate.annotations.IndexColumn indexColumn,
ListIndexBase listIndexBase,
AnnotationUsage<OrderColumn> orderColumn,
AnnotationUsage<org.hibernate.annotations.IndexColumn> indexColumn,
AnnotationUsage<ListIndexBase> listIndexBase,
PropertyHolder propertyHolder,
PropertyData inferredData,
Map<String, Join> secondaryTables,
MetadataBuildingContext context) {
final IndexColumn column;
if ( orderColumn != null ) {
column = buildColumnFromAnnotation( orderColumn, propertyHolder, inferredData, secondaryTables, context );
column = buildColumnFromOrderColumn( orderColumn, propertyHolder, inferredData, secondaryTables, context );
}
else if ( indexColumn != null ) {
column = buildColumnFromAnnotation( indexColumn, propertyHolder, inferredData, context );
column.setBase( indexColumn.base() );
column = buildColumnFromIndexColumn( indexColumn, propertyHolder, inferredData, context );
column.setBase( indexColumn.getInteger( "base" ) );
}
else {
column = new IndexColumn();
@ -58,7 +59,7 @@ public class IndexColumn extends AnnotatedColumn {
}
if ( listIndexBase != null ) {
column.setBase( listIndexBase.value() );
column.setBase( listIndexBase.getInteger( "value" ) );
}
return column;
@ -94,24 +95,25 @@ public class IndexColumn extends AnnotatedColumn {
*
* @return The index column
*/
public static IndexColumn buildColumnFromAnnotation(
OrderColumn orderColumn,
public static IndexColumn buildColumnFromOrderColumn(
AnnotationUsage<OrderColumn> orderColumn,
PropertyHolder propertyHolder,
PropertyData inferredData,
Map<String, Join> secondaryTables,
MetadataBuildingContext context) {
if ( orderColumn != null ) {
final String sqlType = nullIfEmpty( orderColumn.columnDefinition() );
final String name = orderColumn.name().isEmpty()
final String sqlType = nullIfEmpty( orderColumn.getString( "columnDefinition" ) );
final String explicitName = orderColumn.getString( "name" );
final String name = explicitName.isEmpty()
? inferredData.getPropertyName() + "_ORDER"
: orderColumn.name();
: explicitName;
final IndexColumn column = new IndexColumn();
column.setLogicalColumnName( name );
column.setSqlType( sqlType );
column.setNullable( orderColumn.nullable() );
column.setNullable( orderColumn.getBoolean( "nullable" ) );
// column.setJoins( secondaryTables );
column.setInsertable( orderColumn.insertable() );
column.setUpdatable( orderColumn.updatable() );
column.setInsertable( orderColumn.getBoolean( "insertable" ) );
column.setUpdatable( orderColumn.getBoolean( "updatable" ) );
// column.setContext( context );
// column.setPropertyHolder( propertyHolder );
createParent( propertyHolder, secondaryTables, column, context );
@ -138,22 +140,23 @@ public class IndexColumn extends AnnotatedColumn {
*
* @return The index column
*/
public static IndexColumn buildColumnFromAnnotation(
org.hibernate.annotations.IndexColumn indexColumn,
public static IndexColumn buildColumnFromIndexColumn(
AnnotationUsage<org.hibernate.annotations.IndexColumn> indexColumn,
PropertyHolder propertyHolder,
PropertyData inferredData,
MetadataBuildingContext context) {
if ( indexColumn != null ) {
final String sqlType = nullIfEmpty( indexColumn.columnDefinition() );
final String name = indexColumn.name().isEmpty()
final String explicitName = indexColumn.getString( "name" );
final String name = explicitName.isEmpty()
? inferredData.getPropertyName()
: indexColumn.name();
: explicitName;
final String sqlType = nullIfEmpty( indexColumn.getString( "columnDefinition" ) );
//TODO move it to a getter based system and remove the constructor
final IndexColumn column = new IndexColumn();
column.setLogicalColumnName( name );
column.setSqlType( sqlType );
column.setNullable( indexColumn.nullable() );
column.setBase( indexColumn.base() );
column.setNullable( indexColumn.getBoolean( "nullable" ) );
column.setBase( indexColumn.getInteger( "base" ) );
// column.setContext( context );
// column.setPropertyHolder( propertyHolder );
createParent( propertyHolder, null, column, context );

View File

@ -11,15 +11,16 @@ import java.util.List;
import java.util.Map;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MethodDetails;
import jakarta.persistence.Access;
import jakarta.persistence.EmbeddedId;
@ -40,7 +41,7 @@ import static org.hibernate.boot.model.internal.PropertyBinder.addElementsOfClas
* @author Emmanuel Bernard
*/
public class InheritanceState {
private XClass clazz;
private ClassDetails classDetails;
/**
* Has sibling (either mappedsuperclass entity)
@ -53,33 +54,32 @@ public class InheritanceState {
private boolean hasParents = false;
private InheritanceType type;
private boolean isEmbeddableSuperclass = false;
private final Map<XClass, InheritanceState> inheritanceStatePerClass;
private final List<XClass> classesToProcessForMappedSuperclass = new ArrayList<>();
private final Map<ClassDetails, InheritanceState> inheritanceStatePerClass;
private final List<ClassDetails> classesToProcessForMappedSuperclass = new ArrayList<>();
private final MetadataBuildingContext buildingContext;
private AccessType accessType;
private ElementsToProcess elementsToProcess;
private Boolean hasIdClassOrEmbeddedId;
public InheritanceState(
XClass clazz,
Map<XClass, InheritanceState> inheritanceStatePerClass,
ClassDetails classDetails,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MetadataBuildingContext buildingContext) {
this.setClazz( clazz );
this.setClassDetails( classDetails );
this.buildingContext = buildingContext;
this.inheritanceStatePerClass = inheritanceStatePerClass;
extractInheritanceType();
extractInheritanceType( classDetails );
}
private void extractInheritanceType() {
XAnnotatedElement element = getClazz();
Inheritance inhAnn = element.getAnnotation( Inheritance.class );
MappedSuperclass mappedSuperClass = element.getAnnotation( MappedSuperclass.class );
if ( mappedSuperClass != null ) {
private void extractInheritanceType(ClassDetails classDetails) {
final AnnotationUsage<Inheritance> inheritanceAnn = classDetails.getAnnotationUsage( Inheritance.class );
final AnnotationUsage<MappedSuperclass> mappedSuperAnn = classDetails.getAnnotationUsage( MappedSuperclass.class );
if ( mappedSuperAnn != null ) {
setEmbeddableSuperclass( true );
setType( inhAnn == null ? null : inhAnn.strategy() );
setType( inheritanceAnn == null ? null : inheritanceAnn.getEnum( "strategy", InheritanceType.class ) );
}
else {
setType( inhAnn == null ? SINGLE_TABLE : inhAnn.strategy() );
setType( inheritanceAnn == null ? SINGLE_TABLE : inheritanceAnn.getEnum( "strategy", InheritanceType.class ) );
}
}
@ -91,23 +91,27 @@ public class InheritanceState {
return hasParents() && TABLE_PER_CLASS == getType();
}
public static InheritanceState getInheritanceStateOfSuperEntity(XClass clazz, Map<XClass, InheritanceState> states) {
XClass superclass = clazz;
public static InheritanceState getInheritanceStateOfSuperEntity(
ClassDetails classDetails,
Map<ClassDetails, InheritanceState> states) {
ClassDetails candidate = classDetails;
do {
superclass = superclass.getSuperclass();
final InheritanceState currentState = states.get( superclass );
candidate = candidate.getSuperClass();
final InheritanceState currentState = states.get( candidate );
if ( currentState != null && !currentState.isEmbeddableSuperclass() ) {
return currentState;
}
}
while ( superclass != null && !Object.class.getName().equals( superclass.getName() ) );
while ( candidate != null && !Object.class.getName().equals( candidate.getName() ) );
return null;
}
public static InheritanceState getSuperclassInheritanceState(XClass clazz, Map<XClass, InheritanceState> states) {
XClass superclass = clazz;
public static InheritanceState getSuperclassInheritanceState(
ClassDetails classDetails,
Map<ClassDetails, InheritanceState> states) {
ClassDetails superclass = classDetails;
do {
superclass = superclass.getSuperclass();
superclass = superclass.getSuperClass();
InheritanceState currentState = states.get( superclass );
if ( currentState != null ) {
return currentState;
@ -117,12 +121,12 @@ public class InheritanceState {
return null;
}
public XClass getClazz() {
return clazz;
public ClassDetails getClassDetails() {
return classDetails;
}
public void setClazz(XClass clazz) {
this.clazz = clazz;
public void setClassDetails(ClassDetails classDetails) {
this.classDetails = classDetails;
}
public boolean hasSiblings() {
@ -174,15 +178,15 @@ public class InheritanceState {
addMappedSuperClassInMetadata( component );
}
public XClass getClassWithIdClass(boolean evenIfSubclass) {
public ClassDetails getClassWithIdClass(boolean evenIfSubclass) {
if ( !evenIfSubclass && hasParents() ) {
return null;
}
else if ( clazz.isAnnotationPresent( IdClass.class ) ) {
return clazz;
else if ( classDetails.hasAnnotationUsage( IdClass.class ) ) {
return classDetails;
}
else {
final InheritanceState state = getSuperclassInheritanceState( clazz, inheritanceStatePerClass );
final InheritanceState state = getSuperclassInheritanceState( classDetails, inheritanceStatePerClass );
if ( state != null ) {
return state.getClassWithIdClass( true );
}
@ -201,7 +205,7 @@ public class InheritanceState {
else {
final ElementsToProcess process = getElementsToProcess();
for ( PropertyData property : process.getElements() ) {
if ( property.getProperty().isAnnotationPresent( EmbeddedId.class ) ) {
if ( property.getAttributeMember().hasAnnotationUsage( EmbeddedId.class ) ) {
hasIdClassOrEmbeddedId = true;
break;
}
@ -218,7 +222,7 @@ public class InheritanceState {
*/
private ElementsToProcess getElementsToProcess() {
if ( elementsToProcess == null ) {
InheritanceState inheritanceState = inheritanceStatePerClass.get( clazz );
InheritanceState inheritanceState = inheritanceStatePerClass.get( classDetails );
assert !inheritanceState.isEmbeddableSuperclass();
getMappedSuperclassesTillNextEntityOrdered();
@ -228,10 +232,10 @@ public class InheritanceState {
final ArrayList<PropertyData> elements = new ArrayList<>();
int idPropertyCount = 0;
for ( XClass classToProcessForMappedSuperclass : classesToProcessForMappedSuperclass ) {
for ( ClassDetails classToProcessForMappedSuperclass : classesToProcessForMappedSuperclass ) {
PropertyContainer propertyContainer = new PropertyContainer(
classToProcessForMappedSuperclass,
clazz,
classDetails,
accessType
);
int currentIdPropertyCount = addElementsOfClass(
@ -243,7 +247,7 @@ public class InheritanceState {
}
if ( idPropertyCount == 0 && !inheritanceState.hasParents() ) {
throw new AnnotationException( "Entity '" + clazz.getName() + "' has no identifier"
throw new AnnotationException( "Entity '" + classDetails.getName() + "' has no identifier"
+ " (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)" );
}
elements.trimToSize();
@ -253,57 +257,54 @@ public class InheritanceState {
}
private AccessType determineDefaultAccessType() {
for ( XClass xclass = clazz; xclass != null; xclass = xclass.getSuperclass() ) {
if ( ( xclass.getSuperclass() == null || Object.class.getName().equals( xclass.getSuperclass().getName() ) )
&& ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) )
&& xclass.isAnnotationPresent( Access.class ) ) {
return AccessType.getAccessStrategy( xclass.getAnnotation( Access.class ).value() );
for ( ClassDetails candidate = classDetails; candidate != null; candidate = candidate.getSuperClass() ) {
if ( ( candidate.getSuperClass() == null || Object.class.getName().equals( candidate.getSuperClass().getName() ) )
&& ( candidate.hasAnnotationUsage( Entity.class ) || candidate.hasAnnotationUsage( MappedSuperclass.class ) )
&& candidate.hasAnnotationUsage( Access.class ) ) {
return AccessType.getAccessStrategy( candidate.getAnnotationUsage( Access.class ).getEnum( "value" ) );
}
}
// Guess from identifier.
// FIX: Shouldn't this be determined by the first attribute (i.e., field or property) with annotations,
// but without an explicit Access annotation, according to JPA 2.0 spec 2.3.1: Default Access Type?
for ( XClass xclass = clazz;
xclass != null && !Object.class.getName().equals( xclass.getName() );
xclass = xclass.getSuperclass() ) {
if ( xclass.isAnnotationPresent( Entity.class ) || xclass.isAnnotationPresent( MappedSuperclass.class ) ) {
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.PROPERTY.getType() ) ) {
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
for ( ClassDetails candidate = classDetails;
candidate != null && !Object.class.getName().equals( candidate.getName() );
candidate = candidate.getSuperClass() ) {
if ( candidate.hasAnnotationUsage( Entity.class ) || candidate.hasAnnotationUsage( MappedSuperclass.class ) ) {
for ( MethodDetails method : candidate.getMethods() ) {
if ( method.getMethodKind() != MethodDetails.MethodKind.GETTER ) {
continue;
}
if ( method.hasAnnotationUsage( Id.class ) || method.hasAnnotationUsage( EmbeddedId.class ) ) {
return AccessType.PROPERTY;
}
}
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
for ( FieldDetails field : candidate.getFields() ) {
if ( field.hasAnnotationUsage( Id.class ) || field.hasAnnotationUsage( EmbeddedId.class ) ) {
return AccessType.FIELD;
}
}
for ( XProperty prop : xclass.getDeclaredProperties( AccessType.RECORD.getType() ) ) {
final boolean isEmbeddedId = prop.isAnnotationPresent( EmbeddedId.class );
if ( prop.isAnnotationPresent( Id.class ) || isEmbeddedId ) {
return AccessType.RECORD;
}
}
}
}
throw new AnnotationException( "Entity '" + clazz.getName() + "' has no identifier"
+ " (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)" );
throw new AnnotationException( "Entity '" + classDetails.getName() + "' has no identifier"
+ " (every '@Entity' class must declare or inherit at least one '@Id' or '@EmbeddedId' property)" );
}
private void getMappedSuperclassesTillNextEntityOrdered() {
//ordered to allow proper messages on properties subclassing
XClass currentClassInHierarchy = clazz;
ClassDetails currentClassInHierarchy = classDetails;
InheritanceState superclassState;
do {
classesToProcessForMappedSuperclass.add( 0, currentClassInHierarchy );
XClass superClass = currentClassInHierarchy;
ClassDetails superClass = currentClassInHierarchy;
do {
superClass = superClass.getSuperclass();
superClass = superClass.getSuperClass();
superclassState = inheritanceStatePerClass.get( superClass );
}
while ( superClass != null
&& !buildingContext.getBootstrapContext().getReflectionManager().equals( superClass, Object.class )
&& !Object.class.getName().equals( superClass.getClassName() )
&& superclassState == null );
currentClassInHierarchy = superClass;
@ -328,23 +329,25 @@ public class InheritanceState {
private org.hibernate.mapping.MappedSuperclass processMappedSuperclass(Table implicitTable) {
//add @MappedSuperclass in the metadata
// classes from 0 to n-1 are @MappedSuperclass and should be linked
final InheritanceState superEntityState = getInheritanceStateOfSuperEntity( clazz, inheritanceStatePerClass );
final InheritanceState superEntityState = getInheritanceStateOfSuperEntity( classDetails, inheritanceStatePerClass );
final PersistentClass superEntity =
superEntityState != null ?
buildingContext.getMetadataCollector().getEntityBinding( superEntityState.getClazz().getName() ) :
buildingContext.getMetadataCollector().getEntityBinding( superEntityState.getClassDetails().getName() ) :
null;
final int lastMappedSuperclass = classesToProcessForMappedSuperclass.size() - 1;
org.hibernate.mapping.MappedSuperclass mappedSuperclass = null;
for ( int index = 0; index < lastMappedSuperclass; index++ ) {
org.hibernate.mapping.MappedSuperclass parentSuperclass = mappedSuperclass;
final Class<?> type = buildingContext.getBootstrapContext().getReflectionManager()
.toClass( classesToProcessForMappedSuperclass.get( index ) );
// todo (jpa32) : causes the mapped-superclass Class reference to be loaded...
// - but this is how it's always worked, so...
final ClassDetails mappedSuperclassDetails = classesToProcessForMappedSuperclass.get( index );
final Class<?> mappedSuperclassJavaType = mappedSuperclassDetails.toJavaClass();
//add MappedSuperclass if not already there
mappedSuperclass = buildingContext.getMetadataCollector().getMappedSuperclass( type );
mappedSuperclass = buildingContext.getMetadataCollector().getMappedSuperclass( mappedSuperclassJavaType );
if ( mappedSuperclass == null ) {
mappedSuperclass = new org.hibernate.mapping.MappedSuperclass( parentSuperclass, superEntity, implicitTable );
mappedSuperclass.setMappedClass( type );
buildingContext.getMetadataCollector().addMappedSuperclass( type, mappedSuperclass );
mappedSuperclass.setMappedClass( mappedSuperclassJavaType );
buildingContext.getMetadataCollector().addMappedSuperclass( mappedSuperclassJavaType, mappedSuperclass );
}
}
return mappedSuperclass;

View File

@ -2222,7 +2222,7 @@ public class JPAXMLOverriddenAnnotationReader implements AnnotationReader {
List<StoredProcedureParameter> storedProcedureParameters = new ArrayList<>();
for ( JaxbStoredProcedureParameterImpl parameterElement : element.getParameter() ) {
for ( JaxbStoredProcedureParameterImpl parameterElement : element.getProcedureParameters() ) {
AnnotationDescriptor parameterDescriptor = new AnnotationDescriptor( StoredProcedureParameter.class );
copyAttribute( parameterDescriptor, "name", parameterElement.getName(), false );
ParameterMode modeValue = parameterElement.getMode();

View File

@ -20,6 +20,7 @@ import org.hibernate.mapping.List;
import org.hibernate.mapping.OneToMany;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.usertype.UserCollectionType;
@ -49,7 +50,7 @@ public class ListBinder extends CollectionBinder {
}
@Override
public void setSqlOrderBy(OrderBy orderByAnn) {
public void setSqlOrderBy(AnnotationUsage<OrderBy> orderByAnn) {
if ( orderByAnn != null ) {
throw new AnnotationException( "A collection of type 'List' is annotated '@OrderBy'" );
}

View File

@ -14,10 +14,7 @@ import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
import org.hibernate.MappingException;
import org.hibernate.annotations.MapKeyCompositeType;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.engine.jdbc.Size;
@ -36,6 +33,11 @@ import org.hibernate.mapping.Selectable;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.Value;
import org.hibernate.models.internal.ClassTypeDetailsImpl;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.type.BasicType;
import org.hibernate.usertype.CompositeUserType;
@ -44,6 +46,7 @@ import org.hibernate.usertype.UserCollectionType;
import jakarta.persistence.AttributeOverride;
import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.ForeignKey;
import jakarta.persistence.InheritanceType;
import jakarta.persistence.MapKeyClass;
import jakarta.persistence.MapKeyColumn;
@ -107,11 +110,9 @@ public class MapBinder extends CollectionBinder {
};
}
private void makeOneToManyMapKeyColumnNullableIfNotInProperty(
final XProperty property) {
private void makeOneToManyMapKeyColumnNullableIfNotInProperty(MemberDetails property) {
final Map map = (Map) this.collection;
if ( map.isOneToMany() &&
property.isAnnotationPresent( MapKeyColumn.class ) ) {
if ( map.isOneToMany() && property.hasAnnotationUsage( MapKeyColumn.class ) ) {
final Value indexValue = map.getIndex();
if ( indexValue.getColumnSpan() != 1 ) {
throw new AssertionFailure( "Map key mapped by @MapKeyColumn does not have 1 column" );
@ -150,11 +151,11 @@ public class MapBinder extends CollectionBinder {
}
private void bindKeyFromAssociationTable(
XClass elementType,
TypeDetails elementType,
java.util.Map<String, PersistentClass> persistentClasses,
boolean hasMapKeyProperty,
String mapKeyPropertyName,
XProperty property,
MemberDetails property,
boolean isEmbedded,
AnnotatedColumns mapKeyColumns,
AnnotatedJoinColumns mapKeyManyToManyColumns) {
@ -170,7 +171,7 @@ public class MapBinder extends CollectionBinder {
private void handleMapKey(
java.util.Map<String, PersistentClass> persistentClasses,
XProperty property,
MemberDetails property,
boolean isEmbedded,
AnnotatedColumns mapKeyColumns,
AnnotatedJoinColumns mapKeyManyToManyColumns) {
@ -184,14 +185,15 @@ public class MapBinder extends CollectionBinder {
bindManyToManyInverseForeignKey( collectionEntity, mapKeyManyToManyColumns, element, false );
}
else {
final XClass keyClass = mapKeyClass( mapKeyType );
final ClassDetails keyClass = mapKeyClass( mapKeyType );
final TypeDetails keyTypeDetails = new ClassTypeDetailsImpl( keyClass, TypeDetails.Kind.CLASS );
handleMapKey(
property,
mapKeyColumns,
mapKeyType,
keyClass,
annotatedMapKeyType( property, isEmbedded, mapKeyType, keyClass ),
buildCollectionPropertyHolder( property, keyClass ),
keyTypeDetails,
annotatedMapKeyType( property, isEmbedded, mapKeyType, keyTypeDetails ),
buildCollectionPropertyHolder( property, keyTypeDetails ),
accessType( property, collection.getOwner() )
);
}
@ -205,10 +207,10 @@ public class MapBinder extends CollectionBinder {
}
private AnnotatedClassType annotatedMapKeyType(
XProperty property,
MemberDetails property,
boolean isEmbedded,
String mapKeyType,
XClass keyClass) {
TypeDetails keyTypeDetails) {
if ( isPrimitive( mapKeyType ) ) {
return NONE;
}
@ -216,32 +218,31 @@ public class MapBinder extends CollectionBinder {
// force in case of attribute override naming the key
return isEmbedded || mappingDefinedAttributeOverrideOnMapKey( property )
? EMBEDDABLE
: buildingContext.getMetadataCollector().getClassType( keyClass );
: buildingContext.getMetadataCollector().getClassType( keyTypeDetails.determineRawClass() );
}
}
private XClass mapKeyClass(String mapKeyType) {
private ClassDetails mapKeyClass(String mapKeyType) {
if ( isPrimitive( mapKeyType ) ) {
return null;
}
else {
final BootstrapContext bootstrapContext = buildingContext.getBootstrapContext();
final Class<Object> mapKeyClass = bootstrapContext.getClassLoaderAccess().classForName( mapKeyType );
return bootstrapContext.getReflectionManager().toXClass( mapKeyClass );
return buildingContext.getMetadataCollector().getSourceModelBuildingContext().getClassDetailsRegistry().resolveClassDetails( mapKeyType );
}
}
private static String getKeyType(XProperty property) {
private static String getKeyType(MemberDetails property) {
//target has priority over reflection for the map key type
//JPA 2 has priority
final Class<?> target = property.isAnnotationPresent( MapKeyClass.class )
? property.getAnnotation( MapKeyClass.class ).value()
final AnnotationUsage<MapKeyClass> mapKeyClassAnn = property.getAnnotationUsage( MapKeyClass.class );
final Class<?> target = mapKeyClassAnn != null
? mapKeyClassAnn.getClassDetails( "value" ).toJavaClass()
: void.class;
return void.class.equals( target ) ? property.getMapKey().getName() : target.getName();
return void.class.equals( target ) ? property.getMapKeyType().getName() : target.getName();
}
private void handleMapKeyProperty(
XClass elementType,
TypeDetails elementType,
java.util.Map<String, PersistentClass> persistentClasses,
String mapKeyPropertyName) {
final PersistentClass associatedClass = persistentClasses.get( elementType.getName() );
@ -255,7 +256,7 @@ public class MapBinder extends CollectionBinder {
+ "' not found in target entity '" + associatedClass.getEntityName() + "'" );
}
// HHH-11005 - if InheritanceType.JOINED then need to find class defining the column
final InheritanceState inheritanceState = inheritanceStatePerClass.get( elementType );
final InheritanceState inheritanceState = inheritanceStatePerClass.get( elementType.determineRawClass() );
final PersistentClass targetEntity = InheritanceType.JOINED == inheritanceState.getType()
? mapProperty.getPersistentClass()
: associatedClass;
@ -265,8 +266,13 @@ public class MapBinder extends CollectionBinder {
}
private CollectionPropertyHolder buildCollectionPropertyHolder(
XProperty property,
XClass keyClass) {
MemberDetails property,
TypeDetails keyClass) {
return buildCollectionPropertyHolder( property, keyClass.determineRawClass() );
}
private CollectionPropertyHolder buildCollectionPropertyHolder(
MemberDetails property,
ClassDetails keyClass) {
final CollectionPropertyHolder holder = buildPropertyHolder(
collection,
qualify( collection.getRole(), "mapkey" ),
@ -283,18 +289,17 @@ public class MapBinder extends CollectionBinder {
return holder;
}
private void handleForeignKey(XProperty property, ManyToOne element) {
final jakarta.persistence.ForeignKey foreignKey = getMapKeyForeignKey( property );
private void handleForeignKey(MemberDetails property, ManyToOne element) {
final AnnotationUsage<ForeignKey> foreignKey = getMapKeyForeignKey( property );
if ( foreignKey != null ) {
final ConstraintMode constraintMode = foreignKey.value();
final ConstraintMode constraintMode = foreignKey.getEnum( "value" );
if ( constraintMode == ConstraintMode.NO_CONSTRAINT
|| constraintMode == ConstraintMode.PROVIDER_DEFAULT
&& getBuildingContext().getBuildingOptions().isNoConstraintByDefault() ) {
|| constraintMode == ConstraintMode.PROVIDER_DEFAULT && getBuildingContext().getBuildingOptions().isNoConstraintByDefault() ) {
element.disableForeignKey();
}
else {
element.setForeignKeyName( nullIfEmpty( foreignKey.name() ) );
element.setForeignKeyDefinition( nullIfEmpty( foreignKey.foreignKeyDefinition() ) );
element.setForeignKeyName( nullIfEmpty( foreignKey.getString( "name" ) ) );
element.setForeignKeyDefinition( nullIfEmpty( foreignKey.getString( "foreignKeyDefinition" ) ) );
}
}
}
@ -315,28 +320,28 @@ public class MapBinder extends CollectionBinder {
}
private void handleMapKey(
XProperty property,
MemberDetails property,
AnnotatedColumns mapKeyColumns,
String mapKeyType,
XClass keyClass,
TypeDetails keyTypeDetails,
AnnotatedClassType classType,
CollectionPropertyHolder holder,
AccessType accessType) {
final Class<? extends CompositeUserType<?>> compositeUserType =
resolveCompositeUserType( property, keyClass, buildingContext );
final Class<? extends CompositeUserType<?>> compositeUserType
= resolveCompositeUserType( property, keyTypeDetails, buildingContext );
if ( classType == EMBEDDABLE || compositeUserType != null ) {
handleCompositeMapKey( keyClass, holder, accessType, compositeUserType );
handleCompositeMapKey( keyTypeDetails, holder, accessType, compositeUserType );
}
else {
handleMapKey( property, mapKeyColumns, mapKeyType, keyClass, holder, accessType );
handleMapKey( property, mapKeyColumns, mapKeyType, keyTypeDetails, holder, accessType );
}
}
private void handleMapKey(
XProperty property,
MemberDetails property,
AnnotatedColumns mapKeyColumns,
String mapKeyType,
XClass keyClass,
TypeDetails keyTypeDetails,
CollectionPropertyHolder holder,
AccessType accessType) {
final BasicValueBinder elementBinder = new BasicValueBinder( BasicValueBinder.Kind.MAP_KEY, buildingContext );
@ -353,9 +358,9 @@ public class MapBinder extends CollectionBinder {
//the algorithm generally does not apply for map key anyway
elementBinder.setType(
property,
keyClass,
keyTypeDetails,
collection.getOwnerEntityName(),
holder.mapKeyAttributeConverterDescriptor( property, keyClass )
holder.mapKeyAttributeConverterDescriptor( property, keyTypeDetails )
);
elementBinder.setPersistentClassName( propertyHolder.getEntityName() );
elementBinder.setAccessType( accessType );
@ -363,13 +368,13 @@ public class MapBinder extends CollectionBinder {
}
private void handleCompositeMapKey(
XClass keyClass,
TypeDetails keyTypeDetails,
CollectionPropertyHolder holder,
AccessType accessType,
Class<? extends CompositeUserType<?>> compositeUserType) {
getMap().setIndex( fillEmbeddable(
holder,
propertyPreloadedData( keyClass ),
propertyPreloadedData( keyTypeDetails ),
accessType,
//TODO be smart with isNullable
true,
@ -385,55 +390,51 @@ public class MapBinder extends CollectionBinder {
) );
}
private PropertyPreloadedData propertyPreloadedData(XClass keyClass) {
private PropertyPreloadedData propertyPreloadedData(TypeDetails keyTypeDetails) {
return isHibernateExtensionMapping()
? new PropertyPreloadedData( AccessType.PROPERTY, "index", keyClass )
? new PropertyPreloadedData( AccessType.PROPERTY, "index", keyTypeDetails )
// "key" is the JPA 2 prefix for map keys
: new PropertyPreloadedData( AccessType.PROPERTY, "key", keyClass );
: new PropertyPreloadedData( AccessType.PROPERTY, "key", keyTypeDetails );
}
private static Class<? extends CompositeUserType<?>> resolveCompositeUserType(
XProperty property,
XClass returnedClass,
MemberDetails property,
TypeDetails returnedClass,
MetadataBuildingContext context) {
final MapKeyCompositeType compositeType = property.getAnnotation( MapKeyCompositeType.class );
final AnnotationUsage<MapKeyCompositeType> compositeType = property.getAnnotationUsage( MapKeyCompositeType.class );
if ( compositeType != null ) {
return compositeType.value();
final ClassDetails compositeTypeImplDetails = compositeType.getClassDetails( "value" );
return compositeTypeImplDetails.toJavaClass();
}
if ( returnedClass != null ) {
final Class<?> embeddableClass = context.getBootstrapContext()
.getReflectionManager()
.toClass( returnedClass );
if ( embeddableClass != null ) {
return context.getMetadataCollector().findRegisteredCompositeUserType( embeddableClass );
}
return context.getMetadataCollector().findRegisteredCompositeUserType( returnedClass.determineRawClass().toJavaClass() );
}
return null;
}
private jakarta.persistence.ForeignKey getMapKeyForeignKey(XProperty property) {
final MapKeyJoinColumns mapKeyJoinColumns = property.getAnnotation( MapKeyJoinColumns.class );
final MapKeyJoinColumn mapKeyJoinColumn = property.getAnnotation( MapKeyJoinColumn.class );
private AnnotationUsage<jakarta.persistence.ForeignKey> getMapKeyForeignKey(MemberDetails property) {
final AnnotationUsage<MapKeyJoinColumns> mapKeyJoinColumns = property.getAnnotationUsage( MapKeyJoinColumns.class );
final AnnotationUsage<MapKeyJoinColumn> mapKeyJoinColumn = property.getAnnotationUsage( MapKeyJoinColumn.class );
if ( mapKeyJoinColumns != null ) {
return mapKeyJoinColumns.foreignKey();
return mapKeyJoinColumns.getNestedUsage( "foreignKey" );
}
else if ( mapKeyJoinColumn != null ) {
return mapKeyJoinColumn.foreignKey();
return mapKeyJoinColumn.getNestedUsage( "foreignKey" );
}
else {
return null;
}
}
private boolean mappingDefinedAttributeOverrideOnMapKey(XProperty property) {
if ( property.isAnnotationPresent( AttributeOverride.class ) ) {
return namedMapKey( property.getAnnotation( AttributeOverride.class ) );
private boolean mappingDefinedAttributeOverrideOnMapKey(MemberDetails property) {
if ( property.hasAnnotationUsage( AttributeOverride.class ) ) {
return namedMapKey( property.getAnnotationUsage( AttributeOverride.class ) );
}
if ( property.isAnnotationPresent( AttributeOverrides.class ) ) {
final AttributeOverrides annotations = property.getAnnotation( AttributeOverrides.class );
for ( AttributeOverride attributeOverride : annotations.value() ) {
if ( property.hasAnnotationUsage( AttributeOverrides.class ) ) {
final AnnotationUsage<AttributeOverrides> annotations = property.getAnnotationUsage( AttributeOverrides.class );
for ( AnnotationUsage<AttributeOverride> attributeOverride : annotations.<AnnotationUsage<AttributeOverride>>getList( "value" ) ) {
if ( namedMapKey( attributeOverride ) ) {
return true;
}
@ -442,8 +443,8 @@ public class MapBinder extends CollectionBinder {
return false;
}
private boolean namedMapKey(AttributeOverride annotation) {
return annotation.name().startsWith( "key." );
private boolean namedMapKey(AnnotationUsage<AttributeOverride> annotation) {
return annotation.getString( "name" ).startsWith( "key." );
}
private Value createFormulatedValue(

View File

@ -1,95 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation;
import jakarta.persistence.CheckConstraint;
import jakarta.persistence.Column;
import jakarta.persistence.MapKeyColumn;
/**
* @author Emmanuel Bernard
*/
@SuppressWarnings({ "ClassExplicitlyAnnotation" })
public class MapKeyColumnDelegator implements Column {
private final MapKeyColumn column;
public MapKeyColumnDelegator(MapKeyColumn column) {
this.column = column;
}
@Override
public String name() {
return column.name();
}
@Override
public boolean unique() {
return column.unique();
}
@Override
public boolean nullable() {
return column.nullable();
}
@Override
public boolean insertable() {
return column.insertable();
}
@Override
public boolean updatable() {
return column.updatable();
}
@Override
public String columnDefinition() {
return column.columnDefinition();
}
@Override
public String options() {
return column.options();
}
@Override
public String table() {
return column.table();
}
@Override
public int length() {
return column.length();
}
@Override
public int precision() {
return column.precision();
}
@Override
public int scale() {
return column.scale();
}
@Override
public CheckConstraint[] check() {
return new CheckConstraint[0];
}
@Override
public String comment() {
return "";
}
@Override
public Class<? extends Annotation> annotationType() {
return Column.class;
}
}

View File

@ -8,6 +8,12 @@ package org.hibernate.boot.model.internal;
import java.lang.annotation.Annotation;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.MutableAnnotationUsage;
import jakarta.persistence.CheckConstraint;
import jakarta.persistence.Column;
import jakarta.persistence.ForeignKey;
@ -21,10 +27,37 @@ import jakarta.persistence.MapKeyJoinColumn;
public class MapKeyJoinColumnDelegator implements JoinColumn {
private final MapKeyJoinColumn column;
public MapKeyJoinColumnDelegator(AnnotationUsage<MapKeyJoinColumn> column) {
this( column.toAnnotation() );
}
public MapKeyJoinColumnDelegator(MapKeyJoinColumn column) {
this.column = column;
}
public static MutableAnnotationUsage<JoinColumn> fromMapKeyJoinColumn(
AnnotationUsage<MapKeyJoinColumn> mapKeyJoinColumn,
MemberDetails attributeMember,
MetadataBuildingContext context) {
final MutableAnnotationUsage<JoinColumn> joinColumn = JpaAnnotations.JOIN_COLUMN.createUsage(
attributeMember,
context.getMetadataCollector().getSourceModelBuildingContext()
);
joinColumn.setAttributeValue( "name", mapKeyJoinColumn.getAttributeValue( "name" ) );
joinColumn.setAttributeValue( "table", mapKeyJoinColumn.getAttributeValue( "table" ) );
joinColumn.setAttributeValue( "unique", mapKeyJoinColumn.getAttributeValue( "unique" ) );
joinColumn.setAttributeValue( "nullable", mapKeyJoinColumn.getAttributeValue( "nullable" ) );
joinColumn.setAttributeValue( "insertable", mapKeyJoinColumn.getAttributeValue( "insertable" ) );
joinColumn.setAttributeValue( "referencedColumnName", mapKeyJoinColumn.getAttributeValue( "referencedColumnName" ) );
joinColumn.setAttributeValue( "columnDefinition", mapKeyJoinColumn.getAttributeValue( "columnDefinition" ) );
joinColumn.setAttributeValue( "options", mapKeyJoinColumn.getAttributeValue( "options" ) );
// joinColumn.setAttributeValue( "comment", mapKeyJoinColumn.getAttributeValue( "comment" ) );
joinColumn.setAttributeValue( "foreignKey", mapKeyJoinColumn.getAttributeValue( "foreignKey" ) );
return joinColumn;
}
@Override
public String name() {
return column.name();

View File

@ -13,7 +13,6 @@ import org.hibernate.MappingException;
import org.hibernate.annotations.LazyGroup;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.boot.spi.SecondPass;
@ -27,6 +26,9 @@ import org.hibernate.mapping.OneToOne;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.SortableValue;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.type.ForeignKeyDirection;
import jakarta.persistence.ForeignKey;
@ -45,16 +47,16 @@ import static org.hibernate.type.ForeignKeyDirection.TO_PARENT;
* in a second pass.
*/
public class OneToOneSecondPass implements SecondPass {
private final MetadataBuildingContext buildingContext;
private final PropertyData inferredData;
private final PropertyHolder propertyHolder;
private final String mappedBy;
private final String ownerEntity;
private final PropertyHolder propertyHolder;
private final NotFoundAction notFoundAction;
private final PropertyData inferredData;
private final OnDeleteAction onDeleteAction;
private final boolean optional;
private final String cascadeStrategy;
private final AnnotatedJoinColumns joinColumns;
private final MetadataBuildingContext buildingContext;
private final String referencedEntityName;
private final boolean annotatedEntity;
@ -95,7 +97,7 @@ public class OneToOneSecondPass implements SecondPass {
final String propertyName = inferredData.getPropertyName();
value.setPropertyName( propertyName );
value.setReferencedEntityName( referencedEntityName );
XProperty property = inferredData.getProperty();
MemberDetails property = inferredData.getAttributeMember();
defineFetchingStrategy( value, property, inferredData, propertyHolder );
//value.setFetchMode( fetchMode );
value.setOnDeleteAction( onDeleteAction );
@ -103,20 +105,20 @@ public class OneToOneSecondPass implements SecondPass {
value.setConstrained( !optional );
value.setForeignKeyType( getForeignKeyDirection() );
bindForeignKeyNameAndDefinition( value, property, property.getAnnotation( ForeignKey.class ), buildingContext );
bindForeignKeyNameAndDefinition( value, property, property.getAnnotationUsage( ForeignKey.class ), buildingContext );
final PropertyBinder binder = new PropertyBinder();
binder.setName( propertyName );
binder.setProperty( property );
binder.setMemberDetails( property );
binder.setValue( value );
binder.setCascade( cascadeStrategy );
binder.setAccessType( inferredData.getDefaultAccess() );
binder.setBuildingContext( buildingContext );
binder.setHolder( propertyHolder );
final LazyGroup lazyGroupAnnotation = property.getAnnotation( LazyGroup.class );
final AnnotationUsage<LazyGroup> lazyGroupAnnotation = property.getAnnotationUsage( LazyGroup.class );
if ( lazyGroupAnnotation != null ) {
binder.setLazyGroup( lazyGroupAnnotation.value() );
binder.setLazyGroup( lazyGroupAnnotation.getString( "value" ) );
}
final Property result = binder.makeProperty();
@ -148,7 +150,7 @@ public class OneToOneSecondPass implements SecondPass {
}
final Property targetProperty = targetProperty( oneToOne, targetEntity );
if ( targetProperty.getValue() instanceof OneToOne ) {
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
propertyHolder.addProperty( property, inferredData.getAttributeMember(), inferredData.getDeclaringClass() );
}
else if ( targetProperty.getValue() instanceof ManyToOne ) {
bindTargetManyToOne( persistentClasses, oneToOne, property, targetEntity, targetProperty );
@ -186,15 +188,7 @@ public class OneToOneSecondPass implements SecondPass {
final Join mappedByJoin = buildJoinFromMappedBySide(
persistentClasses.get( ownerEntity ), targetProperty, otherSideJoin
);
final ManyToOne manyToOne = new ManyToOne( buildingContext, mappedByJoin.getTable() );
manyToOne.setNotFoundAction( notFoundAction );
manyToOne.setOnDeleteAction( oneToOne.getOnDeleteAction() );
manyToOne.setFetchMode( oneToOne.getFetchMode() );
manyToOne.setLazy( oneToOne.isLazy() );
manyToOne.setReferencedEntityName( oneToOne.getReferencedEntityName() );
manyToOne.setReferencedPropertyName( mappedBy );
manyToOne.setUnwrapProxy( oneToOne.isUnwrapProxy() );
manyToOne.markAsLogicalOneToOne();
final ManyToOne manyToOne = createManyToOne( oneToOne, mappedByJoin );
property.setValue( manyToOne );
for ( Column column: otherSideJoin.getKey().getColumns() ) {
Column copy = column.clone();
@ -204,7 +198,7 @@ public class OneToOneSecondPass implements SecondPass {
mappedByJoin.addProperty( property );
}
else {
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
propertyHolder.addProperty( property, inferredData.getAttributeMember(), inferredData.getDeclaringClass() );
}
oneToOne.setReferencedPropertyName( mappedBy );
@ -225,6 +219,19 @@ public class OneToOneSecondPass implements SecondPass {
}
}
private ManyToOne createManyToOne(OneToOne oneToOne, Join mappedByJoin) {
final ManyToOne manyToOne = new ManyToOne( buildingContext, mappedByJoin.getTable() );
manyToOne.setNotFoundAction( notFoundAction );
manyToOne.setOnDeleteAction( oneToOne.getOnDeleteAction() );
manyToOne.setFetchMode( oneToOne.getFetchMode() );
manyToOne.setLazy( oneToOne.isLazy() );
manyToOne.setReferencedEntityName( oneToOne.getReferencedEntityName() );
manyToOne.setReferencedPropertyName( mappedBy );
manyToOne.setUnwrapProxy( oneToOne.isUnwrapProxy() );
manyToOne.markAsLogicalOneToOne();
return manyToOne;
}
private Property targetProperty(OneToOne oneToOne, PersistentClass targetEntity) {
try {
Property targetProperty = findPropertyByName( targetEntity, mappedBy );
@ -256,7 +263,7 @@ public class OneToOneSecondPass implements SecondPass {
);
secondPass.doSecondPass(persistentClasses);
//no column associated since it's a one to one
propertyHolder.addProperty( property, inferredData.getDeclaringClass() );
propertyHolder.addProperty( property, inferredData.getAttributeMember(), inferredData.getDeclaringClass() );
}
/**

View File

@ -11,20 +11,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapsId;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Version;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
@ -40,12 +26,10 @@ import org.hibernate.annotations.NaturalId;
import org.hibernate.annotations.OptimisticLock;
import org.hibernate.annotations.Parent;
import org.hibernate.annotations.ValueGenerationType;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.binder.AttributeBinder;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ImplicitNamingStrategy;
import org.hibernate.boot.model.naming.ImplicitUniqueKeyNameSource;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.spi.AccessType;
@ -67,35 +51,52 @@ import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
import org.hibernate.metamodel.spi.EmbeddableInstantiator;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.resource.beans.container.spi.BeanContainer;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.usertype.CompositeUserType;
import org.jboss.logging.Logger;
import jakarta.persistence.Basic;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.EmbeddedId;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.MapsId;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Version;
import static jakarta.persistence.FetchType.LAZY;
import static java.util.Collections.singletonList;
import static org.hibernate.boot.model.internal.AnyBinder.bindAny;
import static org.hibernate.boot.model.internal.BinderHelper.getMappedSuperclassOrNull;
import static org.hibernate.boot.model.internal.BinderHelper.getPath;
import static org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId;
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
import static org.hibernate.boot.model.internal.BinderHelper.isCompositeId;
import static org.hibernate.boot.model.internal.BinderHelper.isGlobalGeneratorNameGlobal;
import static org.hibernate.boot.model.internal.ClassPropertyHolder.handleGenericComponentProperty;
import static org.hibernate.boot.model.internal.ClassPropertyHolder.prepareActualProperty;
import static org.hibernate.boot.model.internal.CollectionBinder.bindCollection;
import static org.hibernate.boot.model.internal.GeneratorBinder.createForeignGenerator;
import static org.hibernate.boot.model.internal.GeneratorBinder.createIdGenerator;
import static org.hibernate.boot.model.internal.GeneratorBinder.generatorCreator;
import static org.hibernate.boot.model.internal.BinderHelper.getMappedSuperclassOrNull;
import static org.hibernate.boot.model.internal.BinderHelper.getPath;
import static org.hibernate.boot.model.internal.BinderHelper.getPropertyOverriddenByMapperOrMapsId;
import static org.hibernate.boot.model.internal.BinderHelper.hasToOneAnnotation;
import static org.hibernate.boot.model.internal.GeneratorBinder.identifierGeneratorCreator;
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
import static org.hibernate.boot.model.internal.EmbeddableBinder.createCompositeBinder;
import static org.hibernate.boot.model.internal.EmbeddableBinder.createEmbeddable;
import static org.hibernate.boot.model.internal.EmbeddableBinder.isEmbedded;
import static org.hibernate.boot.model.internal.HCANNHelper.findAnnotation;
import static org.hibernate.boot.model.internal.HCANNHelper.findContainingAnnotations;
import static org.hibernate.boot.model.internal.GeneratorBinder.createForeignGenerator;
import static org.hibernate.boot.model.internal.GeneratorBinder.createIdGenerator;
import static org.hibernate.boot.model.internal.GeneratorBinder.generatorCreator;
import static org.hibernate.boot.model.internal.GeneratorBinder.identifierGeneratorCreator;
import static org.hibernate.boot.model.internal.GeneratorBinder.makeIdGenerator;
import static org.hibernate.boot.model.internal.TimeZoneStorageHelper.resolveTimeZoneStorageCompositeUserType;
import static org.hibernate.boot.model.internal.ToOneBinder.bindManyToOne;
import static org.hibernate.boot.model.internal.ToOneBinder.bindOneToOne;
@ -127,7 +128,7 @@ public class PropertyBinder {
private boolean updatable = true;
private String cascade;
private BasicValueBinder basicValueBinder;
private XClass declaringClass;
private ClassDetails declaringClass;
private boolean declaringClassSet;
private boolean embedded;
private EntityBinder entityBinder;
@ -148,10 +149,10 @@ public class PropertyBinder {
// property can be null
// prefer propertyName to property.getName() since some are overloaded
private XProperty property;
private XClass returnedClass;
private MemberDetails memberDetails;
private ClassDetails returnedClass;
private boolean isId;
private Map<XClass, InheritanceState> inheritanceStatePerClass;
private Map<ClassDetails, InheritanceState> inheritanceStatePerClass;
public void setInsertable(boolean insertable) {
this.insertable = insertable;
@ -206,8 +207,8 @@ public class PropertyBinder {
this.buildingContext = buildingContext;
}
public void setDeclaringClass(XClass declaringClass) {
this.declaringClass = declaringClass;
public void setDeclaringClass(ClassDetails declaringClassDetails) {
this.declaringClass = declaringClassDetails;
this.declaringClassSet = true;
}
@ -215,11 +216,11 @@ public class PropertyBinder {
return value instanceof ToOne;
}
public void setProperty(XProperty property) {
this.property = property;
public void setMemberDetails(MemberDetails memberDetails) {
this.memberDetails = memberDetails;
}
public void setReturnedClass(XClass returnedClass) {
public void setReturnedClass(ClassDetails returnedClass) {
this.returnedClass = returnedClass;
}
@ -239,7 +240,7 @@ public class PropertyBinder {
return isId;
}
public void setInheritanceStatePerClass(Map<XClass, InheritanceState> inheritanceStatePerClass) {
public void setInheritanceStatePerClass(Map<ClassDetails, InheritanceState> inheritanceStatePerClass) {
this.inheritanceStatePerClass = inheritanceStatePerClass;
}
@ -258,7 +259,7 @@ public class PropertyBinder {
LOG.debugf( "MetadataSourceProcessor property %s with lazy=%s", name, lazy );
final String containerClassName = holder.getClassName();
holder.startingProperty( property );
holder.startingProperty( memberDetails );
basicValueBinder = new BasicValueBinder( BasicValueBinder.Kind.ATTRIBUTE, buildingContext );
basicValueBinder.setPropertyName( name );
@ -266,10 +267,10 @@ public class PropertyBinder {
basicValueBinder.setColumns( columns );
basicValueBinder.setPersistentClassName( containerClassName );
basicValueBinder.setType(
property,
returnedClass,
memberDetails,
memberDetails.getType(),
containerClassName,
holder.resolveAttributeConverterDescriptor( property )
holder.resolveAttributeConverterDescriptor( memberDetails )
);
basicValueBinder.setReferencedEntityName( referencedEntityName );
basicValueBinder.setAccessType( accessType );
@ -281,19 +282,17 @@ public class PropertyBinder {
@SuppressWarnings({"rawtypes", "unchecked"})
private void callAttributeBinders(Property property, Map<String, PersistentClass> persistentClasses) {
for ( Annotation containingAnnotation : findContainingAnnotations( this.property, AttributeBinderType.class ) ) {
final AttributeBinderType binderType =
containingAnnotation.annotationType().getAnnotation( AttributeBinderType.class );
for ( AnnotationUsage<?> binderAnnotationUsage : memberDetails.getMetaAnnotated( AttributeBinderType.class ) ) {
final AttributeBinderType binderType = binderAnnotationUsage.getAnnotationType().getAnnotation( AttributeBinderType.class );
try {
final AttributeBinder binder = binderType.binder().newInstance();
final PersistentClass persistentClass =
entityBinder != null
? entityBinder.getPersistentClass()
: persistentClasses.get( holder.getEntityName() );
binder.bind( containingAnnotation, buildingContext, persistentClass, property );
final AttributeBinder binder = binderType.binder().getConstructor().newInstance();
final PersistentClass persistentClass = entityBinder != null
? entityBinder.getPersistentClass()
: persistentClasses.get( holder.getEntityName() );
binder.bind( binderAnnotationUsage.toAnnotation(), buildingContext, persistentClass, property );
}
catch ( Exception e ) {
throw new AnnotationException( "error processing @AttributeBinderType annotation '" + containingAnnotation + "'", e );
throw new AnnotationException( "error processing @AttributeBinderType annotation '" + binderAnnotationUsage.getAnnotationType() + "'", e );
}
}
}
@ -317,7 +316,7 @@ public class PropertyBinder {
bindId( property );
}
else {
holder.addProperty( property, columns, declaringClass );
holder.addProperty( property, memberDetails, columns, declaringClass );
}
callAttributeBindersInSecondPass( property );
@ -359,15 +358,12 @@ public class PropertyBinder {
}
private void setDeclaredIdentifier(RootClass rootClass, MappedSuperclass superclass, Property prop) {
handleGenericComponentProperty( prop, buildingContext );
handleGenericComponentProperty( prop, memberDetails, buildingContext );
if ( superclass == null ) {
rootClass.setDeclaredIdentifierProperty( prop );
}
else {
final Class<?> type =
buildingContext.getBootstrapContext().getReflectionManager()
.toClass( declaringClass );
prepareActualProperty( prop, type, false, buildingContext,
prepareActualProperty( prop, memberDetails, false, buildingContext,
superclass::setDeclaredIdentifierProperty );
}
}
@ -380,7 +376,7 @@ public class PropertyBinder {
new PropertyPreloadedData(),
true,
false,
resolveCustomInstantiator( property, returnedClass ),
resolveCustomInstantiator( memberDetails, returnedClass ),
buildingContext
);
rootClass.setIdentifier( identifier );
@ -394,12 +390,16 @@ public class PropertyBinder {
}
}
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(XProperty property, XClass embeddableClass) {
if ( property.isAnnotationPresent( org.hibernate.annotations.EmbeddableInstantiator.class ) ) {
return property.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class ).value();
private Class<? extends EmbeddableInstantiator> resolveCustomInstantiator(MemberDetails property, ClassDetails embeddableClass) {
if ( property.hasAnnotationUsage( org.hibernate.annotations.EmbeddableInstantiator.class ) ) {
final AnnotationUsage<org.hibernate.annotations.EmbeddableInstantiator> annotationUsage = property.getAnnotationUsage(
org.hibernate.annotations.EmbeddableInstantiator.class );
return annotationUsage.getClassDetails( "value" ).toJavaClass();
}
else if ( embeddableClass.isAnnotationPresent( org.hibernate.annotations.EmbeddableInstantiator.class ) ) {
return embeddableClass.getAnnotation( org.hibernate.annotations.EmbeddableInstantiator.class ).value();
else if ( embeddableClass.hasAnnotationUsage( org.hibernate.annotations.EmbeddableInstantiator.class ) ) {
final AnnotationUsage<org.hibernate.annotations.EmbeddableInstantiator> annotationUsage = embeddableClass.getAnnotationUsage(
org.hibernate.annotations.EmbeddableInstantiator.class );
return annotationUsage.getClassDetails( "value" ).toJavaClass();
}
else {
return null;
@ -430,22 +430,22 @@ public class PropertyBinder {
}
private void handleValueGeneration(Property property) {
if ( this.property != null ) {
property.setValueGeneratorCreator( getValueGenerationFromAnnotations( this.property ) );
if ( this.memberDetails != null ) {
property.setValueGeneratorCreator( getValueGenerationFromAnnotations( this.memberDetails ) );
}
}
/**
* Returns the value generation strategy for the given property, if any.
*/
private GeneratorCreator getValueGenerationFromAnnotations(XProperty property) {
private GeneratorCreator getValueGenerationFromAnnotations(MemberDetails property) {
GeneratorCreator creator = null;
for ( Annotation annotation : property.getAnnotations() ) {
final GeneratorCreator candidate = generatorCreator( property, annotation );
for ( AnnotationUsage<?> usage : property.getAllAnnotationUsages() ) {
final GeneratorCreator candidate = generatorCreator( property, usage, buildingContext );
if ( candidate != null ) {
if ( creator != null ) {
throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
+ "' has multiple '@ValueGenerationType' annotations" );
+ "' has multiple '@ValueGenerationType' annotations" );
}
else {
creator = candidate;
@ -456,14 +456,14 @@ public class PropertyBinder {
}
private void handleLob(Property property) {
if ( this.property != null ) {
if ( this.memberDetails != null ) {
// HHH-4635 -- needed for dialect-specific property ordering
property.setLob( this.property.isAnnotationPresent( Lob.class ) );
property.setLob( this.memberDetails.hasAnnotationUsage( Lob.class ) );
}
}
private void handleMutability(Property property) {
if ( this.property != null && this.property.isAnnotationPresent( Immutable.class ) ) {
if ( this.memberDetails != null && this.memberDetails.hasAnnotationUsage( Immutable.class ) ) {
updatable = false;
}
property.setInsertable( insertable );
@ -471,8 +471,8 @@ public class PropertyBinder {
}
private void handleOptional(Property property) {
if ( this.property != null ) {
property.setOptional( !isId && isOptional( this.property, this.holder ) );
if ( this.memberDetails != null ) {
property.setOptional( !isId && isOptional( this.memberDetails, this.holder ) );
if ( property.isOptional() ) {
final OptionalDeterminationSecondPass secondPass = persistentClasses -> {
// Defer determining whether a property and its columns are nullable,
@ -501,15 +501,15 @@ public class PropertyBinder {
}
private void handleNaturalId(Property property) {
if ( this.property != null && entityBinder != null ) {
final NaturalId naturalId = this.property.getAnnotation( NaturalId.class );
if ( this.memberDetails != null && entityBinder != null ) {
final AnnotationUsage<NaturalId> naturalId = this.memberDetails.getAnnotationUsage( NaturalId.class );
if ( naturalId != null ) {
if ( !entityBinder.isRootEntity() ) {
throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
+ "' belongs to an entity subclass and may not be annotated '@NaturalId'" +
" (only a property of a root '@Entity' or a '@MappedSuperclass' may be a '@NaturalId')" );
}
if ( !naturalId.mutable() ) {
if ( !naturalId.getBoolean( "mutable" ) ) {
updatable = false;
}
property.setNaturalIdentifier( true );
@ -522,27 +522,28 @@ public class PropertyBinder {
if ( value instanceof Collection ) {
property.setOptimisticLocked( ((Collection) value).isOptimisticLocked() );
}
else if ( this.property != null && this.property.isAnnotationPresent( OptimisticLock.class ) ) {
final OptimisticLock optimisticLock = this.property.getAnnotation( OptimisticLock.class );
validateOptimisticLock( optimisticLock );
property.setOptimisticLocked( !optimisticLock.excluded() );
else if ( this.memberDetails != null && this.memberDetails.hasAnnotationUsage( OptimisticLock.class ) ) {
final AnnotationUsage<OptimisticLock> optimisticLock = this.memberDetails.getAnnotationUsage( OptimisticLock.class );
final boolean excluded = optimisticLock.getBoolean( "excluded" );
validateOptimisticLock( excluded );
property.setOptimisticLocked( !excluded );
}
else {
property.setOptimisticLocked( !isToOneValue(value) || insertable ); // && updatable as well???
}
}
private void validateOptimisticLock(OptimisticLock optimisticLock) {
if ( optimisticLock.excluded() ) {
if ( property.isAnnotationPresent( Version.class ) ) {
private void validateOptimisticLock(boolean excluded) {
if ( excluded ) {
if ( memberDetails.hasAnnotationUsage( Version.class ) ) {
throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
+ "' is annotated '@OptimisticLock(excluded=true)' and '@Version'" );
}
if ( property.isAnnotationPresent( Id.class ) ) {
if ( memberDetails.hasAnnotationUsage( Id.class ) ) {
throw new AnnotationException("Property '" + qualify( holder.getPath(), name )
+ "' is annotated '@OptimisticLock(excluded=true)' and '@Id'" );
}
if ( property.isAnnotationPresent( EmbeddedId.class ) ) {
if ( memberDetails.hasAnnotationUsage( EmbeddedId.class ) ) {
throw new AnnotationException( "Property '" + qualify( holder.getPath(), name )
+ "' is annotated '@OptimisticLock(excluded=true)' and '@EmbeddedId'" );
}
@ -561,7 +562,7 @@ public class PropertyBinder {
PropertyContainer propertyContainer,
MetadataBuildingContext context) {
int idPropertyCounter = 0;
for ( XProperty property : propertyContainer.propertyIterator() ) {
for ( MemberDetails property : propertyContainer.propertyIterator() ) {
idPropertyCounter += addProperty( propertyContainer, property, elements, context );
}
return idPropertyCounter;
@ -569,11 +570,11 @@ public class PropertyBinder {
private static int addProperty(
PropertyContainer propertyContainer,
XProperty property,
MemberDetails property,
List<PropertyData> inFlightPropertyDataList,
MetadataBuildingContext context) {
// see if inFlightPropertyDataList already contains a PropertyData for this name,
// and if so, skip it..
// and if so, skip it...
for ( PropertyData propertyData : inFlightPropertyDataList ) {
if ( propertyData.getPropertyName().equals( property.getName() ) ) {
checkIdProperty( property, propertyData );
@ -582,19 +583,19 @@ public class PropertyBinder {
}
}
final XClass declaringClass = propertyContainer.getDeclaringClass();
final XClass entity = propertyContainer.getEntityAtStake();
final ClassDetails declaringClass = propertyContainer.getDeclaringClass();
final ClassDetails entity = propertyContainer.getEntityAtStake();
int idPropertyCounter = 0;
final PropertyData propertyAnnotatedElement = new PropertyInferredData(
declaringClass,
property,
propertyContainer.getClassLevelAccessType().getType(),
context.getBootstrapContext().getReflectionManager()
context
);
// put element annotated by @Id in front, since it has to be parsed
// before any association by Hibernate
final XAnnotatedElement element = propertyAnnotatedElement.getProperty();
final MemberDetails element = propertyAnnotatedElement.getAttributeMember();
if ( hasIdAnnotation( element ) ) {
inFlightPropertyDataList.add( 0, propertyAnnotatedElement );
handleIdProperty( propertyContainer, context, declaringClass, entity, element );
@ -606,23 +607,23 @@ public class PropertyBinder {
else {
inFlightPropertyDataList.add( propertyAnnotatedElement );
}
if ( element.isAnnotationPresent( MapsId.class ) ) {
if ( element.hasAnnotationUsage( MapsId.class ) ) {
context.getMetadataCollector().addPropertyAnnotatedWithMapsId( entity, propertyAnnotatedElement );
}
return idPropertyCounter;
}
private static void checkIdProperty(XProperty property, PropertyData propertyData) {
final Id incomingIdProperty = property.getAnnotation( Id.class );
final Id existingIdProperty = propertyData.getProperty().getAnnotation( Id.class );
private static void checkIdProperty(MemberDetails property, PropertyData propertyData) {
final AnnotationUsage<Id> incomingIdProperty = property.getAnnotationUsage( Id.class );
final AnnotationUsage<Id> existingIdProperty = propertyData.getAttributeMember().getAnnotationUsage( Id.class );
if ( incomingIdProperty != null && existingIdProperty == null ) {
throw new MappingException(
String.format(
"You cannot override the [%s] non-identifier property from the [%s] base class or @MappedSuperclass and make it an identifier in the [%s] subclass",
propertyData.getProperty().getName(),
propertyData.getProperty().getDeclaringClass().getName(),
property.getDeclaringClass().getName()
propertyData.getAttributeMember().getName(),
propertyData.getAttributeMember().getDeclaringType().getName(),
property.getDeclaringType().getName()
)
);
}
@ -631,58 +632,53 @@ public class PropertyBinder {
private static void handleIdProperty(
PropertyContainer propertyContainer,
MetadataBuildingContext context,
XClass declaringClass,
XClass entity,
XAnnotatedElement element) {
ClassDetails declaringClass,
ClassDetails entity,
MemberDetails element) {
// The property must be put in hibernate.properties as it's a system wide property. Fixable?
//TODO support true/false/default on the property instead of present / not present
//TODO is @Column mandatory?
//TODO add method support
if ( context.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) {
if ( element.isAnnotationPresent( Id.class ) && element.isAnnotationPresent( Column.class ) ) {
final String columnName = element.getAnnotation( Column.class ).name();
for ( XProperty property : declaringClass.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
if ( !property.isAnnotationPresent( MapsId.class ) && isJoinColumnPresent( columnName, property ) ) {
if ( element.hasAnnotationUsage( Id.class ) && element.hasAnnotationUsage( Column.class ) ) {
final String columnName = element.getAnnotationUsage( Column.class ).getString( "name" );
declaringClass.forEachField( (index, fieldDetails) -> {
if ( !element.hasAnnotationUsage( MapsId.class ) && isJoinColumnPresent( columnName, element ) ) {
//create a PropertyData for the specJ property holding the mapping
context.getMetadataCollector().addPropertyAnnotatedWithMapsIdSpecj(
entity,
new PropertyInferredData(
declaringClass,
//same dec
property,
element,
// the actual @XToOne property
propertyContainer.getClassLevelAccessType().getType(),
//TODO we should get the right accessor but the same as id would do
context.getBootstrapContext().getReflectionManager()
context
),
element.toString()
);
}
}
} );
}
}
}
private static boolean isJoinColumnPresent(String columnName, XProperty property) {
private static boolean isJoinColumnPresent(String columnName, MemberDetails property) {
//The detection of a configured individual JoinColumn differs between Annotation
//and XML configuration processing.
if ( property.isAnnotationPresent( JoinColumn.class )
&& property.getAnnotation( JoinColumn.class ).name().equals( columnName ) ) {
return true;
}
else if ( property.isAnnotationPresent( JoinColumns.class ) ) {
for ( JoinColumn columnAnnotation : property.getAnnotation( JoinColumns.class ).value() ) {
if ( columnName.equals( columnAnnotation.name() ) ) {
return true;
}
final List<AnnotationUsage<JoinColumn>> joinColumnAnnotations = property.getRepeatedAnnotationUsages( JoinColumn.class );
for ( AnnotationUsage<JoinColumn> joinColumnAnnotation : joinColumnAnnotations ) {
if ( joinColumnAnnotation.getString( "name" ).equals( columnName ) ) {
return true;
}
}
return false;
}
static boolean hasIdAnnotation(XAnnotatedElement element) {
return element.isAnnotationPresent( Id.class )
|| element.isAnnotationPresent( EmbeddedId.class );
static boolean hasIdAnnotation(MemberDetails element) {
return element.hasAnnotationUsage( Id.class )
|| element.hasAnnotationUsage( EmbeddedId.class );
}
/**
@ -698,7 +694,7 @@ public class PropertyBinder {
boolean isComponentEmbedded,
boolean inSecondPass,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) throws MappingException {
Map<ClassDetails, InheritanceState> inheritanceStatePerClass) throws MappingException {
if ( alreadyProcessedBySuper( propertyHolder, inferredData, entityBinder ) ) {
LOG.debugf(
@ -720,8 +716,8 @@ public class PropertyBinder {
);
}
final XProperty property = inferredData.getProperty();
if ( property.isAnnotationPresent( Parent.class ) ) {
final MemberDetails property = inferredData.getAttributeMember();
if ( property.hasAnnotationUsage( Parent.class ) ) {
handleParentProperty( propertyHolder, inferredData, property );
}
else {
@ -737,8 +733,7 @@ public class PropertyBinder {
inSecondPass,
context,
inheritanceStatePerClass,
property,
inferredData.getClassOrElement()
property
);
}
}
@ -749,7 +744,7 @@ public class PropertyBinder {
&& binder.isPropertyDefinedInSuperHierarchy( data.getPropertyName() );
}
private static void handleParentProperty(PropertyHolder holder, PropertyData data, XProperty property) {
private static void handleParentProperty(PropertyHolder holder, PropertyData data, MemberDetails property) {
if ( holder.isComponent() ) {
holder.setParentProperty( property.getName() );
}
@ -769,10 +764,12 @@ public class PropertyBinder {
boolean isComponentEmbedded,
boolean inSecondPass,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
XProperty property,
XClass returnedClass) {
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MemberDetails property) {
final TypeDetails attributeTypeDetails = inferredData.getAttributeMember().isPlural()
? inferredData.getAttributeMember().getType()
: inferredData.getClassOrElementType();
final ClassDetails attributeClassDetails = attributeTypeDetails.determineRawClass();
final ColumnsBuilder columnsBuilder = new ColumnsBuilder(
propertyHolder,
nullability,
@ -787,8 +784,8 @@ public class PropertyBinder {
propertyBinder.setReturnedClassName( inferredData.getTypeName() );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setHolder( propertyHolder );
propertyBinder.setProperty( property );
propertyBinder.setReturnedClass( inferredData.getPropertyClass() );
propertyBinder.setMemberDetails( property );
propertyBinder.setReturnedClass( attributeClassDetails );
propertyBinder.setBuildingContext( context );
if ( isIdentifierMapper ) {
propertyBinder.setInsertable( false );
@ -799,9 +796,9 @@ public class PropertyBinder {
propertyBinder.setInheritanceStatePerClass( inheritanceStatePerClass );
propertyBinder.setId( !entityBinder.isIgnoreIdAnnotations() && hasIdAnnotation( property ) );
final LazyGroup lazyGroupAnnotation = property.getAnnotation( LazyGroup.class );
final AnnotationUsage<LazyGroup> lazyGroupAnnotation = property.getAnnotationUsage( LazyGroup.class );
if ( lazyGroupAnnotation != null ) {
propertyBinder.setLazyGroup( lazyGroupAnnotation.value() );
propertyBinder.setLazyGroup( lazyGroupAnnotation.getString( "value" ) );
}
final AnnotatedJoinColumns joinColumns = columnsBuilder.getJoinColumns();
@ -817,7 +814,7 @@ public class PropertyBinder {
context,
inheritanceStatePerClass,
property,
returnedClass,
attributeClassDetails,
columnsBuilder,
propertyBinder
);
@ -835,9 +832,9 @@ public class PropertyBinder {
boolean isComponentEmbedded,
boolean inSecondPass,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
XProperty property,
XClass returnedClass,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MemberDetails property,
ClassDetails returnedClass,
ColumnsBuilder columnsBuilder,
PropertyBinder propertyBinder) {
if ( isVersion( property ) ) {
@ -927,32 +924,32 @@ public class PropertyBinder {
return columnsBuilder.getColumns();
}
private static boolean isVersion(XProperty property) {
return property.isAnnotationPresent( Version.class );
private static boolean isVersion(MemberDetails property) {
return property.hasAnnotationUsage( Version.class );
}
private static boolean isOneToOne(XProperty property) {
return property.isAnnotationPresent( OneToOne.class );
private static boolean isOneToOne(MemberDetails property) {
return property.hasAnnotationUsage( OneToOne.class );
}
private static boolean isManyToOne(XProperty property) {
return property.isAnnotationPresent( ManyToOne.class );
private static boolean isManyToOne(MemberDetails property) {
return property.hasAnnotationUsage( ManyToOne.class );
}
private static boolean isAny(XProperty property) {
return property.isAnnotationPresent( Any.class );
private static boolean isAny(MemberDetails property) {
return property.hasAnnotationUsage( Any.class );
}
private static boolean isCollection(XProperty property) {
return property.isAnnotationPresent( OneToMany.class )
|| property.isAnnotationPresent( ManyToMany.class )
|| property.isAnnotationPresent( ElementCollection.class )
|| property.isAnnotationPresent( ManyToAny.class );
private static boolean isCollection(MemberDetails property) {
return property.hasAnnotationUsage( OneToMany.class )
|| property.hasAnnotationUsage( ManyToMany.class )
|| property.hasAnnotationUsage( ElementCollection.class )
|| property.hasAnnotationUsage( ManyToAny.class );
}
private static boolean isForcePersist(XProperty property) {
return property.isAnnotationPresent( MapsId.class )
|| property.isAnnotationPresent( Id.class );
private static boolean isForcePersist(MemberDetails property) {
return property.hasAnnotationUsage( MapsId.class )
|| property.hasAnnotationUsage( Id.class );
}
private static void bindVersionProperty(
@ -960,7 +957,7 @@ public class PropertyBinder {
PropertyData inferredData,
boolean isIdentifierMapper,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
AnnotatedColumns columns,
PropertyBinder propertyBinder) {
checkVersionProperty( propertyHolder, isIdentifierMapper );
@ -974,7 +971,7 @@ public class PropertyBinder {
rootClass.setVersion( property );
//If version is on a mapped superclass, update the mapping
final XClass declaringClass = inferredData.getDeclaringClass();
final ClassDetails declaringClass = inferredData.getDeclaringClass();
final org.hibernate.mapping.MappedSuperclass superclass =
getMappedSuperclassOrNull( declaringClass, inheritanceStatePerClass, context );
if ( superclass != null ) {
@ -1022,11 +1019,11 @@ public class PropertyBinder {
boolean isIdentifierMapper,
boolean isComponentEmbedded,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
XProperty property,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MemberDetails property,
ColumnsBuilder columnsBuilder,
AnnotatedColumns columns,
XClass returnedClass,
ClassDetails returnedClass,
PropertyBinder propertyBinder) {
// overrides from @MapsId or @IdClass if needed
@ -1066,8 +1063,8 @@ public class PropertyBinder {
resolveCompositeUserType( inferredData, context );
if ( isComposite || compositeUserType != null ) {
if ( property.isArray() && property.getElementClass() != null
&& isEmbedded( property, property.getElementClass() ) ) {
if ( property.isArray() && property.getElementType() != null
&& isEmbedded( property, property.getElementType() ) ) {
// This is a special kind of basic aggregate component array type
propertyBinder.setComponentElement(
EmbeddableBinder.bindEmbeddable(
@ -1109,8 +1106,9 @@ public class PropertyBinder {
);
}
}
else if ( property.isCollection() && property.getElementClass() != null
&& isEmbedded( property, property.getElementClass() ) ) {
else if ( property.isPlural()
&& property.getElementType() != null
&& isEmbedded( property, property.getElementType() ) ) {
// This is a special kind of basic aggregate component array type
propertyBinder.setComponentElement(
EmbeddableBinder.bindEmbeddable(
@ -1125,7 +1123,7 @@ public class PropertyBinder {
inheritanceStatePerClass,
null,
null,
EmbeddableBinder.determineCustomInstantiator( property, property.getElementClass(), context ),
EmbeddableBinder.determineCustomInstantiator( property, property.getElementType().determineRawClass(), context ),
compositeUserType,
null,
columns
@ -1170,11 +1168,11 @@ public class PropertyBinder {
}
private static boolean isComposite(
Map<XClass, InheritanceState> inheritanceStatePerClass,
XProperty property,
XClass returnedClass,
Map<ClassDetails, InheritanceState> inheritanceStatePerClass,
MemberDetails property,
ClassDetails returnedClass,
PropertyData overridingProperty) {
final InheritanceState state = inheritanceStatePerClass.get( overridingProperty.getClassOrElement() );
final InheritanceState state = inheritanceStatePerClass.get( overridingProperty.getClassOrElementType().determineRawClass() );
return state != null ? state.hasIdClassOrEmbeddedId() : isEmbedded( property, returnedClass );
}
@ -1182,7 +1180,7 @@ public class PropertyBinder {
PropertyHolder propertyHolder,
Map<String, IdentifierGeneratorDefinition> classGenerators,
MetadataBuildingContext context,
XProperty property,
MemberDetails property,
PropertyBinder propertyBinder) {
final PropertyData mapsIdProperty = getPropertyOverriddenByMapperOrMapsId(
propertyBinder.isId(),
@ -1221,11 +1219,10 @@ public class PropertyBinder {
PropertyData inferredData,
Nullability nullability,
MetadataBuildingContext context,
XProperty property,
MemberDetails property,
AnnotatedColumns columns,
PropertyBinder propertyBinder,
boolean isOverridden) {
if ( shouldForceNotNull( nullability, propertyBinder, isExplicitlyOptional( property ) ) ) {
forceColumnsNotNull( propertyHolder, inferredData, columns, propertyBinder );
}
@ -1267,47 +1264,69 @@ public class PropertyBinder {
/**
* Should this property be considered optional, without considering
* whether it is primitive?
*
* @apiNote Poorly named to a degree. The intention is really whether non-optional is explicit
*/
private static boolean isExplicitlyOptional(XProperty property) {
return !property.isAnnotationPresent( Basic.class )
|| property.getAnnotation( Basic.class ).optional();
private static boolean isExplicitlyOptional(MemberDetails attributeMember) {
final AnnotationUsage<Basic> basicAnn = attributeMember.getAnnotationUsage( Basic.class );
if ( basicAnn == null ) {
// things are optional (nullable) by default. If there is no annotation, that cannot be altered
return true;
}
return basicAnn.getBoolean( "optional" );
}
/**
* Should this property be considered optional, taking into
* account whether it is primitive?
*/
public static boolean isOptional(XProperty property, PropertyHolder propertyHolder) {
return property.isAnnotationPresent( Basic.class )
? property.getAnnotation( Basic.class ).optional()
: property.isArray()
|| propertyHolder != null && propertyHolder.isComponent()
|| !property.getClassOrElementClass().isPrimitive();
public static boolean isOptional(MemberDetails attributeMember, PropertyHolder propertyHolder) {
final AnnotationUsage<Basic> basicAnn = attributeMember.getAnnotationUsage( Basic.class );
if ( basicAnn != null ) {
return basicAnn.getBoolean( "optional" );
}
if ( attributeMember.isArray() ) {
return true;
}
if ( propertyHolder != null && propertyHolder.isComponent() ) {
return true;
}
if ( attributeMember.isPlural() ) {
return attributeMember.getElementType().getTypeKind() != TypeDetails.Kind.PRIMITIVE;
}
return attributeMember.getType().getTypeKind() != TypeDetails.Kind.PRIMITIVE;
}
private static boolean isLazy(XProperty property) {
return property.isAnnotationPresent( Basic.class )
&& property.getAnnotation( Basic.class ).fetch() == LAZY;
private static boolean isLazy(MemberDetails property) {
final AnnotationUsage<Basic> annotationUsage = property.getAnnotationUsage( Basic.class );
return annotationUsage != null && annotationUsage.getEnum( "fetch" ) == LAZY;
}
private static void addIndexes(
boolean inSecondPass,
XProperty property,
MemberDetails property,
AnnotatedColumns columns,
AnnotatedJoinColumns joinColumns) {
//process indexes after everything: in second pass, many to one has to be done before indexes
final Index index = property.getAnnotation( Index.class );
if ( index != null ) {
if ( joinColumns != null ) {
for ( AnnotatedColumn column : joinColumns.getColumns() ) {
column.addIndex( index, inSecondPass);
}
final AnnotationUsage<Index> index = property.getAnnotationUsage( Index.class );
if ( index == null ) {
return;
}
if ( joinColumns != null ) {
for ( AnnotatedColumn column : joinColumns.getColumns() ) {
column.addIndex( index, inSecondPass );
}
else {
if ( columns != null ) {
for ( AnnotatedColumn column : columns.getColumns() ) {
column.addIndex( index, inSecondPass );
}
}
else {
if ( columns != null ) {
for ( AnnotatedColumn column : columns.getColumns() ) {
column.addIndex( index, inSecondPass );
}
}
}
@ -1315,7 +1334,7 @@ public class PropertyBinder {
private static void addNaturalIds(
boolean inSecondPass,
XProperty property,
MemberDetails property,
AnnotatedColumns columns,
AnnotatedJoinColumns joinColumns,
MetadataBuildingContext context) {
@ -1323,12 +1342,13 @@ public class PropertyBinder {
// For now, simply ensure consistent naming.
// TODO: AFAIK, there really isn't a reason for these UKs to be created
// on the SecondPass. This whole area should go away...
final NaturalId naturalId = property.getAnnotation( NaturalId.class );
final AnnotationUsage<NaturalId> naturalId = property.getAnnotationUsage( NaturalId.class );
if ( naturalId != null ) {
final Database database = context.getMetadataCollector().getDatabase();
final ImplicitNamingStrategy implicitNamingStrategy = context.getBuildingOptions().getImplicitNamingStrategy();
if ( joinColumns != null ) {
final Identifier name = context.getBuildingOptions().getImplicitNamingStrategy()
.determineUniqueKeyName(new ImplicitUniqueKeyNameSource() {
final Identifier name =
implicitNamingStrategy.determineUniqueKeyName( new ImplicitUniqueKeyNameSource() {
@Override
public Identifier getTableName() {
return joinColumns.getTable().getNameIdentifier();
@ -1355,8 +1375,8 @@ public class PropertyBinder {
}
}
else {
final Identifier name = context.getBuildingOptions().getImplicitNamingStrategy()
.determineUniqueKeyName(new ImplicitUniqueKeyNameSource() {
final Identifier name =
implicitNamingStrategy.determineUniqueKeyName(new ImplicitUniqueKeyNameSource() {
@Override
public Identifier getTableName() {
return columns.getTable().getNameIdentifier();
@ -1388,25 +1408,23 @@ public class PropertyBinder {
private static Class<? extends CompositeUserType<?>> resolveCompositeUserType(
PropertyData inferredData,
MetadataBuildingContext context) {
final XProperty property = inferredData.getProperty();
final XClass returnedClass = inferredData.getClassOrElement();
final MemberDetails attributeMember = inferredData.getAttributeMember();
final TypeDetails classOrElementType = inferredData.getClassOrElementType();
final ClassDetails returnedClass = classOrElementType.determineRawClass();
if ( property != null ) {
final CompositeType compositeType = findAnnotation( property, CompositeType.class );
if ( attributeMember != null ) {
final AnnotationUsage<CompositeType> compositeType = attributeMember.locateAnnotationUsage( CompositeType.class );
if ( compositeType != null ) {
return compositeType.value();
return compositeType.getClassDetails( "value" ).toJavaClass();
}
final Class<? extends CompositeUserType<?>> compositeUserType =
resolveTimeZoneStorageCompositeUserType( property, returnedClass, context );
final Class<? extends CompositeUserType<?>> compositeUserType = resolveTimeZoneStorageCompositeUserType( attributeMember, returnedClass, context );
if ( compositeUserType != null ) {
return compositeUserType;
}
}
if ( returnedClass != null ) {
final Class<?> embeddableClass = context.getBootstrapContext()
.getReflectionManager()
.toClass( returnedClass );
final Class<?> embeddableClass = returnedClass.toJavaClass();
if ( embeddableClass != null ) {
return context.getMetadataCollector().findRegisteredCompositeUserType( embeddableClass );
}
@ -1426,36 +1444,36 @@ public class PropertyBinder {
throw new AnnotationException( "Property '"+ getPath( propertyHolder, inferredData )
+ "' belongs to an '@IdClass' and may not be annotated '@Id' or '@EmbeddedId'" );
}
final XProperty idProperty = inferredData.getProperty();
final List<Annotation> idGeneratorAnnotations = findContainingAnnotations( idProperty, IdGeneratorType.class );
final List<Annotation> generatorAnnotations = findContainingAnnotations( idProperty, ValueGenerationType.class );
final MemberDetails idAttributeMember = inferredData.getAttributeMember();
final List<AnnotationUsage<? extends Annotation>> idGeneratorAnnotations = idAttributeMember.getMetaAnnotated( IdGeneratorType.class );
final List<AnnotationUsage<? extends Annotation>> generatorAnnotations = idAttributeMember.getMetaAnnotated( ValueGenerationType.class );
removeIdGenerators( generatorAnnotations, idGeneratorAnnotations );
if ( idGeneratorAnnotations.size() + generatorAnnotations.size() > 1 ) {
throw new AnnotationException( "Property '"+ getPath( propertyHolder, inferredData )
+ "' has too many generator annotations " + combine( idGeneratorAnnotations, generatorAnnotations ) );
}
if ( !idGeneratorAnnotations.isEmpty() ) {
final Annotation annotation = idGeneratorAnnotations.get(0);
final AnnotationUsage annotation = idGeneratorAnnotations.get(0);
final ServiceRegistry serviceRegistry = context.getBootstrapContext().getServiceRegistry();
final BeanContainer beanContainer =
allowExtensionsInCdi( serviceRegistry )
? serviceRegistry.requireService( ManagedBeanRegistry.class ).getBeanContainer()
: null;
idValue.setCustomIdGeneratorCreator( identifierGeneratorCreator( idProperty, annotation, beanContainer ) );
idValue.setCustomIdGeneratorCreator( identifierGeneratorCreator( idAttributeMember, annotation, beanContainer ) );
}
else if ( !generatorAnnotations.isEmpty() ) {
// idValue.setCustomGeneratorCreator( generatorCreator( idProperty, generatorAnnotation ) );
// idValue.setCustomGeneratorCreator( generatorCreator( idAttributeMember, generatorAnnotation ) );
throw new AnnotationException( "Property '"+ getPath( propertyHolder, inferredData )
+ "' is annotated '" + generatorAnnotations.get(0).annotationType()
+ "' is annotated '" + generatorAnnotations.get(0).getAnnotationType()
+ "' which is not an '@IdGeneratorType'" );
}
else {
final XClass entityClass = inferredData.getClassOrElement();
createIdGenerator( idValue, classGenerators, context, entityClass, idProperty );
final ClassDetails entityClass = inferredData.getClassOrElementType().determineRawClass();
createIdGenerator( idValue, classGenerators, context, entityClass, idAttributeMember );
if ( LOG.isTraceEnabled() ) {
LOG.tracev(
"Bind {0} on {1}",
isCompositeId( entityClass, idProperty ) ? "@EmbeddedId" : "@Id",
isCompositeId( entityClass, idAttributeMember ) ? "@EmbeddedId" : "@Id",
inferredData.getPropertyName()
);
}
@ -1466,9 +1484,18 @@ public class PropertyBinder {
// collection methods as those proxies do not implement hashcode/equals and even a simple `a.equals(a)` will return `false`.
// Instead, we will check the annotation types, since generator annotations should not be "repeatable" we should have only
// at most one annotation for a generator:
private static void removeIdGenerators(List<Annotation> generatorAnnotations, List<Annotation> idGeneratorAnnotations) {
for ( Annotation id : idGeneratorAnnotations ) {
generatorAnnotations.removeIf( gen -> gen.annotationType().equals( id.annotationType() ) );
// todo (jpa32) : is this still necessary with s/hibernate-common-annotations/hibernate-models?
private static void removeIdGenerators(
List<AnnotationUsage<? extends Annotation>> generatorAnnotations,
List<AnnotationUsage<? extends Annotation>> idGeneratorAnnotations) {
for ( AnnotationUsage<? extends Annotation> id : idGeneratorAnnotations ) {
final Iterator<AnnotationUsage<? extends Annotation>> iterator = generatorAnnotations.iterator();
while ( iterator.hasNext() ) {
final AnnotationUsage<? extends Annotation> gen = iterator.next();
if ( gen.getAnnotationType().equals( id.getAnnotationType() ) ) {
iterator.remove();
}
}
}
}
}

View File

@ -11,27 +11,31 @@ package org.hibernate.boot.model.internal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.hibernate.AnnotationException;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.JavaType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.Target;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.MappingException;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
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 org.hibernate.models.spi.RecordComponentDetails;
import org.hibernate.models.spi.TypeDetails;
import org.jboss.logging.Logger;
@ -44,9 +48,11 @@ import jakarta.persistence.OneToOne;
import jakarta.persistence.Transient;
/**
* A helper class to keep the {@code XProperty}s of a class ordered by access type.
* Access to the members of a {@linkplain ClassDetails class} which define a persistent attribute
* as defined by the JPA specification and AccessType.
*
* @author Hardy Ferentschik
* @author Steve Ebersole
*/
public class PropertyContainer {
@ -55,8 +61,8 @@ public class PropertyContainer {
/**
* The class for which this container is created.
*/
private final XClass xClass;
private final XClass entityAtStake;
private final ClassDetails classDetails;
private final ClassDetails entityAtStake;
/**
* Holds the AccessType indicated for use at the class/container-level for cases where persistent attribute
@ -64,10 +70,20 @@ public class PropertyContainer {
*/
private final AccessType classLevelAccessType;
private final List<XProperty> persistentAttributes;
private final List<MemberDetails> attributeMembers;
public PropertyContainer(XClass clazz, XClass entityAtStake, AccessType defaultClassLevelAccessType) {
this.xClass = clazz;
public PropertyContainer(ClassDetails classDetails, TypeDetails entityAtStake, AccessType propertyAccessor) {
// todo : should use the TypeDetails, no?
this( classDetails, entityAtStake.determineRawClass(), propertyAccessor );
}
public PropertyContainer(TypeDetails classDetails, TypeDetails entityAtStake, AccessType propertyAccessor) {
// todo : should use the TypeDetails, no?
this( classDetails.determineRawClass(), entityAtStake.determineRawClass(), propertyAccessor );
}
public PropertyContainer(ClassDetails classDetails, ClassDetails entityAtStake, AccessType defaultClassLevelAccessType) {
this.classDetails = classDetails;
this.entityAtStake = entityAtStake;
if ( defaultClassLevelAccessType == AccessType.DEFAULT ) {
@ -85,29 +101,28 @@ public class PropertyContainer {
assert classLevelAccessType == AccessType.FIELD || classLevelAccessType == AccessType.PROPERTY
|| classLevelAccessType == AccessType.RECORD;
attributeMembers = resolveAttributeMembers( classDetails, classLevelAccessType );
}
final List<XProperty> fields = xClass.getDeclaredProperties( AccessType.FIELD.getType() );
final List<XProperty> getters = xClass.getDeclaredProperties( AccessType.PROPERTY.getType() );
final List<XProperty> recordComponents = xClass.getDeclaredProperties( AccessType.RECORD.getType() );
private static List<MemberDetails> resolveAttributeMembers(
ClassDetails classDetails,
AccessType classLevelAccessType) {
final List<FieldDetails> fields = collectPotentialAttributeMembers( classDetails.getFields() );
final List<MethodDetails> getters = collectPotentialAttributeMembers( classDetails.getMethods() );
final List<RecordComponentDetails> recordComponents = collectPotentialAttributeMembers( classDetails.getRecordComponents() );
preFilter( fields, getters, recordComponents );
final Map<String, MemberDetails> attributeMemberMap = buildAttributeMemberMap(
recordComponents,
fields,
getters
);
final Map<String,XProperty> persistentAttributesFromGetters = new HashMap<>();
final Map<String,XProperty> persistentAttributesFromComponents = new HashMap<>();
final Map<String,MethodDetails> persistentAttributesFromGetters = new HashMap<>();
final Map<String,RecordComponentDetails> persistentAttributesFromComponents = new HashMap<>();
final Map<String, XProperty> localAttributeMap;
// If the record class has only record components which match up with fields and no additional getters,
// we can retain the property order, to match up with the record component order
if ( !recordComponents.isEmpty() && recordComponents.size() == fields.size() && getters.isEmpty() ) {
localAttributeMap = new LinkedHashMap<>();
}
//otherwise we sort them in alphabetical order, since this is at least deterministic
else {
localAttributeMap = new TreeMap<>();
}
collectPersistentAttributesUsingLocalAccessType(
xClass,
localAttributeMap,
classDetails,
attributeMemberMap,
persistentAttributesFromGetters,
persistentAttributesFromComponents,
fields,
@ -115,125 +130,121 @@ public class PropertyContainer {
recordComponents
);
collectPersistentAttributesUsingClassLevelAccessType(
xClass,
classDetails,
classLevelAccessType,
localAttributeMap,
attributeMemberMap,
persistentAttributesFromGetters,
persistentAttributesFromComponents,
fields,
getters,
recordComponents
);
this.persistentAttributes = verifyAndInitializePersistentAttributes( xClass, localAttributeMap );
return verifyAndInitializePersistentAttributes( classDetails, attributeMemberMap );
}
private void preFilter(List<XProperty> fields, List<XProperty> getters, List<XProperty> recordComponents) {
Iterator<XProperty> propertyIterator = fields.iterator();
while ( propertyIterator.hasNext() ) {
final XProperty property = propertyIterator.next();
if ( mustBeSkipped( property ) ) {
propertyIterator.remove();
}
private static Map<String, MemberDetails> buildAttributeMemberMap(
List<RecordComponentDetails> recordComponents,
List<FieldDetails> fields,
List<MethodDetails> getters) {
final Map<String, MemberDetails> attributeMemberMap;
// If the record class has only record components which match up with fields and no additional getters,
// we can retain the property order, to match up with the record component order
if ( !recordComponents.isEmpty() && recordComponents.size() == fields.size() && getters.isEmpty() ) {
attributeMemberMap = new LinkedHashMap<>();
}
//otherwise we sort them in alphabetical order, since this is at least deterministic
else {
attributeMemberMap = new TreeMap<>();
}
return attributeMemberMap;
}
propertyIterator = getters.iterator();
while ( propertyIterator.hasNext() ) {
final XProperty property = propertyIterator.next();
if ( mustBeSkipped( property ) ) {
propertyIterator.remove();
}
}
propertyIterator = recordComponents.iterator();
while ( propertyIterator.hasNext() ) {
final XProperty property = propertyIterator.next();
if ( mustBeSkipped( property ) ) {
propertyIterator.remove();
private static <E extends MemberDetails> List<E> collectPotentialAttributeMembers(List<E> source) {
final List<E> results = new ArrayList<>();
for ( int i = 0; i < source.size(); i++ ) {
final E possible = source.get( i );
if ( possible.isPersistable() ) {
if ( !mustBeSkipped( possible ) ) {
results.add( possible );
}
}
}
return results;
}
private static void collectPersistentAttributesUsingLocalAccessType(
XClass xClass,
Map<String, XProperty> persistentAttributeMap,
Map<String,XProperty> persistentAttributesFromGetters,
Map<String,XProperty> persistentAttributesFromComponents,
List<XProperty> fields,
List<XProperty> getters,
List<XProperty> recordComponents) {
ClassDetails classDetails,
Map<String, MemberDetails> persistentAttributeMap,
Map<String,MethodDetails> persistentAttributesFromGetters,
Map<String,RecordComponentDetails> persistentAttributesFromComponents,
List<FieldDetails> fields,
List<MethodDetails> getters,
List<RecordComponentDetails> recordComponents) {
// Check fields...
Iterator<XProperty> propertyIterator = fields.iterator();
while ( propertyIterator.hasNext() ) {
final XProperty xProperty = propertyIterator.next();
final Access localAccessAnnotation = xProperty.getAnnotation( Access.class );
for ( int i = 0; i < fields.size(); i++ ) {
final FieldDetails fieldDetails = fields.get( i );
final AnnotationUsage<Access> localAccessAnnotation = fieldDetails.getAnnotationUsage( Access.class );
if ( localAccessAnnotation == null
|| localAccessAnnotation.value() != jakarta.persistence.AccessType.FIELD ) {
|| localAccessAnnotation.getEnum( "value" ) != jakarta.persistence.AccessType.FIELD ) {
continue;
}
propertyIterator.remove();
persistentAttributeMap.put( xProperty.getName(), xProperty );
persistentAttributeMap.put( fieldDetails.getName(), fieldDetails );
}
// Check getters...
propertyIterator = getters.iterator();
while ( propertyIterator.hasNext() ) {
final XProperty xProperty = propertyIterator.next();
final Access localAccessAnnotation = xProperty.getAnnotation( Access.class );
for ( int i = 0; i < getters.size(); i++ ) {
final MethodDetails getterDetails = getters.get( i );
final AnnotationUsage<Access> localAccessAnnotation = getterDetails.getAnnotationUsage( Access.class );
if ( localAccessAnnotation == null
|| localAccessAnnotation.value() != jakarta.persistence.AccessType.PROPERTY ) {
|| localAccessAnnotation.getEnum( "value" ) != jakarta.persistence.AccessType.PROPERTY ) {
continue;
}
propertyIterator.remove();
final String name = xProperty.getName();
final String name = getterDetails.resolveAttributeName();
// HHH-10242 detect registration of the same property getter twice - eg boolean isId() + UUID getId()
final XProperty previous = persistentAttributesFromGetters.get( name );
final MethodDetails previous = persistentAttributesFromGetters.get( name );
if ( previous != null ) {
throw new org.hibernate.boot.MappingException(
LOG.ambiguousPropertyMethods(
xClass.getName(),
HCANNHelper.annotatedElementSignature( previous ),
HCANNHelper.annotatedElementSignature( xProperty )
classDetails.getName(),
previous.getName(),
getterDetails.getName()
),
new Origin( SourceType.ANNOTATION, xClass.getName() )
new Origin( SourceType.ANNOTATION, classDetails.getName() )
);
}
persistentAttributeMap.put( name, xProperty );
persistentAttributesFromGetters.put( name, xProperty );
persistentAttributeMap.put( name, getterDetails );
persistentAttributesFromGetters.put( name, getterDetails );
}
// Check record components...
propertyIterator = recordComponents.iterator();
while ( propertyIterator.hasNext() ) {
final XProperty xProperty = propertyIterator.next();
final Access localAccessAnnotation = xProperty.getAnnotation( Access.class );
for ( int i = 0; i < recordComponents.size(); i++ ) {
final RecordComponentDetails componentDetails = recordComponents.get( i );
final AnnotationUsage<Access> localAccessAnnotation = componentDetails.getAnnotationUsage( Access.class );
if ( localAccessAnnotation == null ) {
continue;
}
propertyIterator.remove();
final String name = xProperty.getName();
persistentAttributeMap.put( name, xProperty );
persistentAttributesFromComponents.put( name, xProperty );
final String name = componentDetails.getName();
persistentAttributeMap.put( name, componentDetails );
persistentAttributesFromComponents.put( name, componentDetails );
}
}
private static void collectPersistentAttributesUsingClassLevelAccessType(
XClass xClass,
ClassDetails classDetails,
AccessType classLevelAccessType,
Map<String, XProperty> persistentAttributeMap,
Map<String,XProperty> persistentAttributesFromGetters,
Map<String,XProperty> persistentAttributesFromComponents,
List<XProperty> fields,
List<XProperty> getters,
List<XProperty> recordComponents) {
Map<String, MemberDetails> persistentAttributeMap,
Map<String,MethodDetails> persistentAttributesFromGetters,
Map<String,RecordComponentDetails> persistentAttributesFromComponents,
List<FieldDetails> fields,
List<MethodDetails> getters,
List<RecordComponentDetails> recordComponents) {
if ( classLevelAccessType == AccessType.FIELD ) {
for ( XProperty field : fields ) {
for ( int i = 0; i < fields.size(); i++ ) {
final FieldDetails field = fields.get( i );
final String name = field.getName();
if ( persistentAttributeMap.containsKey( name ) ) {
continue;
@ -243,19 +254,20 @@ public class PropertyContainer {
}
}
else {
for ( XProperty getter : getters ) {
final String name = getter.getName();
for ( int i = 0; i < getters.size(); i++ ) {
final MethodDetails getterDetails = getters.get( i );
final String name = getterDetails.getName();
// HHH-10242 detect registration of the same property getter twice - eg boolean isId() + UUID getId()
final XProperty previous = persistentAttributesFromGetters.get( name );
final MethodDetails previous = persistentAttributesFromGetters.get( name );
if ( previous != null ) {
throw new MappingException(
throw new org.hibernate.boot.MappingException(
LOG.ambiguousPropertyMethods(
xClass.getName(),
HCANNHelper.annotatedElementSignature( previous ),
HCANNHelper.annotatedElementSignature( getter )
classDetails.getName(),
previous.getName(),
getterDetails.getName()
),
new Origin( SourceType.ANNOTATION, xClass.getName() )
new Origin( SourceType.ANNOTATION, classDetails.getName() )
);
}
@ -263,192 +275,131 @@ public class PropertyContainer {
continue;
}
persistentAttributeMap.put( getter.getName(), getter );
persistentAttributesFromGetters.put( name, getter );
persistentAttributeMap.put( getterDetails.getName(), getterDetails );
persistentAttributesFromGetters.put( name, getterDetails );
}
// When a user uses the `property` access strategy for the entity owning an embeddable,
// we also have to add the attributes for record components,
// because record classes usually don't have getters, but just the record component accessors
for ( XProperty recordComponent : recordComponents ) {
final String name = recordComponent.getName();
for ( int i = 0; i < recordComponents.size(); i++ ) {
final RecordComponentDetails componentDetails = recordComponents.get( i );
final String name = componentDetails.getName();
if ( persistentAttributeMap.containsKey( name ) ) {
continue;
}
persistentAttributeMap.put( name, recordComponent );
persistentAttributesFromComponents.put( name, recordComponent );
persistentAttributeMap.put( name, componentDetails );
persistentAttributesFromComponents.put( name, componentDetails );
}
}
}
public XClass getEntityAtStake() {
return entityAtStake;
public ClassDetails getDeclaringClass() {
return classDetails;
}
public XClass getDeclaringClass() {
return xClass;
public ClassDetails getEntityAtStake() {
return entityAtStake;
}
public AccessType getClassLevelAccessType() {
return classLevelAccessType;
}
public Iterable<XProperty> propertyIterator() {
return persistentAttributes;
public Iterable<MemberDetails> propertyIterator() {
return attributeMembers;
}
private static List<XProperty> verifyAndInitializePersistentAttributes(XClass xClass, Map<String, XProperty> localAttributeMap) {
ArrayList<XProperty> output = new ArrayList<>( localAttributeMap.size() );
for ( XProperty xProperty : localAttributeMap.values() ) {
if ( !xProperty.isTypeResolved() && !discoverTypeWithoutReflection( xClass, xProperty ) ) {
String msg = "Property '" + StringHelper.qualify( xClass.getName(), xProperty.getName() ) +
private static List<MemberDetails> verifyAndInitializePersistentAttributes(
ClassDetails classDetails,
Map<String, MemberDetails> attributeMemberMap) {
ArrayList<MemberDetails> output = new ArrayList<>( attributeMemberMap.size() );
for ( MemberDetails attributeMemberDetails : attributeMemberMap.values() ) {
final TypeDetails memberType = attributeMemberDetails.getType();
if ( !memberType.isResolved()
&& !discoverTypeWithoutReflection( classDetails, attributeMemberDetails ) ) {
final String msg = "Property '" + StringHelper.qualify( classDetails.getName(), attributeMemberDetails.getName() ) +
"' has an unbound type and no explicit target entity (resolve this generics usage issue" +
" or set an explicit target attribute with '@OneToMany(target=)' or use an explicit '@Type')";
throw new AnnotationException( msg );
}
output.add( xProperty );
output.add( attributeMemberDetails );
}
return CollectionHelper.toSmallList( output );
}
//
// private void considerExplicitFieldAndPropertyAccess() {
// for ( XProperty property : fieldAccessMap.values() ) {
// Access access = property.getAnnotation( Access.class );
// if ( access == null ) {
// continue;
// }
//
// // see "2.3.2 Explicit Access Type" of JPA 2 spec
// // the access type for this property is explicitly set to AccessType.FIELD, hence we have to
// // use field access for this property even if the default access type for the class is AccessType.PROPERTY
// AccessType accessType = AccessType.getAccessStrategy( access.value() );
// if (accessType == AccessType.FIELD) {
// propertyAccessMap.put(property.getName(), property);
// }
// else {
// LOG.debug( "Placing @Access(AccessType.FIELD) on a field does not have any effect." );
// }
// }
//
// for ( XProperty property : propertyAccessMap.values() ) {
// Access access = property.getAnnotation( Access.class );
// if ( access == null ) {
// continue;
// }
//
// AccessType accessType = AccessType.getAccessStrategy( access.value() );
//
// // see "2.3.2 Explicit Access Type" of JPA 2 spec
// // the access type for this property is explicitly set to AccessType.PROPERTY, hence we have to
// // return use method access even if the default class access type is AccessType.FIELD
// if (accessType == AccessType.PROPERTY) {
// fieldAccessMap.put(property.getName(), property);
// }
// else {
// LOG.debug( "Placing @Access(AccessType.PROPERTY) on a field does not have any effect." );
// }
// }
// }
// /**
// * Retrieves all properties from the {@code xClass} with the specified access type. This method does not take
// * any jpa access rules/annotations into account yet.
// *
// * @param access The access type - {@code AccessType.FIELD} or {@code AccessType.Property}
// *
// * @return A maps of the properties with the given access type keyed against their property name
// */
// private TreeMap<String, XProperty> initProperties(AccessType access) {
// if ( !( AccessType.PROPERTY.equals( access ) || AccessType.FIELD.equals( access ) ) ) {
// throw new IllegalArgumentException( "Access type has to be AccessType.FIELD or AccessType.Property" );
// }
//
// //order so that property are used in the same order when binding native query
// TreeMap<String, XProperty> propertiesMap = new TreeMap<String, XProperty>();
// List<XProperty> properties = xClass.getDeclaredProperties( access.getType() );
// for ( XProperty property : properties ) {
// if ( mustBeSkipped( property ) ) {
// continue;
// }
// // HHH-10242 detect registration of the same property twice eg boolean isId() + UUID getId()
// XProperty oldProperty = propertiesMap.get( property.getName() );
// if ( oldProperty != null ) {
// throw new org.hibernate.boot.MappingException(
// LOG.ambiguousPropertyMethods(
// xClass.getName(),
// HCANNHelper.annotatedElementSignature( oldProperty ),
// HCANNHelper.annotatedElementSignature( property )
// ),
// new Origin( SourceType.ANNOTATION, xClass.getName() )
// );
// }
//
// propertiesMap.put( property.getName(), property );
// }
// return propertiesMap;
// }
private AccessType determineLocalClassDefinedAccessStrategy() {
AccessType classDefinedAccessType = AccessType.DEFAULT;
Access access = xClass.getAnnotation( Access.class );
final AnnotationUsage<Access> access = classDetails.getAnnotationUsage( Access.class );
if ( access != null ) {
classDefinedAccessType = AccessType.getAccessStrategy( access.value() );
classDefinedAccessType = AccessType.getAccessStrategy( access.getEnum( "value" ) );
}
return classDefinedAccessType;
}
private static boolean discoverTypeWithoutReflection(XClass clazz, XProperty property) {
if ( property.isAnnotationPresent( OneToOne.class ) && !property.getAnnotation( OneToOne.class )
.targetEntity()
.equals( void.class ) ) {
private static boolean discoverTypeWithoutReflection(ClassDetails classDetails, MemberDetails memberDetails) {
if ( memberDetails.hasAnnotationUsage( Target.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( OneToMany.class ) && !property.getAnnotation( OneToMany.class )
.targetEntity()
.equals( void.class ) ) {
if ( memberDetails.hasAnnotationUsage( Basic.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( ManyToOne.class ) && !property.getAnnotation( ManyToOne.class )
.targetEntity()
.equals( void.class ) ) {
if ( memberDetails.hasAnnotationUsage( Type.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( ManyToMany.class ) && !property.getAnnotation( ManyToMany.class )
.targetEntity()
.equals( void.class ) ) {
if ( memberDetails.hasAnnotationUsage( JavaType.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( org.hibernate.annotations.Any.class ) ) {
final AnnotationUsage<OneToOne> oneToOneAnn = memberDetails.getAnnotationUsage( OneToOne.class );
if ( oneToOneAnn != null ) {
final ClassDetails targetEntity = oneToOneAnn.getClassDetails( "targetEntity" );
return targetEntity != ClassDetails.VOID_CLASS_DETAILS;
}
final AnnotationUsage<OneToMany> oneToManyAnn = memberDetails.getAnnotationUsage( OneToMany.class );
if ( oneToManyAnn != null ) {
final ClassDetails targetEntity = oneToManyAnn.getClassDetails( "targetEntity" );
return targetEntity != ClassDetails.VOID_CLASS_DETAILS;
}
final AnnotationUsage<ManyToOne> manToOneAnn = memberDetails.getAnnotationUsage( ManyToOne.class );
if ( manToOneAnn != null ) {
final ClassDetails targetEntity = manToOneAnn.getClassDetails( "targetEntity" );
return targetEntity != ClassDetails.VOID_CLASS_DETAILS;
}
final AnnotationUsage<ManyToMany> manToManyAnn = memberDetails.getAnnotationUsage( ManyToMany.class );
if ( manToManyAnn != null ) {
final ClassDetails targetEntity = manToManyAnn.getClassDetails( "targetEntity" );
return targetEntity != ClassDetails.VOID_CLASS_DETAILS;
}
if ( memberDetails.hasAnnotationUsage( Any.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
if ( !property.isCollection() && !property.isArray() ) {
throw new AnnotationException( "Property '" + StringHelper.qualify( clazz.getName(), property.getName() )
+ "' annotated '@ManyToAny' is neither a collection nor an array" );
}
final AnnotationUsage<ManyToAny> manToAnyAnn = memberDetails.getAnnotationUsage( ManyToAny.class );
if ( manToAnyAnn != null ) {
return true;
}
else if ( property.isAnnotationPresent( Basic.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( Type.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( JavaType.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( JdbcTypeCode.class ) ) {
return true;
}
else if ( property.isAnnotationPresent( Target.class ) ) {
else if ( memberDetails.hasAnnotationUsage( JdbcTypeCode.class ) ) {
return true;
}
return false;
}
private static boolean mustBeSkipped(XProperty property) {
private static boolean mustBeSkipped(MemberDetails memberDetails) {
//TODO make those hardcoded tests more portable (through the bytecode provider?)
return property.isAnnotationPresent( Transient.class )
|| "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( property.getType().getName() );
return memberDetails.hasAnnotationUsage( Transient.class )
|| (memberDetails.getType() != null && "net.sf.cglib.transform.impl.InterceptFieldCallback".equals( memberDetails.getType().getName() ) );
}
}

View File

@ -6,15 +6,18 @@
*/
package org.hibernate.boot.model.internal;
import java.util.List;
import org.hibernate.annotations.ColumnTransformer;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Table;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Column;
import jakarta.persistence.ForeignKey;
@ -33,9 +36,9 @@ public interface PropertyHolder {
Table getTable();
void addProperty(Property prop, XClass declaringClass);
void addProperty(Property prop, MemberDetails memberDetails, ClassDetails declaringClass);
void addProperty(Property prop, AnnotatedColumns columns, XClass declaringClass);
void addProperty(Property prop, MemberDetails memberDetails, AnnotatedColumns columns, ClassDetails declaringClass);
KeyValue getIdentifier();
@ -62,36 +65,36 @@ public interface PropertyHolder {
/**
* return null if the column is not overridden, or an array of column if true
*/
Column[] getOverriddenColumn(String propertyName);
List<AnnotationUsage<Column>> getOverriddenColumn(String propertyName);
/**
* return null if the column is not overridden, or an array of column if true
*/
JoinColumn[] getOverriddenJoinColumn(String propertyName);
List<AnnotationUsage<JoinColumn>> getOverriddenJoinColumn(String propertyName);
/**
* return null if hte foreign key is not overridden, or the foreign key if true
*/
default ForeignKey getOverriddenForeignKey(String propertyName) {
default AnnotationUsage<ForeignKey> getOverriddenForeignKey(String propertyName) {
// todo: does this necessarily need to be a default method?
return null;
}
ColumnTransformer getOverriddenColumnTransformer(String logicalColumnName);
AnnotationUsage<ColumnTransformer> getOverriddenColumnTransformer(String logicalColumnName);
/**
* return
* - null if no join table is present,
* - the join table if not overridden,
* - the overridden join table otherwise
* - null if no join table is present,
* - the join table if not overridden,
* - the overridden join table otherwise
*/
JoinTable getJoinTable(XProperty property);
AnnotationUsage<JoinTable> getJoinTable(MemberDetails attributeMember);
String getEntityName();
Join addJoin(JoinTable joinTableAnn, boolean noDelayInPkColumnCreation);
Join addJoin(AnnotationUsage<JoinTable> joinTableAnn, boolean noDelayInPkColumnCreation);
Join addJoin(JoinTable joinTable, Table table, boolean noDelayInPkColumnCreation);
Join addJoin(AnnotationUsage<JoinTable> joinTable, Table table, boolean noDelayInPkColumnCreation);
boolean isInIdClass();
@ -103,12 +106,12 @@ public interface PropertyHolder {
*
* @param property The property
*/
void startingProperty(XProperty property);
void startingProperty(MemberDetails property);
/**
* Determine the AttributeConverter to use for the given property.
*
* @return The ConverterDescriptor
*/
ConverterDescriptor resolveAttributeConverterDescriptor(XProperty property);
ConverterDescriptor resolveAttributeConverterDescriptor(MemberDetails property);
}

View File

@ -8,14 +8,14 @@ package org.hibernate.boot.model.internal;
import java.util.Map;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.Component;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
/**
* This factory is here to build a PropertyHolder and prevent .mapping interface adding
@ -27,11 +27,11 @@ public final class PropertyHolderBuilder {
}
public static PropertyHolder buildPropertyHolder(
XClass clazzToProcess,
ClassDetails clazzToProcess,
PersistentClass persistentClass,
EntityBinder entityBinder,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
Map<ClassDetails, InheritanceState> inheritanceStatePerClass) {
return new ClassPropertyHolder(
persistentClass,
clazzToProcess,
@ -65,8 +65,8 @@ public final class PropertyHolderBuilder {
public static CollectionPropertyHolder buildPropertyHolder(
Collection collection,
String path,
XClass clazzToProcess,
XProperty property,
ClassDetails clazzToProcess,
MemberDetails property,
PropertyHolder parentPropertyHolder,
MetadataBuildingContext context) {
return new CollectionPropertyHolder(
@ -87,7 +87,7 @@ public final class PropertyHolderBuilder {
PersistentClass persistentClass,
Map<String, Join> joins,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass) {
Map<ClassDetails, InheritanceState> inheritanceStatePerClass) {
return new ClassPropertyHolder( persistentClass, null, joins, context, inheritanceStatePerClass );
}
}

View File

@ -6,13 +6,18 @@
*/
package org.hibernate.boot.model.internal;
import java.util.Collection;
import org.hibernate.MappingException;
import org.hibernate.annotations.Target;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.models.internal.ClassTypeDetailsImpl;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import jakarta.persistence.Access;
@ -25,23 +30,27 @@ import jakarta.persistence.Access;
public class PropertyInferredData implements PropertyData {
private final AccessType defaultAccess;
private final XProperty property;
private final ReflectionManager reflectionManager;
private final XClass declaringClass;
private final ClassDetails declaringClass;
private final MemberDetails propertyMember;
private final MetadataBuildingContext buildingContext;
/**
* Take the annotated element for lazy process
*/
public PropertyInferredData(XClass declaringClass, XProperty property, String propertyAccessor, ReflectionManager reflectionManager) {
public PropertyInferredData(
ClassDetails declaringClass,
MemberDetails propertyMember,
String propertyAccessor,
MetadataBuildingContext buildingContext) {
this.declaringClass = declaringClass;
this.property = property;
this.propertyMember = propertyMember;
this.defaultAccess = AccessType.getAccessStrategy( propertyAccessor );
this.reflectionManager = reflectionManager;
this.buildingContext = buildingContext;
}
@Override
public String toString() {
return String.format( "PropertyInferredData{property=%s, declaringClass=%s}", property, declaringClass );
return String.format( "PropertyInferredData{property=%s, declaringClass=%s}", propertyMember, declaringClass );
}
@Override
@ -50,9 +59,9 @@ public class PropertyInferredData implements PropertyData {
AccessType jpaAccessType = AccessType.DEFAULT;
Access access = property.getAnnotation( Access.class );
AnnotationUsage<Access> access = propertyMember.getAnnotationUsage( Access.class );
if ( access != null ) {
jpaAccessType = AccessType.getAccessStrategy( access.value() );
jpaAccessType = AccessType.getAccessStrategy( access.getEnum( "value" ) );
}
if ( jpaAccessType != AccessType.DEFAULT ) {
@ -63,59 +72,164 @@ public class PropertyInferredData implements PropertyData {
@Override
public String getPropertyName() throws MappingException {
return property.getName();
return propertyMember.resolveAttributeName();
}
@Override
public XClass getPropertyClass() throws MappingException {
if ( property.isAnnotationPresent( Target.class ) ) {
return reflectionManager.toXClass( property.getAnnotation( Target.class ).value() );
public TypeDetails getPropertyType() throws MappingException {
final AnnotationUsage<org.hibernate.boot.internal.Target> targetAnnotation = propertyMember.getAnnotationUsage( org.hibernate.boot.internal.Target.class );
if ( targetAnnotation != null ) {
return new ClassTypeDetailsImpl( targetAnnotation.getClassDetails( "value" ), TypeDetails.Kind.CLASS );
}
else {
return property.getType();
final AnnotationUsage<Target> legacyTargetAnnotation = propertyMember.getAnnotationUsage( Target.class );
if ( legacyTargetAnnotation != null ) {
return new ClassTypeDetailsImpl( legacyTargetAnnotation.getClassDetails( "value" ), TypeDetails.Kind.CLASS );
}
return propertyMember.getType();
}
@Override
public XClass getClassOrElement() throws MappingException {
if ( property.isAnnotationPresent( Target.class ) ) {
return reflectionManager.toXClass( property.getAnnotation( Target.class ).value() );
public TypeDetails getClassOrElementType() throws MappingException {
final AnnotationUsage<org.hibernate.boot.internal.Target> annotationUsage = propertyMember.getAnnotationUsage( org.hibernate.boot.internal.Target.class );
if ( annotationUsage != null ) {
return new ClassTypeDetailsImpl( annotationUsage.getClassDetails( "value" ), TypeDetails.Kind.CLASS );
}
else {
return property.getClassOrElementClass();
final AnnotationUsage<Target> legacyAnnotationUsage = propertyMember.getAnnotationUsage( Target.class );
if ( legacyAnnotationUsage != null ) {
return new ClassTypeDetailsImpl( legacyAnnotationUsage.getClassDetails( "value" ), TypeDetails.Kind.CLASS );
}
return propertyMember.getAssociatedType();
// final TypeDetails memberType = propertyMember.getType();
//
// if ( !propertyMember.isPlural() ) {
// return memberType;
// }
//
// if ( propertyMember.isArray() ) {
// return memberType.asArrayType().getConstituentType();
// }
//
// if ( memberType.isImplementor( Collection.class ) ) {
// if ( memberType.getTypeKind() == TypeDetails.Kind.PARAMETERIZED_TYPE ) {
// final ParameterizedTypeDetails parameterizedType = memberType.asParameterizedType();
// final List<TypeDetails> typeArguments = parameterizedType.getArguments();
// if ( CollectionHelper.size( typeArguments ) == 1 ) {
// return typeArguments.get( 0 );
// }
// return ClassBasedTypeDetails.OBJECT_TYPE_DETAILS;
// }
// if ( memberType.getTypeKind() == TypeDetails.Kind.TYPE_VARIABLE ) {
// // something like -
// // class TheEntity<E, L extends List<E>> {
// // L stuff;
// // }
// final TypeVariableDetails typeVariable = memberType.asTypeVariable();
// if ( CollectionHelper.size( typeVariable.getBounds() ) == 1 ) {
// return typeVariable.getBounds().get( 0 );
// }
// return ClassBasedTypeDetails.OBJECT_TYPE_DETAILS;
// }
// if ( memberType.getTypeKind() == TypeDetails.Kind.CLASS ) {
// // something like -
// // class LongList extends java.util.ArrayList<Long> {...}
// //
// // LongList values;
// return extractCollectionElementTypeFromClass( memberType.asClassType().getClassDetails() );
// }
// if ( memberType.getTypeKind() == TypeDetails.Kind.WILDCARD_TYPE ) {
// // todo : this is not correct, though can this ever happen in persistence models?
// final WildcardTypeDetails wildcardType = memberType.asWildcardType();
// return wildcardType.getBound();
// }
// }
//
// if ( memberType.isImplementor( Map.class ) ) {
// if ( memberType.getTypeKind() == TypeDetails.Kind.PARAMETERIZED_TYPE ) {
// final ParameterizedTypeDetails parameterizedType = memberType.asParameterizedType();
// final List<TypeDetails> typeArguments = parameterizedType.getArguments();
// if ( CollectionHelper.size( typeArguments ) == 2 ) {
// return typeArguments.get( 1 );
// }
// return ClassBasedTypeDetails.OBJECT_TYPE_DETAILS;
// }
// if ( memberType.getTypeKind() == TypeDetails.Kind.TYPE_VARIABLE ) {
// final TypeVariableDetails typeVariable = memberType.asTypeVariable();
// if ( CollectionHelper.size( typeVariable.getBounds() ) == 2 ) {
// return typeVariable.getBounds().get( 1 );
// }
// return ClassBasedTypeDetails.OBJECT_TYPE_DETAILS;
// }
// if ( memberType.getTypeKind() == TypeDetails.Kind.CLASS ) {
// // something like -
// // class LongList extends java.util.ArrayList<Long> {...}
// //
// // LongList values;
// return extractMapValueTypeFromClass( memberType.asClassType().getClassDetails() );
// }
// if ( memberType.getTypeKind() == TypeDetails.Kind.WILDCARD_TYPE ) {
// final WildcardTypeDetails wildcardType = memberType.asWildcardType();
// wildcardType.getBound();
// }
// }
//
// throw new MappingException(
// String.format(
// Locale.ROOT,
// "Unable to determine class/element type - %s#%s (%s)",
// declaringClass.getName(),
// propertyMember.getName(),
// memberType
// )
// );
}
private TypeDetails extractCollectionElementTypeFromClass(ClassDetails classDetails) {
if ( classDetails.getSuperClass() != null && classDetails.isImplementor( Collection.class ) ) {
// the class extends a class implementing the Collection contract
}
return null;
}
private TypeDetails extractMapValueTypeFromClass(ClassDetails classDetails) {
return null;
}
@Override
public XClass getClassOrPluralElement() throws MappingException {
if ( property.isAnnotationPresent( Target.class ) ) {
return reflectionManager.toXClass( property.getAnnotation( Target.class ).value() );
public ClassDetails getClassOrPluralElement() throws MappingException {
final AnnotationUsage<Target> targetAnnotationUsage = propertyMember.getAnnotationUsage( Target.class );
if ( targetAnnotationUsage != null ) {
return targetAnnotationUsage.getClassDetails( "value" );
}
else if ( property.isCollection() ) {
return property.getElementClass();
}
else {
return property.getClassOrElementClass();
if ( propertyMember.isPlural() ) {
return propertyMember.getElementType().determineRawClass();
}
return propertyMember.getAssociatedType().determineRawClass();
}
@Override
public String getClassOrElementName() throws MappingException {
return getClassOrElement().getName();
return getClassOrElementType().getName();
}
@Override
public String getTypeName() throws MappingException {
return getPropertyClass().getName();
return getPropertyType().getName();
}
@Override
public XProperty getProperty() {
return property;
public MemberDetails getAttributeMember() {
return propertyMember;
}
@Override
public XClass getDeclaringClass() {
public ClassDetails getDeclaringClass() {
return declaringClass;
}
}

View File

@ -7,19 +7,20 @@
package org.hibernate.boot.model.internal;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
public class PropertyPreloadedData implements PropertyData {
private final AccessType defaultAccess;
private final String propertyName;
private final XClass returnedClass;
private final TypeDetails returnedClass;
public PropertyPreloadedData(AccessType defaultAccess, String propertyName, XClass returnedClass) {
public PropertyPreloadedData(AccessType defaultAccess, String propertyName, TypeDetails returnedClass) {
this.defaultAccess = defaultAccess;
this.propertyName = propertyName;
this.returnedClass = returnedClass;
@ -40,17 +41,17 @@ public class PropertyPreloadedData implements PropertyData {
}
@Override
public XClass getClassOrElement() throws MappingException {
return getPropertyClass();
public TypeDetails getClassOrElementType() throws MappingException {
return getPropertyType();
}
@Override
public XClass getClassOrPluralElement() throws MappingException {
return getPropertyClass();
public ClassDetails getClassOrPluralElement() throws MappingException {
return getPropertyType().determineRawClass();
}
@Override
public XClass getPropertyClass() throws MappingException {
public TypeDetails getPropertyType() throws MappingException {
return returnedClass;
}
@ -65,12 +66,12 @@ public class PropertyPreloadedData implements PropertyData {
}
@Override
public XProperty getProperty() {
public MemberDetails getAttributeMember() {
return null; //instead of UnsupportedOperationException
}
@Override
public XClass getDeclaringClass() {
public ClassDetails getDeclaringClass() {
//Preloaded properties are artificial wrapper for collection element accesses
//and idClass creation, ignore.
return null;

View File

@ -7,6 +7,7 @@
package org.hibernate.boot.model.internal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.function.Supplier;
@ -33,6 +34,8 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.log.DeprecationLogger;
import org.hibernate.jpa.HibernateHints;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.query.sql.internal.ParameterParser;
import org.hibernate.query.sql.spi.ParameterRecognizer;
import org.hibernate.type.BasicType;
@ -50,7 +53,9 @@ import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.SqlResultSetMappings;
import jakarta.persistence.StoredProcedureParameter;
import static java.lang.Boolean.TRUE;
import static org.hibernate.internal.util.StringHelper.nullIfEmpty;
import static org.hibernate.internal.util.collections.CollectionHelper.determineProperSizing;
import static org.hibernate.internal.util.collections.CollectionHelper.setOf;
/**
@ -65,15 +70,15 @@ public abstract class QueryBinder {
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, QueryBinder.class.getName());
public static void bindQuery(
NamedQuery namedQuery,
AnnotationUsage<NamedQuery> namedQuery,
MetadataBuildingContext context,
boolean isDefault) {
if ( namedQuery == null ) {
return;
}
final String queryName = namedQuery.name();
final String queryString = namedQuery.query();
final String queryName = namedQuery.getString( "name" );
final String queryString = namedQuery.getString( "query" );
if ( queryName.isEmpty() ) {
throw new AnnotationException( "Class or package level '@NamedQuery' annotation must specify a 'name'" );
@ -83,7 +88,7 @@ public abstract class QueryBinder {
LOG.debugf( "Binding named query: %s => %s", queryName, queryString );
}
final QueryHintDefinition hints = new QueryHintDefinition( queryName, namedQuery.hints() );
final QueryHintDefinition hints = new QueryHintDefinition( queryName, namedQuery.getList( "hints" ) );
final NamedHqlQueryDefinition queryMapping = new NamedHqlQueryDefinitionImpl.Builder( queryName )
.setHqlString( queryString )
@ -108,26 +113,27 @@ public abstract class QueryBinder {
public static void bindNativeQuery(
NamedNativeQuery namedNativeQuery,
AnnotationUsage<NamedNativeQuery> namedNativeQuery,
MetadataBuildingContext context,
boolean isDefault) {
if ( namedNativeQuery == null ) {
return;
}
final String registrationName = namedNativeQuery.name();
final String queryString = namedNativeQuery.query();
final String registrationName = namedNativeQuery.getString( "name" );
final String queryString = namedNativeQuery.getString( "query" );
if ( registrationName.isEmpty() ) {
throw new AnnotationException( "Class or package level '@NamedNativeQuery' annotation must specify a 'name'" );
}
final QueryHintDefinition hints = new QueryHintDefinition( registrationName, namedNativeQuery.hints() );
final QueryHintDefinition hints = new QueryHintDefinition( registrationName, namedNativeQuery.getList( "hints" ) );
final String resultSetMappingName = namedNativeQuery.resultSetMapping();
final String resultSetMappingClassName = void.class.equals( namedNativeQuery.resultClass() )
final String resultSetMappingName = namedNativeQuery.getString( "resultSetMapping" );
final ClassDetails resultClassDetails = namedNativeQuery.getClassDetails( "resultClass" );
final String resultSetMappingClassName = ClassDetails.VOID_CLASS_DETAILS == resultClassDetails
? null
: namedNativeQuery.resultClass().getName();
: resultClassDetails.getName();
final NamedNativeQueryDefinitionBuilder builder = new NamedNativeQueryDefinitionBuilder( registrationName )
.setSqlString( queryString )
@ -161,24 +167,23 @@ public abstract class QueryBinder {
public static void bindNativeQuery(
String name,
SQLSelect sqlSelect,
XClass annotatedClass,
AnnotationUsage<SQLSelect> sqlSelect,
ClassDetails annotatedClass,
MetadataBuildingContext context) {
final NamedNativeQueryDefinitionBuilder builder = new NamedNativeQueryDefinitionBuilder( name )
.setFlushMode( FlushMode.MANUAL )
.setSqlString( sqlSelect.sql() )
.setQuerySpaces( setOf( sqlSelect.querySpaces() ) );
.setSqlString( sqlSelect.getString( "sql" ) )
.setQuerySpaces( setOf( sqlSelect.getList( "querySpaces" ) ) );
if ( annotatedClass != null ) {
builder.setResultSetMappingClassName( annotatedClass.getName() );
}
final SqlResultSetMapping resultSetMapping = sqlSelect.resultSetMapping();
if ( resultSetMapping.columns().length != 0
|| resultSetMapping.entities().length != 0
|| resultSetMapping.classes().length != 0) {
context.getMetadataCollector()
.addResultSetMapping( SqlResultSetMappingDescriptor.from( resultSetMapping, name ) );
final AnnotationUsage<SqlResultSetMapping> resultSetMapping = sqlSelect.getNestedUsage( "resultSetMapping" );
if ( !resultSetMapping.getList( "columns" ).isEmpty()
|| !resultSetMapping.getList( "entities" ).isEmpty()
|| !resultSetMapping.getList( "classes" ).isEmpty() ) {
context.getMetadataCollector().addResultSetMapping( SqlResultSetMappingDescriptor.from( resultSetMapping, name ) );
builder.setResultSetMappingName( name );
}
@ -186,39 +191,47 @@ public abstract class QueryBinder {
}
public static void bindNativeQuery(
org.hibernate.annotations.NamedNativeQuery namedNativeQuery,
AnnotationUsage<org.hibernate.annotations.NamedNativeQuery> namedNativeQuery,
MetadataBuildingContext context) {
if ( namedNativeQuery == null ) {
return;
}
final String registrationName = namedNativeQuery.name();
final String registrationName = namedNativeQuery.getString( "name" );
//ResultSetMappingDefinition mappingDefinition = mappings.getJdbcValuesMappingProducer( queryAnn.resultSetMapping() );
if ( registrationName.isEmpty() ) {
throw new AnnotationException( "Class or package level '@NamedNativeQuery' annotation must specify a 'name'" );
}
final String resultSetMappingName = namedNativeQuery.resultSetMapping();
final String resultSetMappingClassName = void.class.equals( namedNativeQuery.resultClass() )
final String resultSetMappingName = namedNativeQuery.getString( "resultSetMapping" );
final ClassDetails resultClassDetails = namedNativeQuery.getClassDetails( "resultClass" );
final String resultSetMappingClassName = ClassDetails.VOID_CLASS_DETAILS == resultClassDetails
? null
: namedNativeQuery.resultClass().getName();
: resultClassDetails.getName();
final Integer timeout = namedNativeQuery.getInteger( "timeout" );
final Integer fetchSize = namedNativeQuery.getInteger( "fetchSize" );
final List<String> querySpacesList = namedNativeQuery.getList( "querySpaces" );
final HashSet<String> querySpaces = new HashSet<>( determineProperSizing( querySpacesList.size() ) );
querySpaces.addAll( querySpacesList );
final NamedNativeQueryDefinitionBuilder builder = new NamedNativeQueryDefinitionBuilder( registrationName )
.setSqlString( namedNativeQuery.query() )
.setSqlString( namedNativeQuery.getString( "query" ) )
.setResultSetMappingName( resultSetMappingName )
.setResultSetMappingClassName( resultSetMappingClassName )
.setCacheable( namedNativeQuery.cacheable() )
.setCacheRegion( nullIfEmpty( namedNativeQuery.cacheRegion() ) )
.setCacheMode( getCacheMode( namedNativeQuery ) )
.setTimeout( namedNativeQuery.timeout() < 0 ? null : namedNativeQuery.timeout() )
.setFetchSize( namedNativeQuery.fetchSize() < 0 ? null : namedNativeQuery.fetchSize() )
.setFlushMode( getFlushMode( namedNativeQuery.flushMode() ) )
.setReadOnly( namedNativeQuery.readOnly() )
.setQuerySpaces( setOf( namedNativeQuery.querySpaces() ) )
.setComment( nullIfEmpty( namedNativeQuery.comment() ) );
.setCacheable( namedNativeQuery.getBoolean( "cacheable" ) )
.setCacheRegion( nullIfEmpty( namedNativeQuery.getString( "cacheRegion" ) ) )
.setCacheMode( getCacheMode( namedNativeQuery ) )
.setTimeout( timeout < 0 ? null : timeout )
.setFetchSize( fetchSize < 0 ? null : fetchSize )
.setFlushMode( getFlushMode( namedNativeQuery.getEnum( "flushMode" ) ) )
.setReadOnly( namedNativeQuery.getBoolean( "readOnly" ) )
.setQuerySpaces( querySpaces )
.setComment( nullIfEmpty( namedNativeQuery.getString( "comment" ) ) );
if ( namedNativeQuery.callable() ) {
if ( TRUE == namedNativeQuery.getBoolean( "callable" ) ) {
final NamedProcedureCallDefinition definition =
createStoredProcedure( builder, context, () -> illegalCallSyntax( namedNativeQuery ) );
context.getMetadataCollector().addNamedProcedureCallDefinition( definition );
@ -320,71 +333,83 @@ public abstract class QueryBinder {
return new NamedProcedureCallDefinitionImpl( AnnotationFactory.create( descriptor ) );
}
public static void bindQueries(NamedQueries namedQueries, MetadataBuildingContext context, boolean isDefault) {
if ( namedQueries != null ) {
for ( NamedQuery namedQuery : namedQueries.value() ) {
bindQuery( namedQuery, context, isDefault );
}
public static void bindQueries(AnnotationUsage<NamedQueries> namedQueries, MetadataBuildingContext context, boolean isDefault) {
if ( namedQueries == null ) {
return;
}
final List<AnnotationUsage<NamedQuery>> nestedValues = namedQueries.getList( "value" );
for ( AnnotationUsage<NamedQuery> nestedValue : nestedValues ) {
bindQuery( nestedValue, context, isDefault );
}
}
public static void bindNativeQueries(
NamedNativeQueries namedNativeQueries,
AnnotationUsage<NamedNativeQueries> namedNativeQueries,
MetadataBuildingContext context,
boolean isDefault) {
if ( namedNativeQueries != null ) {
for ( NamedNativeQuery namedNativeQuery : namedNativeQueries.value() ) {
bindNativeQuery( namedNativeQuery, context, isDefault );
}
if ( namedNativeQueries == null ) {
return;
}
final List<AnnotationUsage<NamedNativeQuery>> nestedValues = namedNativeQueries.getList( "value" );
for ( AnnotationUsage<NamedNativeQuery> nestedValue : nestedValues ) {
bindNativeQuery( nestedValue, context, isDefault );
}
}
public static void bindNativeQueries(
org.hibernate.annotations.NamedNativeQueries namedNativeQueries,
AnnotationUsage<org.hibernate.annotations.NamedNativeQueries> namedNativeQueries,
MetadataBuildingContext context) {
if ( namedNativeQueries != null ) {
for ( org.hibernate.annotations.NamedNativeQuery namedNativeQuery : namedNativeQueries.value() ) {
bindNativeQuery( namedNativeQuery, context );
}
if ( namedNativeQueries == null ) {
return;
}
final List<AnnotationUsage<org.hibernate.annotations.NamedNativeQuery>> nestedValues = namedNativeQueries.getList( "value" );
for ( AnnotationUsage<org.hibernate.annotations.NamedNativeQuery> nestedValue : nestedValues ) {
bindNativeQuery( nestedValue, context );
}
}
public static void bindQuery(
String name,
HQLSelect hqlSelect,
AnnotationUsage<HQLSelect> hqlSelect,
MetadataBuildingContext context) {
final NamedHqlQueryDefinition hqlQueryDefinition = new NamedHqlQueryDefinition.Builder( name )
.setFlushMode( FlushMode.MANUAL )
.setHqlString( hqlSelect.query() )
.setHqlString( hqlSelect.getString( "query" ) )
.build();
context.getMetadataCollector().addNamedQuery( hqlQueryDefinition );
}
public static void bindQuery(
org.hibernate.annotations.NamedQuery namedQuery,
AnnotationUsage<org.hibernate.annotations.NamedQuery> namedQuery,
MetadataBuildingContext context) {
if ( namedQuery == null ) {
return;
}
final String registrationName = namedQuery.name();
final String registrationName = namedQuery.getString( "name" );
//ResultSetMappingDefinition mappingDefinition = mappings.getJdbcValuesMappingProducer( namedQuery.resultSetMapping() );
if ( registrationName.isEmpty() ) {
throw new AnnotationException( "Class or package level '@NamedQuery' annotation must specify a 'name'" );
}
final Integer timeout = namedQuery.getInteger( "timeout" );
final Integer fetchSize = namedQuery.getInteger( "fetchSize" );
final NamedHqlQueryDefinition.Builder builder = new NamedHqlQueryDefinition.Builder( registrationName )
.setHqlString( namedQuery.query() )
.setCacheable( namedQuery.cacheable() )
.setCacheRegion(nullIfEmpty(namedQuery.cacheRegion()))
.setHqlString( namedQuery.getString( "query" ) )
.setCacheable( namedQuery.getBoolean( "cacheable" ) )
.setCacheRegion( nullIfEmpty( namedQuery.getString( "cacheRegion" ) ) )
.setCacheMode( getCacheMode( namedQuery ) )
.setTimeout( namedQuery.timeout() < 0 ? null : namedQuery.timeout() )
.setFetchSize( namedQuery.fetchSize() < 0 ? null : namedQuery.fetchSize() )
.setFlushMode( getFlushMode( namedQuery.flushMode() ) )
.setReadOnly( namedQuery.readOnly() )
.setComment( nullIfEmpty( namedQuery.comment() ) );
.setTimeout( timeout < 0 ? null : timeout )
.setFetchSize( fetchSize < 0 ? null : fetchSize )
.setFlushMode( getFlushMode( namedQuery.getEnum( "flushMode" ) ) )
.setReadOnly( namedQuery.getBoolean( "readOnly" ) )
.setComment( nullIfEmpty( namedQuery.getString( "comment" ) ) );
final NamedHqlQueryDefinitionImpl hqlQueryDefinition = builder.build();
@ -395,14 +420,14 @@ public abstract class QueryBinder {
context.getMetadataCollector().addNamedQuery( hqlQueryDefinition );
}
private static CacheMode getCacheMode(org.hibernate.annotations.NamedQuery namedQuery) {
CacheMode cacheMode = CacheMode.fromJpaModes( namedQuery.cacheRetrieveMode(), namedQuery.cacheStoreMode() );
return cacheMode == null || cacheMode == CacheMode.NORMAL ? getCacheMode( namedQuery.cacheMode() ) : cacheMode;
}
private static CacheMode getCacheMode(org.hibernate.annotations.NamedNativeQuery namedNativeQuery) {
CacheMode cacheMode = CacheMode.fromJpaModes( namedNativeQuery.cacheRetrieveMode(), namedNativeQuery.cacheStoreMode() );
return cacheMode == null || cacheMode == CacheMode.NORMAL ? getCacheMode( namedNativeQuery.cacheMode() ) : cacheMode;
private static CacheMode getCacheMode(AnnotationUsage<?> namedQuery) {
final CacheMode cacheMode = CacheMode.fromJpaModes(
namedQuery.getEnum( "cacheRetrieveMode" ),
namedQuery.getEnum( "cacheStoreMode" )
);
return cacheMode == null || cacheMode == CacheMode.NORMAL
? interpretCacheMode( namedQuery.getEnum( "cacheMode" ) )
: cacheMode;
}
private static FlushMode getFlushMode(FlushModeType flushModeType) {
@ -422,7 +447,7 @@ public abstract class QueryBinder {
}
}
private static CacheMode getCacheMode(CacheModeType cacheModeType) {
private static CacheMode interpretCacheMode(CacheModeType cacheModeType) {
switch ( cacheModeType ) {
case GET:
return CacheMode.GET;
@ -441,21 +466,24 @@ public abstract class QueryBinder {
public static void bindQueries(
org.hibernate.annotations.NamedQueries namedQueries,
AnnotationUsage<org.hibernate.annotations.NamedQueries> namedQueries,
MetadataBuildingContext context) {
if ( namedQueries != null ) {
for (org.hibernate.annotations.NamedQuery namedQuery : namedQueries.value()) {
bindQuery( namedQuery, context );
}
if ( namedQueries == null ) {
return;
}
final List<AnnotationUsage<org.hibernate.annotations.NamedQuery>> nestedValues = namedQueries.getList( "value" );
for ( AnnotationUsage<org.hibernate.annotations.NamedQuery> nestedValue : nestedValues ) {
bindQuery( nestedValue, context );
}
}
public static void bindNamedStoredProcedureQuery(
NamedStoredProcedureQuery namedStoredProcedureQuery,
AnnotationUsage<NamedStoredProcedureQuery> namedStoredProcedureQuery,
MetadataBuildingContext context,
boolean isDefault) {
if ( namedStoredProcedureQuery != null ) {
if ( namedStoredProcedureQuery.name().isEmpty() ) {
if ( namedStoredProcedureQuery.getString( "name" ).isEmpty() ) {
throw new AnnotationException( "Class or package level '@NamedStoredProcedureQuery' annotation must specify a 'name'" );
}
@ -471,22 +499,25 @@ public abstract class QueryBinder {
}
public static void bindSqlResultSetMappings(
SqlResultSetMappings resultSetMappings,
AnnotationUsage<SqlResultSetMappings> resultSetMappings,
MetadataBuildingContext context,
boolean isDefault) {
if ( resultSetMappings != null ) {
for ( SqlResultSetMapping resultSetMapping : resultSetMappings.value() ) {
bindSqlResultSetMapping( resultSetMapping, context, isDefault );
}
if ( resultSetMappings == null ) {
return;
}
final List<AnnotationUsage<SqlResultSetMapping>> mappings = resultSetMappings.getList( "value" );
for ( AnnotationUsage<SqlResultSetMapping> mapping : mappings ) {
bindSqlResultSetMapping( mapping, context, isDefault );
}
}
public static void bindSqlResultSetMapping(
SqlResultSetMapping resultSetMapping,
AnnotationUsage<SqlResultSetMapping> resultSetMappingAnn,
MetadataBuildingContext context,
boolean isDefault) {
//no need to handle inSecondPass
context.getMetadataCollector().addSecondPass( new ResultSetMappingSecondPass( resultSetMapping, context, isDefault ) );
context.getMetadataCollector().addSecondPass( new ResultSetMappingSecondPass( resultSetMappingAnn, context, isDefault ) );
}
private static class JdbcCall {
@ -631,8 +662,8 @@ public abstract class QueryBinder {
return i;
}
private static AnnotationException illegalCallSyntax(org.hibernate.annotations.NamedNativeQuery queryAnn) {
return new AnnotationException( "Callable 'NamedNativeQuery' named '" + queryAnn.name()
private static AnnotationException illegalCallSyntax(AnnotationUsage<org.hibernate.annotations.NamedNativeQuery> queryAnn) {
return new AnnotationException( "Callable 'NamedNativeQuery' named '" + queryAnn.getString( "name" )
+ "' does not use the JDBC call syntax" );
}
}

View File

@ -7,7 +7,7 @@
package org.hibernate.boot.model.internal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.AnnotationException;
@ -18,15 +18,19 @@ import org.hibernate.LockOptions;
import org.hibernate.MappingException;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.internal.util.LockModeConverter;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.jpa.HibernateHints;
import org.hibernate.jpa.LegacySpecHints;
import org.hibernate.jpa.SpecHints;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.LockModeType;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.QueryHint;
import static org.hibernate.internal.util.collections.CollectionHelper.mapOfSize;
/**
* @author Strong Liu
*/
@ -34,15 +38,15 @@ public class QueryHintDefinition {
private final String queryName;
private final Map<String, Object> hintsMap;
public QueryHintDefinition(String queryName, final QueryHint[] hints) {
public QueryHintDefinition(String queryName, final List<AnnotationUsage<QueryHint>> hints) {
this.queryName = queryName;
if ( hints == null || hints.length == 0 ) {
if ( CollectionHelper.isEmpty( hints ) ) {
hintsMap = Collections.emptyMap();
}
else {
final Map<String, Object> hintsMap = new HashMap<>();
for ( QueryHint hint : hints ) {
hintsMap.put( hint.name(), hint.value() );
final Map<String, Object> hintsMap = mapOfSize( hints.size() );
for ( AnnotationUsage<QueryHint> hint : hints ) {
hintsMap.put( hint.getString( "name" ), hint.getString( "value" ) );
}
this.hintsMap = hintsMap;
}
@ -149,8 +153,8 @@ public class QueryHintDefinition {
}
}
public LockOptions determineLockOptions(NamedQuery namedQueryAnnotation) {
final LockModeType lockModeType = namedQueryAnnotation.lockMode();
public LockOptions determineLockOptions(AnnotationUsage<NamedQuery> namedQueryAnnotation) {
final LockModeType lockModeType = namedQueryAnnotation.getEnum( "lockMode" );
final Integer lockTimeoutHint = specLockTimeout();
final Boolean followOnLocking = getBooleanWrapper( HibernateHints.HINT_FOLLOW_ON_LOCKING );

View File

@ -12,6 +12,7 @@ import org.hibernate.MappingException;
import org.hibernate.boot.query.SqlResultSetMappingDescriptor;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.SqlResultSetMapping;
@ -21,11 +22,11 @@ import jakarta.persistence.SqlResultSetMapping;
public class ResultSetMappingSecondPass implements QuerySecondPass {
// private static final CoreMessageLogger LOG = CoreLogging.messageLogger( ResultsetMappingSecondPass.class );
private final SqlResultSetMapping annotation;
private final AnnotationUsage<SqlResultSetMapping> annotation;
private final MetadataBuildingContext context;
private final boolean isDefault;
public ResultSetMappingSecondPass(SqlResultSetMapping annotation, MetadataBuildingContext context, boolean isDefault) {
public ResultSetMappingSecondPass(AnnotationUsage<SqlResultSetMapping> annotation, MetadataBuildingContext context, boolean isDefault) {
this.annotation = annotation;
this.context = context;
this.isDefault = isDefault;

View File

@ -9,19 +9,18 @@ package org.hibernate.boot.model.internal;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.mapping.PersistentClass;
public class SecondaryTableFromAnnotationSecondPass implements SecondPass {
private final EntityBinder entityBinder;
private final PropertyHolder propertyHolder;
private final XAnnotatedElement annotatedClass;
public SecondaryTableFromAnnotationSecondPass(EntityBinder entityBinder, PropertyHolder propertyHolder, XAnnotatedElement annotatedClass) {
public SecondaryTableFromAnnotationSecondPass(
EntityBinder entityBinder,
PropertyHolder propertyHolder) {
this.entityBinder = entityBinder;
this.propertyHolder = propertyHolder;
this.annotatedClass = annotatedClass;
}
@Override

View File

@ -9,7 +9,6 @@ package org.hibernate.boot.model.internal;
import java.util.Map;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.boot.spi.SecondPass;
import org.hibernate.mapping.PersistentClass;
@ -19,12 +18,10 @@ import org.hibernate.mapping.PersistentClass;
public class SecondaryTableSecondPass implements SecondPass {
private final EntityBinder entityBinder;
private final PropertyHolder propertyHolder;
private final XAnnotatedElement annotatedClass;
public SecondaryTableSecondPass(EntityBinder entityBinder, PropertyHolder propertyHolder, XAnnotatedElement annotatedClass) {
public SecondaryTableSecondPass(EntityBinder entityBinder, PropertyHolder propertyHolder) {
this.entityBinder = entityBinder;
this.propertyHolder = propertyHolder;
this.annotatedClass = annotatedClass;
}
@Override

View File

@ -13,6 +13,7 @@ import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.mapping.Collection;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Set;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.usertype.UserCollectionType;
@ -37,7 +38,7 @@ public class SetBinder extends CollectionBinder {
}
@Override
public void setSqlOrderBy(OrderBy orderByAnn) {
public void setSqlOrderBy(AnnotationUsage<OrderBy> orderByAnn) {
if ( orderByAnn != null ) {
super.setSqlOrderBy( orderByAnn );
}

View File

@ -21,6 +21,7 @@ import org.hibernate.metamodel.mapping.SoftDeletableModelPart;
import org.hibernate.metamodel.mapping.SoftDeleteMapping;
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
import org.hibernate.metamodel.mapping.internal.SoftDeleteMappingImpl;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.sql.ast.spi.SqlExpressionResolver;
import org.hibernate.sql.ast.tree.expression.ColumnReference;
import org.hibernate.sql.ast.tree.expression.Expression;
@ -44,18 +45,19 @@ public class SoftDeleteHelper {
/**
* Creates and binds the column and value for modeling the soft-delete in the database
*
* @param softDeleteConfig The SoftDelete annotation
* @param softDeleteConfigAnnotation The SoftDelete annotation
* @param target The thing which is to be soft-deleted
* @param table The table to which the soft-delete should be applied
* @param context The processing context for access to needed info and services
*/
public static void bindSoftDeleteIndicator(
SoftDelete softDeleteConfig,
AnnotationUsage<SoftDelete> softDeleteConfigAnnotation,
SoftDeletable target,
Table table,
MetadataBuildingContext context) {
assert softDeleteConfig != null;
assert softDeleteConfigAnnotation != null;
final SoftDelete softDeleteConfig = softDeleteConfigAnnotation.toAnnotation();
final BasicValue softDeleteIndicatorValue = createSoftDeleteIndicatorValue( softDeleteConfig, table, context );
final Column softDeleteIndicatorColumn = createSoftDeleteIndicatorColumn(
softDeleteConfig,

View File

@ -37,6 +37,7 @@ import org.hibernate.mapping.SortableValue;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.ToOne;
import org.hibernate.mapping.Value;
import org.hibernate.models.spi.AnnotationUsage;
import org.jboss.logging.Logger;
@ -72,8 +73,8 @@ public class TableBinder {
private String associatedEntity;
private String associatedJpaEntity;
private boolean isJPA2ElementCollection;
private UniqueConstraint[] uniqueConstraints;
private Index[] indexes;
private List<AnnotationUsage<UniqueConstraint>> uniqueConstraints;
private List<AnnotationUsage<Index>> indexes;
public void setBuildingContext(MetadataBuildingContext buildingContext) {
this.buildingContext = buildingContext;
@ -99,11 +100,11 @@ public class TableBinder {
isAbstract = anAbstract;
}
public void setUniqueConstraints(UniqueConstraint[] uniqueConstraints) {
public void setUniqueConstraints(List<AnnotationUsage<UniqueConstraint>> uniqueConstraints) {
this.uniqueConstraints = uniqueConstraints;
}
public void setJpaIndex(Index[] indexes){
public void setJpaIndex(List<AnnotationUsage<Index>> indexes){
this.indexes = indexes;
}
@ -431,7 +432,7 @@ public class TableBinder {
String catalog,
Identifier logicalName,
boolean isAbstract,
UniqueConstraint[] uniqueConstraints,
List<AnnotationUsage<UniqueConstraint>> uniqueConstraints,
MetadataBuildingContext buildingContext) {
return buildAndFillTable(
schema,
@ -451,7 +452,7 @@ public class TableBinder {
String catalog,
Identifier logicalName,
boolean isAbstract,
UniqueConstraint[] uniqueConstraints,
List<AnnotationUsage<UniqueConstraint>> uniqueConstraints,
MetadataBuildingContext buildingContext,
String subselect,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
@ -473,17 +474,23 @@ public class TableBinder {
String catalog,
Identifier logicalName,
boolean isAbstract,
UniqueConstraint[] uniqueConstraints,
Index[] indexes,
List<AnnotationUsage<UniqueConstraint>> uniqueConstraints,
List<AnnotationUsage<Index>> indexes,
MetadataBuildingContext buildingContext,
String subselect,
InFlightMetadataCollector.EntityTableXref denormalizedSuperTableXref) {
final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector();
final Table table =
addTable( nullIfEmpty( schema ), nullIfEmpty( catalog ),
logicalName, isAbstract, buildingContext, subselect,
denormalizedSuperTableXref, metadataCollector );
final Table table = addTable(
nullIfEmpty( schema ),
nullIfEmpty( catalog ),
logicalName,
isAbstract,
buildingContext,
subselect,
denormalizedSuperTableXref,
metadataCollector
);
if ( uniqueConstraints != null ) {
new IndexBinder( buildingContext ).bindUniqueConstraints( table, uniqueConstraints );
@ -841,12 +848,34 @@ public class TableBinder {
}
}
static void addIndexes(Table table, org.hibernate.annotations.Index[] indexes, MetadataBuildingContext context) {
for ( org.hibernate.annotations.Index index : indexes ) {
static void addIndexes(Table table, List<AnnotationUsage<org.hibernate.annotations.Index>> indexes, MetadataBuildingContext context) {
for ( AnnotationUsage<org.hibernate.annotations.Index> indexUsage : indexes ) {
final String name = indexUsage.getString( "name" );
final String[] columnNames = indexUsage.<String>getList( "columnNames" ).toArray(new String[0]);
//no need to handle inSecondPass here since it is only called from EntityBinder
context.getMetadataCollector().addSecondPass(
new IndexOrUniqueKeySecondPass( table, index.name(), index.columnNames(), context )
);
context.getMetadataCollector().addSecondPass( new IndexOrUniqueKeySecondPass(
table,
name,
columnNames,
context
) );
}
}
static void addJpaIndexes(Table table, List<AnnotationUsage<jakarta.persistence.Index>> indexes, MetadataBuildingContext context) {
for ( AnnotationUsage<jakarta.persistence.Index> indexUsage : indexes ) {
final String name = indexUsage.getString( "name" );
final String columnList = indexUsage.getString( "columnList" );
final String[] columnFragments = columnList.split(",");
//no need to handle inSecondPass here since it is only called from EntityBinder
context.getMetadataCollector().addSecondPass( new IndexOrUniqueKeySecondPass(
table,
name,
columnFragments,
context
) );
}
}

View File

@ -6,20 +6,23 @@
*/
package org.hibernate.boot.model.internal;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import org.hibernate.annotations.TimeZoneStorage;
import org.hibernate.annotations.common.reflection.XAnnotatedElement;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.annotations.TimeZoneStorageType;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.models.spi.AnnotationTarget;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.usertype.CompositeUserType;
import org.hibernate.usertype.internal.OffsetDateTimeCompositeUserType;
import org.hibernate.usertype.internal.OffsetTimeCompositeUserType;
import org.hibernate.usertype.internal.ZonedDateTimeCompositeUserType;
import java.time.OffsetDateTime;
import java.time.OffsetTime;
import java.time.ZonedDateTime;
import static org.hibernate.TimeZoneStorageStrategy.COLUMN;
import static org.hibernate.dialect.TimeZoneSupport.NATIVE;
@ -30,10 +33,10 @@ public class TimeZoneStorageHelper {
private static final String ZONED_DATETIME_CLASS = ZonedDateTime.class.getName();
static Class<? extends CompositeUserType<?>> resolveTimeZoneStorageCompositeUserType(
XProperty property,
XClass returnedClass,
MemberDetails attributeMember,
ClassDetails returnedClass,
MetadataBuildingContext context) {
if ( useColumnForTimeZoneStorage( property, context ) ) {
if ( useColumnForTimeZoneStorage( attributeMember, context ) ) {
String returnedClassName = returnedClass.getName();
if ( OFFSET_DATETIME_CLASS.equals( returnedClassName ) ) {
return OffsetDateTimeCompositeUserType.class;
@ -54,24 +57,31 @@ public class TimeZoneStorageHelper {
|| isOffsetTimeClass( returnedClassName );
}
public static boolean isOffsetTimeClass(XAnnotatedElement element) {
if ( element instanceof XProperty ) {
XProperty property = (XProperty) element;
return isOffsetTimeClass( property.getType().getName() );
public static boolean isOffsetTimeClass(AnnotationTarget element) {
if ( element instanceof MemberDetails memberDetails ) {
return isOffsetTimeClass( memberDetails );
}
return false;
}
public static boolean isOffsetTimeClass(MemberDetails element) {
final TypeDetails type = element.getType();
if ( type == null ) {
return false;
}
return isOffsetTimeClass( type.determineRawClass().getClassName() );
}
private static boolean isOffsetTimeClass(String returnedClassName) {
return OFFSET_TIME_CLASS.equals( returnedClassName );
}
static boolean useColumnForTimeZoneStorage(XAnnotatedElement element, MetadataBuildingContext context) {
final TimeZoneStorage timeZoneStorage = element.getAnnotation( TimeZoneStorage.class );
static boolean useColumnForTimeZoneStorage(AnnotationTarget element, MetadataBuildingContext context) {
final AnnotationUsage<TimeZoneStorage> timeZoneStorage = element.getAnnotationUsage( TimeZoneStorage.class );
if ( timeZoneStorage == null ) {
if ( element instanceof XProperty ) {
XProperty property = (XProperty) element;
return isTemporalWithTimeZoneClass( property.getType().getName() )
if ( element instanceof MemberDetails attributeMember ) {
return isTemporalWithTimeZoneClass( attributeMember.getType().getName() )
//no @TimeZoneStorage annotation, so we need to use the default storage strategy
&& context.getBuildingOptions().getDefaultTimeZoneStorage() == COLUMN;
}
@ -80,15 +90,12 @@ public class TimeZoneStorageHelper {
}
}
else {
switch ( timeZoneStorage.value() ) {
case COLUMN:
return true;
case AUTO:
// if the db has native support for timezones, we use that, not a column
return context.getBuildingOptions().getTimeZoneSupport() != NATIVE;
default:
return false;
}
return switch ( timeZoneStorage.getEnum( "value", TimeZoneStorageType.class ) ) {
case COLUMN -> true;
// if the db has native support for timezones, we use that, not a column
case AUTO -> context.getBuildingOptions().getTimeZoneSupport() != NATIVE;
default -> false;
};
}
}
}

View File

@ -17,24 +17,24 @@ import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchProfileOverride;
import org.hibernate.annotations.FetchProfileOverrides;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.annotations.LazyToOneOption;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.Join;
import org.hibernate.mapping.KeyValue;
import org.hibernate.mapping.SimpleValue;
import org.hibernate.mapping.ToOne;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import jakarta.persistence.Column;
import jakarta.persistence.FetchType;
@ -80,15 +80,15 @@ public class ToOneBinder {
boolean isIdentifierMapper,
boolean inSecondPass,
MetadataBuildingContext context,
XProperty property,
MemberDetails property,
AnnotatedJoinColumns joinColumns,
PropertyBinder propertyBinder,
boolean forcePersist) {
final ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
final AnnotationUsage<ManyToOne> manyToOne = property.getAnnotationUsage( ManyToOne.class );
//check validity
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
if ( property.hasAnnotationUsage( Column.class )
|| property.hasAnnotationUsage( Columns.class ) ) {
throw new AnnotationException(
"Property '" + getPath( propertyHolder, inferredData )
+ "' is a '@ManyToOne' association and may not use '@Column' to specify column mappings (use '@JoinColumn' instead)"
@ -102,19 +102,19 @@ public class ToOneBinder {
);
}
final Cascade hibernateCascade = property.getAnnotation( Cascade.class );
final NotFound notFound = property.getAnnotation( NotFound.class );
final NotFoundAction notFoundAction = notFound == null ? null : notFound.action();
matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), notFoundAction, manyToOne.fetch() );
final OnDelete onDelete = property.getAnnotation( OnDelete.class );
final JoinTable joinTable = propertyHolder.getJoinTable( property );
final AnnotationUsage<Cascade> hibernateCascade = property.getAnnotationUsage( Cascade.class );
final AnnotationUsage<NotFound> notFound = property.getAnnotationUsage( NotFound.class );
final NotFoundAction notFoundAction = notFound == null ? null : notFound.getEnum( "action" );
matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), notFoundAction, manyToOne.getEnum( "fetch" ) );
final AnnotationUsage<OnDelete> onDelete = property.getAnnotationUsage( OnDelete.class );
final AnnotationUsage<JoinTable> joinTable = propertyHolder.getJoinTable( property );
bindManyToOne(
getCascadeStrategy( manyToOne.cascade(), hibernateCascade, false, forcePersist ),
getCascadeStrategy( manyToOne.getList( "cascade" ), hibernateCascade, false, forcePersist ),
joinColumns,
joinTable,
!isMandatory( manyToOne.optional(), property, notFoundAction ),
!isMandatory( manyToOne.getBoolean( "optional" ), property, notFoundAction ),
notFoundAction,
onDelete == null ? null : onDelete.action(),
onDelete == null ? null : onDelete.getEnum( "action" ),
getTargetEntity( inferredData, context ),
propertyHolder,
inferredData,
@ -136,7 +136,7 @@ public class ToOneBinder {
|| isIdentifierMapper;
}
private static boolean isMandatory(boolean optional, XProperty property, NotFoundAction notFoundAction) {
private static boolean isMandatory(boolean optional, MemberDetails property, NotFoundAction notFoundAction) {
// @MapsId means the columns belong to the pk;
// A @MapsId association (obviously) must be non-null when the entity is first persisted.
// If a @MapsId association is not mapped with @NotFound(IGNORE), then the association
@ -145,18 +145,18 @@ public class ToOneBinder {
// the association is optional.
// @OneToOne(optional = true) with @PKJC makes the association optional.
return !optional
|| property.isAnnotationPresent( Id.class )
|| property.isAnnotationPresent( MapsId.class ) && notFoundAction != NotFoundAction.IGNORE;
|| property.hasAnnotationUsage( Id.class )
|| property.hasAnnotationUsage( MapsId.class ) && notFoundAction != NotFoundAction.IGNORE;
}
private static void bindManyToOne(
String cascadeStrategy,
AnnotatedJoinColumns joinColumns,
JoinTable joinTable,
AnnotationUsage<JoinTable> joinTable,
boolean optional,
NotFoundAction notFoundAction,
OnDeleteAction onDeleteAction,
XClass targetEntity,
ClassDetails targetEntity,
PropertyHolder propertyHolder,
PropertyData inferredData,
boolean unique, // identifies a "logical" @OneToOne
@ -164,8 +164,7 @@ public class ToOneBinder {
boolean inSecondPass,
PropertyBinder propertyBinder,
MetadataBuildingContext context) {
if ( joinTable != null && !isEmpty( joinTable.name() ) ) {
if ( joinTable != null && !isEmpty( joinTable.getString( "name" ) ) ) {
final Join join = propertyHolder.addJoin( joinTable, false );
// TODO: if notFoundAction!=null should we call join.disableForeignKeyCreation() ?
for ( AnnotatedJoinColumn joinColumn : joinColumns.getJoinColumns() ) {
@ -180,10 +179,16 @@ public class ToOneBinder {
final org.hibernate.mapping.ManyToOne value =
new org.hibernate.mapping.ManyToOne( context, joinColumns.getTable() );
if ( joinTable != null && isEmpty( joinTable.name() ) ) {
context.getMetadataCollector()
.addSecondPass( new ImplicitToOneJoinTableSecondPass( propertyHolder, inferredData, context,
joinColumns, joinTable, notFoundAction, value ) );
if ( joinTable != null && isEmpty( joinTable.getString( "name" ) ) ) {
context.getMetadataCollector().addSecondPass( new ImplicitToOneJoinTableSecondPass(
propertyHolder,
inferredData,
context,
joinColumns,
joinTable,
notFoundAction,
value
) );
}
if ( unique ) {
@ -191,7 +196,7 @@ public class ToOneBinder {
value.markAsLogicalOneToOne();
}
value.setReferencedEntityName( getReferenceEntityName( inferredData, targetEntity, context ) );
final XProperty property = inferredData.getProperty();
final MemberDetails property = inferredData.getAttributeMember();
defineFetchingStrategy( value, property, inferredData, propertyHolder );
//value.setFetchMode( fetchMode );
value.setNotFoundAction( notFoundAction );
@ -203,15 +208,15 @@ public class ToOneBinder {
}
}
if ( property.isAnnotationPresent( MapsId.class ) ) {
final MapsId mapsId = property.getAnnotation(MapsId.class);
if ( property.hasAnnotationUsage( MapsId.class ) ) {
final AnnotationUsage<MapsId> mapsId = property.getAnnotationUsage( MapsId.class );
final List<AnnotatedJoinColumn> joinColumnList = joinColumns.getJoinColumns();
//read only
for ( AnnotatedJoinColumn column : joinColumnList ) {
column.setInsertable( false );
column.setUpdatable( false );
}
joinColumns.setMapsId( mapsId.value() );
joinColumns.setMapsId( mapsId.getString( "value" ) );
}
boolean hasSpecjManyToOne = handleSpecjSyntax( joinColumns, inferredData, context, property );
@ -253,31 +258,31 @@ public class ToOneBinder {
);
}
static boolean isTargetAnnotatedEntity(XClass targetEntity, XProperty property, MetadataBuildingContext context) {
final XClass target = isDefault( targetEntity, context ) ? property.getType() : targetEntity;
return target.isAnnotationPresent( Entity.class );
static boolean isTargetAnnotatedEntity(ClassDetails targetEntity, MemberDetails property, MetadataBuildingContext context) {
final ClassDetails target = isDefault( targetEntity, context ) ? property.getType().determineRawClass() : targetEntity;
return target.hasAnnotationUsage( Entity.class );
}
private static boolean handleSpecjSyntax(
AnnotatedJoinColumns columns,
PropertyData inferredData,
MetadataBuildingContext context,
XProperty property) {
MemberDetails property) {
//Make sure that JPA1 key-many-to-one columns are read only too
boolean hasSpecjManyToOne = false;
if ( context.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) {
final JoinColumn joinColumn = property.getAnnotation( JoinColumn.class );
final AnnotationUsage<JoinColumn> joinColumn = property.getAnnotationUsage( JoinColumn.class );
String columnName = "";
for ( XProperty prop : inferredData.getDeclaringClass()
.getDeclaredProperties( AccessType.FIELD.getType() ) ) {
if ( prop.isAnnotationPresent( Id.class ) && prop.isAnnotationPresent( Column.class ) ) {
columnName = prop.getAnnotation( Column.class ).name();
for ( MemberDetails prop : inferredData.getDeclaringClass().getFields() ) {
if ( prop.hasAnnotationUsage( Id.class ) && prop.hasAnnotationUsage( Column.class ) ) {
columnName = prop.getAnnotationUsage( Column.class ).getString( "name" );
}
if ( property.isAnnotationPresent( ManyToOne.class ) && joinColumn != null ) {
if ( !joinColumn.name().isEmpty()
&& joinColumn.name().equals( columnName )
&& !property.isAnnotationPresent( MapsId.class ) ) {
if ( property.hasAnnotationUsage( ManyToOne.class ) && joinColumn != null ) {
final String joinColumnName = joinColumn.getString( "name" );
if ( StringHelper.isNotEmpty( joinColumnName )
&& joinColumnName.equals( columnName )
&& !property.hasAnnotationUsage( MapsId.class ) ) {
hasSpecjManyToOne = true;
for ( AnnotatedJoinColumn column : columns.getJoinColumns() ) {
column.setInsertable( false );
@ -298,7 +303,7 @@ public class ToOneBinder {
boolean isIdentifierMapper,
PropertyBinder propertyBinder,
org.hibernate.mapping.ManyToOne value,
XProperty property,
MemberDetails property,
boolean hasSpecjManyToOne,
String propertyName) {
@ -319,35 +324,34 @@ public class ToOneBinder {
propertyBinder.setColumns( columns );
propertyBinder.setAccessType( inferredData.getDefaultAccess() );
propertyBinder.setCascade( cascadeStrategy );
propertyBinder.setProperty( property );
propertyBinder.setMemberDetails( property );
propertyBinder.setToMany( true );
final JoinColumn joinColumn = property.getAnnotation( JoinColumn.class );
final JoinColumns joinColumns = property.getAnnotation( JoinColumns.class );
propertyBinder.makePropertyAndBind()
.setOptional( optional && isNullable( joinColumns, joinColumn ) );
final AnnotationUsage<JoinColumn> joinColumn = property.getSingleAnnotationUsage( JoinColumn.class );
final AnnotationUsage<JoinColumns> joinColumns = property.getAnnotationUsage( JoinColumns.class );
propertyBinder.makePropertyAndBind().setOptional( optional && isNullable( joinColumns, joinColumn ) );
}
private static boolean isNullable(JoinColumns joinColumns, JoinColumn joinColumn) {
private static boolean isNullable(AnnotationUsage<JoinColumns> joinColumns, AnnotationUsage<JoinColumn> joinColumn) {
if ( joinColumn != null ) {
return joinColumn.nullable();
return joinColumn.getBoolean( "nullable" );
}
else if ( joinColumns != null ) {
for ( JoinColumn column : joinColumns.value() ) {
if ( column.nullable() ) {
if ( joinColumns != null ) {
for ( AnnotationUsage<JoinColumn> column : joinColumns.<AnnotationUsage<JoinColumn>>getList( "value" ) ) {
if ( column.getBoolean( "nullable" ) ) {
return true;
}
}
return false;
}
else {
return true;
}
return true;
}
static void defineFetchingStrategy(
ToOne toOne,
XProperty property,
MemberDetails property,
PropertyData inferredData,
PropertyHolder propertyHolder) {
handleLazy( toOne, property, inferredData, propertyHolder );
@ -355,8 +359,8 @@ public class ToOneBinder {
handleFetchProfileOverrides( toOne, property, propertyHolder, inferredData );
}
private static void handleLazy(ToOne toOne, XProperty property, PropertyData inferredData, PropertyHolder propertyHolder) {
if ( property.isAnnotationPresent( NotFound.class ) ) {
private static void handleLazy(ToOne toOne, MemberDetails property, PropertyData inferredData, PropertyHolder propertyHolder) {
if ( property.hasAnnotationUsage( NotFound.class ) ) {
toOne.setLazy( false );
toOne.setUnwrapProxy( true );
}
@ -370,33 +374,28 @@ public class ToOneBinder {
private static void handleFetchProfileOverrides(
ToOne toOne,
XProperty property,
MemberDetails property,
PropertyHolder propertyHolder,
PropertyData inferredData) {
final MetadataBuildingContext context = toOne.getBuildingContext();
final InFlightMetadataCollector collector = context.getMetadataCollector();
if ( property.isAnnotationPresent( FetchProfileOverride.class ) ) {
final FetchProfileOverride fetch = property.getAnnotation( FetchProfileOverride.class );
collector.addSecondPass( new FetchSecondPass( fetch, propertyHolder, inferredData.getPropertyName(), context ) );
}
else if ( property.isAnnotationPresent( FetchProfileOverrides.class ) ) {
for ( FetchProfileOverride fetch: property.getAnnotation( FetchProfileOverrides.class ).value() ) {
collector.addSecondPass( new FetchSecondPass( fetch, propertyHolder, inferredData.getPropertyName(), context ) );
}
}
property.forEachAnnotationUsage( FetchProfileOverride.class, (usage) -> {
collector.addSecondPass( new FetchSecondPass( usage, propertyHolder, inferredData.getPropertyName(), context ) );
} );
}
private static void handleFetch(ToOne toOne, XProperty property) {
if ( property.isAnnotationPresent( Fetch.class ) ) {
private static void handleFetch(ToOne toOne, MemberDetails property) {
final AnnotationUsage<Fetch> fetchAnnotationUsage = property.getAnnotationUsage( Fetch.class );
if ( fetchAnnotationUsage != null ) {
// Hibernate @Fetch annotation takes precedence
setHibernateFetchMode( toOne, property, property.getAnnotation( Fetch.class ).value() );
setHibernateFetchMode( toOne, property, fetchAnnotationUsage.getEnum( "value" ) );
}
else {
toOne.setFetchMode( getFetchMode( getJpaFetchType( property ) ) );
}
}
private static void setHibernateFetchMode(ToOne toOne, XProperty property, org.hibernate.annotations.FetchMode fetchMode) {
private static void setHibernateFetchMode(ToOne toOne, MemberDetails property, org.hibernate.annotations.FetchMode fetchMode) {
switch ( fetchMode ) {
case JOIN:
toOne.setFetchMode( FetchMode.JOIN );
@ -414,12 +413,13 @@ public class ToOneBinder {
}
}
private static boolean isEager(XProperty property, PropertyData inferredData, PropertyHolder propertyHolder) {
private static boolean isEager(MemberDetails property, PropertyData inferredData, PropertyHolder propertyHolder) {
final FetchType fetchType = getJpaFetchType( property );
if ( property.isAnnotationPresent( LazyToOne.class ) ) {
// LazyToOne takes precedent
final LazyToOne lazy = property.getAnnotation( LazyToOne.class );
boolean eager = lazy.value() == LazyToOneOption.FALSE;
final AnnotationUsage<LazyToOne> lazyToOneAnnotationUsage = property.getAnnotationUsage( LazyToOne.class );
if ( lazyToOneAnnotationUsage != null ) {
final LazyToOneOption option = lazyToOneAnnotationUsage.getEnum( "value" );
boolean eager = option == LazyToOneOption.FALSE;
if ( eager && fetchType == LAZY ) {
// conflicts with non-default setting
throw new AnnotationException("Association '" + getPath(propertyHolder, inferredData)
@ -432,14 +432,14 @@ public class ToOneBinder {
}
}
private static FetchType getJpaFetchType(XProperty property) {
final ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
final OneToOne oneToOne = property.getAnnotation( OneToOne.class );
private static FetchType getJpaFetchType(MemberDetails property) {
final AnnotationUsage<ManyToOne> manyToOne = property.getAnnotationUsage( ManyToOne.class );
final AnnotationUsage<OneToOne> oneToOne = property.getAnnotationUsage( OneToOne.class );
if ( manyToOne != null ) {
return manyToOne.fetch();
return manyToOne.getEnum( "fetch" );
}
else if ( oneToOne != null ) {
return oneToOne.fetch();
return oneToOne.getEnum( "fetch" );
}
else {
throw new AssertionFailure("Define fetch strategy on a property not annotated with @OneToMany nor @OneToOne");
@ -452,15 +452,15 @@ public class ToOneBinder {
boolean isIdentifierMapper,
boolean inSecondPass,
MetadataBuildingContext context,
XProperty property,
MemberDetails property,
AnnotatedJoinColumns joinColumns,
PropertyBinder propertyBinder,
boolean forcePersist) {
final OneToOne oneToOne = property.getAnnotation( OneToOne.class );
final AnnotationUsage<OneToOne> oneToOne = property.getAnnotationUsage( OneToOne.class );
//check validity
if ( property.isAnnotationPresent( Column.class )
|| property.isAnnotationPresent( Columns.class ) ) {
if ( property.hasAnnotationUsage( Column.class )
|| property.hasAnnotationUsage( Columns.class ) ) {
throw new AnnotationException(
"Property '" + getPath( propertyHolder, inferredData )
+ "' is a '@OneToOne' association and may not use '@Column' to specify column mappings"
@ -476,28 +476,28 @@ public class ToOneBinder {
}
//FIXME support a proper PKJCs
final boolean trueOneToOne = property.isAnnotationPresent( PrimaryKeyJoinColumn.class )
|| property.isAnnotationPresent( PrimaryKeyJoinColumns.class );
final Cascade hibernateCascade = property.getAnnotation( Cascade.class );
final NotFound notFound = property.getAnnotation( NotFound.class );
final NotFoundAction notFoundAction = notFound == null ? null : notFound.action();
final boolean trueOneToOne = property.hasAnnotationUsage( PrimaryKeyJoinColumn.class )
|| property.hasAnnotationUsage( PrimaryKeyJoinColumns.class );
final AnnotationUsage<Cascade> hibernateCascade = property.getAnnotationUsage( Cascade.class );
final AnnotationUsage<NotFound> notFound = property.getAnnotationUsage( NotFound.class );
final NotFoundAction notFoundAction = notFound == null ? null : notFound.getEnum( "action" );
matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), notFoundAction, oneToOne.fetch() );
final OnDelete onDelete = property.getAnnotation( OnDelete.class );
final JoinTable joinTable = propertyHolder.getJoinTable(property);
matchIgnoreNotFoundWithFetchType( propertyHolder.getEntityName(), property.getName(), notFoundAction, oneToOne.getEnum( "fetch" ) );
final AnnotationUsage<OnDelete> onDelete = property.getAnnotationUsage( OnDelete.class );
final AnnotationUsage<JoinTable> joinTable = propertyHolder.getJoinTable( property );
bindOneToOne(
getCascadeStrategy( oneToOne.cascade(), hibernateCascade, oneToOne.orphanRemoval(), forcePersist ),
getCascadeStrategy( oneToOne.getList( "cascade" ), hibernateCascade, oneToOne.getBoolean( "orphanRemoval" ), forcePersist ),
joinColumns,
joinTable,
!isMandatory( oneToOne.optional(), property, notFoundAction ),
getFetchMode( oneToOne.fetch() ),
!isMandatory( oneToOne.getBoolean( "optional" ), property, notFoundAction ),
getFetchMode( oneToOne.getEnum( "fetch" ) ),
notFoundAction,
onDelete == null ? null : onDelete.action(),
onDelete == null ? null : onDelete.getEnum( "action" ),
getTargetEntity( inferredData, context ),
property,
propertyHolder,
inferredData,
nullIfEmpty( oneToOne.mappedBy() ),
nullIfEmpty( oneToOne.getString( "mappedBy" ) ),
trueOneToOne,
isIdentifierMapper,
inSecondPass,
@ -509,13 +509,13 @@ public class ToOneBinder {
private static void bindOneToOne(
String cascadeStrategy,
AnnotatedJoinColumns joinColumns,
JoinTable joinTable,
AnnotationUsage<JoinTable> joinTable,
boolean optional,
FetchMode fetchMode,
NotFoundAction notFoundAction,
OnDeleteAction cascadeOnDelete,
XClass targetEntity,
XProperty annotatedProperty,
ClassDetails targetEntity,
MemberDetails annotatedProperty,
PropertyHolder propertyHolder,
PropertyData inferredData,
String mappedBy,
@ -607,77 +607,75 @@ public class ToOneBinder {
public static void bindForeignKeyNameAndDefinition(
SimpleValue value,
XProperty property,
ForeignKey foreignKey,
MemberDetails property,
AnnotationUsage<ForeignKey> foreignKey,
MetadataBuildingContext context) {
if ( property.getAnnotation( NotFound.class ) != null ) {
if ( property.hasAnnotationUsage( NotFound.class ) ) {
// supersedes all others
value.disableForeignKey();
}
else {
final JoinColumn joinColumn = property.getAnnotation( JoinColumn.class );
final JoinColumns joinColumns = property.getAnnotation( JoinColumns.class );
final AnnotationUsage<JoinColumn> joinColumn = property.getSingleAnnotationUsage( JoinColumn.class );
final AnnotationUsage<JoinColumns> joinColumns = property.getAnnotationUsage( JoinColumns.class );
final boolean noConstraintByDefault = context.getBuildingOptions().isNoConstraintByDefault();
if ( joinColumn != null && noConstraint( joinColumn.foreignKey(), noConstraintByDefault )
|| joinColumns != null && noConstraint( joinColumns.foreignKey(), noConstraintByDefault ) ) {
if ( joinColumn != null && noConstraint( joinColumn.getNestedUsage( "foreignKey" ), noConstraintByDefault )
|| joinColumns != null && noConstraint( joinColumns.getNestedUsage( "foreignKey" ), noConstraintByDefault ) ) {
value.disableForeignKey();
}
else {
final org.hibernate.annotations.ForeignKey fk =
property.getAnnotation( org.hibernate.annotations.ForeignKey.class );
if ( fk != null && isNotEmpty( fk.name() ) ) {
value.setForeignKeyName( fk.name() );
final AnnotationUsage<org.hibernate.annotations.ForeignKey> fk =
property.getAnnotationUsage( org.hibernate.annotations.ForeignKey.class );
if ( fk != null && isNotEmpty( fk.getString( "name" ) ) ) {
value.setForeignKeyName( fk.getString( "name" ) );
}
else {
if ( noConstraint( foreignKey, noConstraintByDefault ) ) {
value.disableForeignKey();
}
else if ( foreignKey != null ) {
value.setForeignKeyName( nullIfEmpty( foreignKey.name() ) );
value.setForeignKeyDefinition( nullIfEmpty( foreignKey.foreignKeyDefinition() ) );
value.setForeignKeyName( nullIfEmpty( foreignKey.getString( "name" ) ) );
value.setForeignKeyDefinition( nullIfEmpty( foreignKey.getString( "foreignKeyDefinition" ) ) );
}
else if ( noConstraintByDefault ) {
value.disableForeignKey();
}
else if ( joinColumns != null ) {
value.setForeignKeyName( nullIfEmpty( joinColumns.foreignKey().name() ) );
value.setForeignKeyDefinition( nullIfEmpty( joinColumns.foreignKey().foreignKeyDefinition() ) );
final AnnotationUsage<ForeignKey> joinColumnsForeignKey = joinColumns.getNestedUsage( "foreignKey" );
value.setForeignKeyName( nullIfEmpty( joinColumnsForeignKey.getString( "name" ) ) );
value.setForeignKeyDefinition( nullIfEmpty( joinColumnsForeignKey.getString( "foreignKeyDefinition" ) ) );
}
else if ( joinColumn != null ) {
value.setForeignKeyName( nullIfEmpty( joinColumn.foreignKey().name() ) );
value.setForeignKeyDefinition( nullIfEmpty( joinColumn.foreignKey().foreignKeyDefinition() ) );
final AnnotationUsage<ForeignKey> joinColumnForeignKey = joinColumn.getNestedUsage( "foreignKey" );
value.setForeignKeyName( nullIfEmpty( joinColumnForeignKey.getString( "name" ) ) );
value.setForeignKeyDefinition( nullIfEmpty( joinColumnForeignKey.getString( "foreignKeyDefinition" ) ) );
}
}
}
}
}
public static String getReferenceEntityName(PropertyData propertyData, XClass targetEntity, MetadataBuildingContext context) {
public static String getReferenceEntityName(PropertyData propertyData, ClassDetails targetEntity, MetadataBuildingContext context) {
return isDefault( targetEntity, context )
? propertyData.getClassOrElementName()
: targetEntity.getName();
}
public static String getReferenceEntityName(PropertyData propertyData, MetadataBuildingContext context) {
final XClass targetEntity = getTargetEntity( propertyData, context );
return isDefault( targetEntity, context )
? propertyData.getClassOrElementName()
: targetEntity.getName();
return getReferenceEntityName( propertyData, getTargetEntity( propertyData, context ), context );
}
public static XClass getTargetEntity(PropertyData propertyData, MetadataBuildingContext context) {
return context.getBootstrapContext().getReflectionManager()
.toXClass( getTargetEntityClass( propertyData.getProperty() ) );
public static ClassDetails getTargetEntity(PropertyData propertyData, MetadataBuildingContext context) {
return getTargetEntityClass( propertyData.getAttributeMember() );
}
private static Class<?> getTargetEntityClass(XProperty property) {
final ManyToOne manyToOne = property.getAnnotation( ManyToOne.class );
private static ClassDetails getTargetEntityClass(MemberDetails property) {
final AnnotationUsage<ManyToOne> manyToOne = property.getAnnotationUsage( ManyToOne.class );
if ( manyToOne != null ) {
return manyToOne.targetEntity();
return manyToOne.getClassDetails( "targetEntity" );
}
final OneToOne oneToOne = property.getAnnotation( OneToOne.class );
final AnnotationUsage<OneToOne> oneToOne = property.getAnnotationUsage( OneToOne.class );
if ( oneToOne != null ) {
return oneToOne.targetEntity();
return oneToOne.getClassDetails( "targetEntity" );
}
throw new AssertionFailure( "Unexpected discovery of a targetEntity: " + property.getName() );
}

View File

@ -7,11 +7,12 @@
package org.hibernate.boot.model.internal;
import org.hibernate.MappingException;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.annotations.common.reflection.XProperty;
import org.hibernate.boot.spi.AccessType;
import org.hibernate.boot.spi.PropertyData;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.models.spi.TypeDetails;
/**
* @author Emmanuel Bernard
@ -26,8 +27,8 @@ public class WrappedInferredData implements PropertyData {
}
@Override
public XClass getClassOrElement() throws MappingException {
return wrappedInferredData.getClassOrElement();
public TypeDetails getClassOrElementType() throws MappingException {
return wrappedInferredData.getClassOrElementType();
}
@Override
@ -46,18 +47,18 @@ public class WrappedInferredData implements PropertyData {
}
@Override
public XProperty getProperty() {
return wrappedInferredData.getProperty();
public MemberDetails getAttributeMember() {
return wrappedInferredData.getAttributeMember();
}
@Override
public XClass getDeclaringClass() {
public ClassDetails getDeclaringClass() {
return wrappedInferredData.getDeclaringClass();
}
@Override
public XClass getPropertyClass() throws MappingException {
return wrappedInferredData.getPropertyClass();
public TypeDetails getPropertyType() throws MappingException {
return wrappedInferredData.getPropertyType();
}
@Override

View File

@ -49,10 +49,8 @@ import org.hibernate.boot.model.source.internal.hbm.HbmMetadataSourceProcessorIm
import org.hibernate.boot.model.source.internal.hbm.MappingDocument;
import org.hibernate.boot.model.source.internal.hbm.ModelBinder;
import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
import org.hibernate.boot.models.categorize.internal.ClassLoaderServiceLoading;
import org.hibernate.boot.models.categorize.internal.DomainModelCategorizationCollector;
import org.hibernate.boot.models.categorize.internal.OrmAnnotationHelper;
import org.hibernate.boot.models.categorize.spi.ManagedResourcesProcessor;
import org.hibernate.boot.models.xml.spi.XmlPreProcessingResult;
import org.hibernate.boot.models.xml.spi.XmlPreProcessor;
import org.hibernate.boot.models.xml.spi.XmlProcessingResult;
@ -75,7 +73,6 @@ import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.config.spi.StandardConverters;
import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.mapping.Table;
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;
@ -194,14 +191,17 @@ public class MetadataBuildingProcess {
final ManagedResources managedResources,
final BootstrapContext bootstrapContext,
final MetadataBuildingOptions options) {
final InFlightMetadataCollectorImpl metadataCollector = new InFlightMetadataCollectorImpl(
bootstrapContext,
options
);
final ClassLoaderService classLoaderService = bootstrapContext.getServiceRegistry().getService( ClassLoaderService.class );
final InFlightMetadataCollectorImpl metadataCollector = new InFlightMetadataCollectorImpl( bootstrapContext, options );
handleTypes( bootstrapContext, options, metadataCollector );
final DomainModelSource domainModelSource = processManagedResources( managedResources, bootstrapContext );
final DomainModelSource domainModelSource = processManagedResources(
managedResources,
metadataCollector,
bootstrapContext
);
final MetadataBuildingContextRootImpl rootMetadataBuildingContext = new MetadataBuildingContextRootImpl(
"orm",
@ -218,14 +218,12 @@ public class MetadataBuildingProcess {
// Set up the processors and start binding
// NOTE : this becomes even more simplified after we move purely
// to unified model
final ClassLoaderService classLoaderService = options.getServiceRegistry().getService( ClassLoaderService.class );
final IndexView jandexView = domainModelSource.getJandexIndex();
final MetadataSourceProcessor processor = new MetadataSourceProcessor() {
private final MetadataSourceProcessor hbmProcessor =
options.isXmlMappingEnabled()
? new HbmMetadataSourceProcessorImpl( managedResources, rootMetadataBuildingContext )
: new NoOpMetadataSourceProcessorImpl();
private final MetadataSourceProcessor hbmProcessor = options.isXmlMappingEnabled()
? new HbmMetadataSourceProcessorImpl( managedResources, rootMetadataBuildingContext )
: new NoOpMetadataSourceProcessorImpl();
private final AnnotationMetadataSourceProcessorImpl annotationProcessor = new AnnotationMetadataSourceProcessorImpl(
managedResources,
@ -375,11 +373,8 @@ public class MetadataBuildingProcess {
@Internal
public static DomainModelSource processManagedResources(
ManagedResources managedResources,
InFlightMetadataCollector metadataCollector,
BootstrapContext bootstrapContext) {
final ClassLoaderService classLoaderService = bootstrapContext.getServiceRegistry().getService( ClassLoaderService.class );
final ClassLoaderServiceLoading classLoading = new ClassLoaderServiceLoading( classLoaderService );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// - pre-process the XML
// - collect all known classes
@ -396,6 +391,7 @@ public class MetadataBuildingProcess {
// - allKnownClassNames (technically could be included in xmlPreProcessingResult)
// - sourceModelBuildingContext
final SourceModelBuildingContext sourceModelBuildingContext = metadataCollector.getSourceModelBuildingContext();
final XmlPreProcessingResult xmlPreProcessingResult = XmlPreProcessor.preProcessXmlResources( managedResources );
//noinspection unchecked
@ -406,7 +402,7 @@ public class MetadataBuildingProcess {
);
managedResources.getAnnotatedPackageNames().forEach( (packageName) -> {
try {
final Class<?> packageInfoClass = classLoading.classForName( packageName + ".package-info" );
final Class<?> packageInfoClass = sourceModelBuildingContext.getClassLoading().classForName( packageName + ".package-info" );
allKnownClassNames.add( packageInfoClass.getName() );
}
catch (ClassLoadingException classLoadingException) {
@ -417,13 +413,7 @@ public class MetadataBuildingProcess {
// 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
);
final IndexView jandexIndex = resolveJandexIndex( allKnownClassNames, bootstrapContext.getJandexView(), sourceModelBuildingContext.getClassLoading() );
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// - process metadata-complete XML
@ -449,6 +439,7 @@ public class MetadataBuildingProcess {
areIdGeneratorsGlobal,
classDetailsRegistry,
descriptorRegistry,
metadataCollector.getGlobalRegistrations(),
jandexIndex
);
@ -473,6 +464,7 @@ public class MetadataBuildingProcess {
return new DomainModelSource(
classDetailsRegistry.makeImmutableCopy(),
jandexIndex,
allKnownClassNames,
modelCategorizationCollector.getGlobalRegistrations(),
xmlPreProcessingResult.getPersistenceUnitMetadata()
);
@ -495,9 +487,10 @@ public class MetadataBuildingProcess {
ClassDetailsRegistry classDetailsRegistry,
DomainModelCategorizationCollector modelCategorizationCollector) {
modelCategorizationCollector.apply( classDetails );
if ( classDetails.getSuperType() != null ) {
if ( categorizedClassNames.add( classDetails.getSuperType().getClassName() ) ) {
applyKnownClass( classDetails.getSuperType(), categorizedClassNames, classDetailsRegistry, modelCategorizationCollector );
if ( classDetails.getSuperClass() != null
&& classDetails.getSuperClass() != ClassDetails.OBJECT_CLASS_DETAILS ) {
if ( categorizedClassNames.add( classDetails.getSuperClass().getClassName() ) ) {
applyKnownClass( classDetails.getSuperClass(), categorizedClassNames, classDetailsRegistry, modelCategorizationCollector );
}
}
}

View File

@ -13,9 +13,6 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.annotations.common.reflection.MetadataProviderInjector;
import org.hibernate.annotations.common.reflection.ReflectionManager;
import org.hibernate.annotations.common.reflection.XClass;
import org.hibernate.boot.internal.MetadataBuildingContextRootImpl;
import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityMappingsImpl;
@ -25,22 +22,19 @@ import org.hibernate.boot.model.convert.spi.ConverterRegistry;
import org.hibernate.boot.model.convert.spi.RegisteredConversion;
import org.hibernate.boot.model.internal.AnnotationBinder;
import org.hibernate.boot.model.internal.InheritanceState;
import org.hibernate.boot.model.internal.JPAXMLOverriddenMetadataProvider;
import org.hibernate.boot.model.process.spi.ManagedResources;
import org.hibernate.boot.model.process.spi.MetadataBuildingProcess;
import org.hibernate.boot.model.source.spi.MetadataSourceProcessor;
import org.hibernate.boot.models.categorize.spi.FilterDefRegistration;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.JpaOrmXmlPersistenceUnitDefaultAware;
import org.hibernate.boot.spi.JpaOrmXmlPersistenceUnitDefaultAware.JpaOrmXmlPersistenceUnitDefaults;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataBuildingOptions;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.JdbcMapping;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.type.descriptor.java.JavaType;
import org.hibernate.usertype.UserType;
import org.jboss.logging.Logger;
@ -52,11 +46,10 @@ import jakarta.persistence.MappedSuperclass;
import static org.hibernate.boot.jaxb.SourceType.OTHER;
import static org.hibernate.boot.model.internal.AnnotationBinder.resolveAttributeConverter;
import static org.hibernate.boot.model.internal.AnnotationBinder.resolveBasicType;
import static org.hibernate.boot.model.internal.AnnotationBinder.resolveFilterParamType;
import static org.hibernate.boot.model.internal.AnnotationBinder.resolveJavaType;
import static org.hibernate.boot.model.internal.AnnotationBinder.resolveUserType;
import static org.hibernate.models.internal.jdk.VoidClassDetails.VOID_CLASS_DETAILS;
import static org.hibernate.models.internal.jdk.VoidClassDetails.VOID_OBJECT_CLASS_DETAILS;
import static org.hibernate.models.spi.ClassDetails.VOID_CLASS_DETAILS;
import static org.hibernate.models.spi.ClassDetails.VOID_OBJECT_CLASS_DETAILS;
/**
* @author Steve Ebersole
@ -73,9 +66,8 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
private final MetadataBuildingContextRootImpl rootMetadataBuildingContext;
private final ClassLoaderService classLoaderService;
private final ReflectionManager reflectionManager;
private final LinkedHashSet<String> annotatedPackages = new LinkedHashSet<>();
private final List<XClass> xClasses = new ArrayList<>();
private final LinkedHashSet<ClassDetails> knownClasses = new LinkedHashSet<>();
/**
* Normal constructor used while processing {@linkplain org.hibernate.boot.MetadataSources mapping sources}
@ -87,10 +79,9 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
this.domainModelSource = domainModelSource;
this.rootMetadataBuildingContext = rootMetadataBuildingContext;
this.reflectionManager = rootMetadataBuildingContext.getBootstrapContext().getReflectionManager();
final MetadataBuildingOptions metadataBuildingOptions = rootMetadataBuildingContext.getBuildingOptions();
this.classLoaderService = metadataBuildingOptions.getServiceRegistry().getService( ClassLoaderService.class );
assert classLoaderService != null;
final ConverterRegistry converterRegistry = rootMetadataBuildingContext.getMetadataCollector().getConverterRegistry();
domainModelSource.getConversionRegistrations().forEach( (registration) -> {
@ -116,45 +107,19 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
) );
} );
if ( metadataBuildingOptions.isXmlMappingEnabled() ) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Ewww. This is temporary until we migrate to Jandex + StAX for annotation binding
final JPAXMLOverriddenMetadataProvider jpaMetadataProvider = (JPAXMLOverriddenMetadataProvider)
( (MetadataProviderInjector) reflectionManager ).getMetadataProvider();
for ( Binding<?> xmlBinding : managedResources.getXmlMappingBindings() ) {
Object root = xmlBinding.getRoot();
if ( !( root instanceof JaxbEntityMappingsImpl ) ) {
continue;
}
final JaxbEntityMappingsImpl entityMappings = (JaxbEntityMappingsImpl) xmlBinding.getRoot();
final List<String> classNames = jpaMetadataProvider.getXMLContext().addDocument( entityMappings );
for ( String className : classNames ) {
xClasses.add( toXClass( className ) );
}
}
}
applyManagedClasses( domainModelSource, knownClasses, rootMetadataBuildingContext );
for ( String className : managedResources.getAnnotatedClassNames() ) {
final Class<?> annotatedClass = classLoaderService.classForName( className );
xClasses.add( toXClass( annotatedClass ) );
knownClasses.add( domainModelSource.getClassDetailsRegistry().resolveClassDetails( className ) );
}
for ( Class<?> annotatedClass : managedResources.getAnnotatedClassReferences() ) {
xClasses.add( toXClass( annotatedClass ) );
knownClasses.add( domainModelSource.getClassDetailsRegistry().resolveClassDetails( annotatedClass.getName() ) );
}
annotatedPackages.addAll( managedResources.getAnnotatedPackageNames() );
}
private XClass toXClass(String className) {
return reflectionManager.toXClass( classLoaderService.classForName( className ) );
}
private XClass toXClass(Class<?> classRef) {
return reflectionManager.toXClass( classRef );
}
/**
* Used as part of processing
* {@linkplain org.hibernate.boot.spi.AdditionalMappingContributions#contributeEntity(Class) "additional" mappings}
@ -172,6 +137,7 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
final ManagedResources mr = mrBuilder.build();
final DomainModelSource additionalDomainModelSource = MetadataBuildingProcess.processManagedResources(
mr,
rootMetadataBuildingContext.getMetadataCollector(),
rootMetadataBuildingContext.getBootstrapContext()
);
final AnnotationMetadataSourceProcessorImpl processor = new AnnotationMetadataSourceProcessorImpl( mr, additionalDomainModelSource, rootMetadataBuildingContext );
@ -181,27 +147,7 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
@Override
public void prepare() {
// use any persistence-unit-defaults defined in orm.xml
// todo : invert this to use the PersistenceUnitMetadata directly (defaulting to the settings)
( (JpaOrmXmlPersistenceUnitDefaultAware) rootMetadataBuildingContext.getBuildingOptions() ).apply(
new JpaOrmXmlPersistenceUnitDefaults() {
final PersistenceUnitMetadata persistenceUnitMetadata = domainModelSource.getPersistenceUnitMetadata();
@Override
public String getDefaultSchemaName() {
return StringHelper.nullIfEmpty( persistenceUnitMetadata.getDefaultSchema() );
}
@Override
public String getDefaultCatalogName() {
return StringHelper.nullIfEmpty( persistenceUnitMetadata.getDefaultCatalog() );
}
@Override
public boolean shouldImplicitlyQuoteIdentifiers() {
return persistenceUnitMetadata.useQuotedIdentifiers();
}
}
);
( (JpaOrmXmlPersistenceUnitDefaultAware) rootMetadataBuildingContext.getBuildingOptions() ).apply( domainModelSource.getPersistenceUnitMetadata() );
rootMetadataBuildingContext.getMetadataCollector().getDatabase().adjustDefaultNamespace(
rootMetadataBuildingContext.getBuildingOptions().getMappingDefaults().getImplicitCatalogName(),
@ -292,13 +238,13 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
@Override
public void processEntityHierarchies(Set<String> processedEntityNames) {
final List<XClass> orderedClasses = orderAndFillHierarchy( xClasses );
Map<XClass, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
final List<ClassDetails> orderedClasses = orderAndFillHierarchy( knownClasses );
Map<ClassDetails, InheritanceState> inheritanceStatePerClass = AnnotationBinder.buildInheritanceStates(
orderedClasses,
rootMetadataBuildingContext
);
for ( XClass clazz : orderedClasses ) {
for ( ClassDetails clazz : orderedClasses ) {
if ( processedEntityNames.contains( clazz.getName() ) ) {
log.debugf( "Skipping annotated class processing of entity [%s], as it has already been processed", clazz );
}
@ -310,17 +256,29 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
}
}
/**
* @return a partially ordered list so entry's ancestors always show up earlier
*/
private List<XClass> orderAndFillHierarchy(List<XClass> classes) {
private List<ClassDetails> orderAndFillHierarchy(LinkedHashSet<ClassDetails> original) {
LinkedHashSet<ClassDetails> copy = new LinkedHashSet<>( original.size() );
insertMappedSuperclasses( original, copy );
// order the hierarchy
List<ClassDetails> workingCopy = new ArrayList<>( copy );
List<ClassDetails> newList = new ArrayList<>( copy.size() );
while ( !workingCopy.isEmpty() ) {
ClassDetails clazz = workingCopy.get( 0 );
orderHierarchy( workingCopy, newList, copy, clazz );
}
return newList;
}
private void insertMappedSuperclasses(LinkedHashSet<ClassDetails> original, LinkedHashSet<ClassDetails> copy) {
final boolean debug = log.isDebugEnabled();
LinkedHashSet<XClass> orderedClasses = CollectionHelper.linkedSetOfSize( classes.size() * 2 );
List<XClass> clazzHierarchy = new ArrayList<>();
for ( XClass clazz : classes ) {
if ( clazz.isAnnotationPresent( MappedSuperclass.class ) ) {
for ( ClassDetails clazz : classes ) {
if ( clazz.hasAnnotationUsage( MappedSuperclass.class ) ) {
if ( debug ) {
log.debugf(
"Skipping explicit MappedSuperclass %s, the class will be discovered analyzing the implementing class",
@ -336,20 +294,29 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
clazzHierarchy.clear();
clazzHierarchy.add( clazz );
XClass superClass = clazz.getSuperclass();
ClassDetails superClass = clazz.getSuperClass();
while ( superClass != null
&& !reflectionManager.equals( superClass, Object.class ) ) {
if ( superClass.isAnnotationPresent( Entity.class )
|| superClass.isAnnotationPresent( MappedSuperclass.class ) ) {
&& !Object.class.getName().equals( superClass.getName() ) ) {
if ( superClass.hasAnnotationUsage( Entity.class )
|| superClass.hasAnnotationUsage( MappedSuperclass.class ) ) {
if ( orderedClasses.contains( superClass ) ) {
break;
}
clazzHierarchy.add( superClass );
}
superClass = superClass.getSuperclass();
superClass = superClass.getSuperClass();
}
for (int i = clazzHierarchy.size() - 1; i >= 0; i-- ) {
orderedClasses.add( clazzHierarchy.get(i) );
}
}
}
private void orderHierarchy(List<ClassDetails> copy, List<ClassDetails> newList, LinkedHashSet<ClassDetails> original, ClassDetails clazz) {
if ( clazz != null && !Object.class.getName().equals( clazz.getName() ) ) {
//process superclass first
orderHierarchy( copy, newList, original, clazz.getSuperClass() );
if ( original.contains( clazz ) ) {
if ( !newList.contains( clazz ) ) {
newList.add( clazz );
}
}
}
@ -370,4 +337,14 @@ public class AnnotationMetadataSourceProcessorImpl implements MetadataSourceProc
@Override
public void finishUp() {
}
private static void applyManagedClasses(
DomainModelSource domainModelSource,
LinkedHashSet<ClassDetails> knownClasses,
MetadataBuildingContextRootImpl rootMetadataBuildingContext) {
final ClassDetailsRegistry classDetailsRegistry = domainModelSource.getClassDetailsRegistry();
domainModelSource.getManagedClassNames().forEach( (className) -> {
knownClasses.add( classDetailsRegistry.resolveClassDetails( className ) );
} );
}
}

View File

@ -6,7 +6,6 @@
*/
package org.hibernate.boot.model.source.internal.annotations;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@ -14,6 +13,7 @@ import org.hibernate.boot.models.categorize.spi.ConversionRegistration;
import org.hibernate.boot.models.categorize.spi.ConverterRegistration;
import org.hibernate.boot.models.categorize.spi.GlobalRegistrations;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.jboss.jandex.IndexView;
@ -24,18 +24,21 @@ import org.jboss.jandex.IndexView;
public class DomainModelSource {
private final ClassDetailsRegistry classDetailsRegistry;
private final IndexView jandexIndex;
private final GlobalRegistrations globalRegistrations;
private final PersistenceUnitMetadata persistenceUnitMetadata;
private final GlobalRegistrations globalRegistrations;
private final List<String> allKnownClassNames;
public DomainModelSource(
ClassDetailsRegistry classDetailsRegistry,
IndexView jandexIndex,
List<String> allKnownClassNames,
GlobalRegistrations globalRegistrations,
PersistenceUnitMetadata persistenceUnitMetadata) {
this.classDetailsRegistry = classDetailsRegistry;
this.jandexIndex = jandexIndex;
this.globalRegistrations = globalRegistrations;
this.persistenceUnitMetadata = persistenceUnitMetadata;
this.globalRegistrations = globalRegistrations;
this.allKnownClassNames = allKnownClassNames;
}
public ClassDetailsRegistry getClassDetailsRegistry() {
@ -46,6 +49,10 @@ public class DomainModelSource {
return jandexIndex;
}
public PersistenceUnitMetadata getPersistenceUnitMetadata() {
return persistenceUnitMetadata;
}
public GlobalRegistrations getGlobalRegistrations() {
return globalRegistrations;
}
@ -58,7 +65,7 @@ public class DomainModelSource {
return globalRegistrations.getJpaConverters();
}
public PersistenceUnitMetadata getPersistenceUnitMetadata() {
return persistenceUnitMetadata;
public List<String> getManagedClassNames() {
return allKnownClassNames;
}
}

View File

@ -442,27 +442,6 @@ public class ModelBinder {
entityDescriptor.setBatchSize( entitySource.getBatchSize() );
entityDescriptor.setSelectBeforeUpdate( entitySource.isSelectBeforeUpdate() );
if ( isNotEmpty( entitySource.getCustomPersisterClassName() ) ) {
try {
entityDescriptor.setEntityPersisterClass(
sourceDocument.getBootstrapContext()
.getClassLoaderAccess()
.classForName( entitySource.getCustomPersisterClassName() )
);
}
catch (ClassLoadingException e) {
throw new MappingException(
String.format(
Locale.ENGLISH,
"Unable to load specified persister class : %s",
entitySource.getCustomPersisterClassName()
),
e,
sourceDocument.getOrigin()
);
}
}
bindCustomSql( entitySource, entityDescriptor );
final JdbcEnvironment jdbcEnvironment = sourceDocument.getMetadataCollector().getDatabase().getJdbcEnvironment();
@ -1418,10 +1397,10 @@ public class ModelBinder {
binding.setOptimisticLocked( source.isIncludedInOptimisticLocking() );
if ( source.getCustomPersisterClassName() != null ) {
binding.setCollectionPersisterClass(
mappingDocument.getBootstrapContext().getClassLoaderAccess().classForName(
mappingDocument.qualifyClassName( source.getCustomPersisterClassName() )
)
DEPRECATION_LOGGER.debugf(
"Custom CollectionPersister impls are no longer supported - %s (%s)",
binding.getRole(),
mappingDocument.getOrigin().getName()
);
}

View File

@ -21,13 +21,17 @@ import org.hibernate.models.spi.AnnotationDescriptor;
import static org.hibernate.models.internal.AnnotationHelper.createOrmDescriptor;
/**
* Details about Hibernate annotations
* Details about Hibernate annotations.
*
* @implNote Suppressed for deprecation because we refer to many deprecated annotations
* @apiNote Here we only collect "stateless" annotations - namely those where we do not care about
* meta-annotations, which is the vast majority.
*
* @implNote Suppressed for deprecation and removal because we refer to many deprecated annotations; suppressed
* for unused because
*
* @author Steve Ebersole
*/
@SuppressWarnings("deprecation")
@SuppressWarnings({ "deprecation", "removal", "unused" })
public interface HibernateAnnotations {
AnnotationDescriptor<Any> ANY = createOrmDescriptor( Any.class );
AnnotationDescriptor<AnyDiscriminator> ANY_DISCRIMINATOR = createOrmDescriptor( AnyDiscriminator.class );
@ -159,7 +163,6 @@ public interface HibernateAnnotations {
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 );

View File

@ -22,6 +22,7 @@ import jakarta.persistence.Cacheable;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ColumnResult;
import jakarta.persistence.ConstructorResult;
import jakarta.persistence.Convert;
import jakarta.persistence.Converter;
import jakarta.persistence.Converts;
@ -116,6 +117,7 @@ public interface JpaAnnotations {
AnnotationDescriptor<CollectionTable> COLLECTION_TABLE = createOrmDescriptor( CollectionTable.class );
AnnotationDescriptor<Column> COLUMN = createOrmDescriptor( Column.class );
AnnotationDescriptor<ColumnResult> COLUMN_RESULT = createOrmDescriptor( ColumnResult.class );
AnnotationDescriptor<ConstructorResult> CONSTRUCTOR_RESULT = createOrmDescriptor( ConstructorResult.class );
AnnotationDescriptor<Converts> CONVERTS = createOrmDescriptor( Converts.class );
AnnotationDescriptor<Convert> CONVERT = createOrmDescriptor( Convert.class, CONVERTS );
AnnotationDescriptor<Converter> CONVERTER = createOrmDescriptor( Converter.class );

View File

@ -69,7 +69,7 @@ public class BasicValueHelper {
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
basicValue.setImplicitJavaTypeAccess( (typeConfiguration) -> member.getType().toJavaClass() );
basicValue.setImplicitJavaTypeAccess( (typeConfiguration) -> member.getType().determineRawClass().toJavaClass() );
}
public static void bindJavaType(
@ -313,7 +313,7 @@ public class BasicValueHelper {
final TypeConfiguration typeConfiguration = collector.getTypeConfiguration();
final MemberDetails memberDetails = tenantIdAttribute.getMember();
final String returnedClassName = memberDetails.getType().getClassName();
final String returnedClassName = memberDetails.getType().determineRawClass().getClassName();
final BasicType<Object> tenantIdType = typeConfiguration
.getBasicTypeRegistry()
.getRegisteredType( returnedClassName );

View File

@ -10,6 +10,7 @@ import java.util.EnumSet;
import org.hibernate.annotations.Any;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.TenantId;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.models.MultipleAttributeNaturesException;
@ -98,7 +99,7 @@ public class CategorizationHelper {
if ( embedded != null
|| embeddedId != null
|| ( backingMember.getType() != null && backingMember.getType().getAnnotationUsage( JpaAnnotations.EMBEDDABLE ) != null ) ) {
|| ( backingMember.getType() != null && backingMember.getType().determineRawClass().getAnnotationUsage( JpaAnnotations.EMBEDDABLE ) != null ) ) {
natures.add( EMBEDDED );
}
@ -142,7 +143,7 @@ public class CategorizationHelper {
|| 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( TenantId.class ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.JAVA_TYPE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.JDBC_TYPE_CODE ) != null
|| backingMember.getAnnotationUsage( HibernateAnnotations.JDBC_TYPE ) != null;

View File

@ -17,11 +17,13 @@ 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.GlobalRegistrations;
import org.hibernate.boot.models.categorize.spi.ManagedResourcesProcessor;
import org.hibernate.boot.models.xml.spi.PersistenceUnitMetadata;
import org.hibernate.models.spi.AnnotationDescriptorRegistry;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.jboss.jandex.IndexView;
@ -53,10 +55,11 @@ public class DomainModelCategorizationCollector {
boolean areIdGeneratorsGlobal,
ClassDetailsRegistry classDetailsRegistry,
AnnotationDescriptorRegistry descriptorRegistry,
GlobalRegistrations globalRegistrations,
IndexView jandexIndex) {
this.areIdGeneratorsGlobal = areIdGeneratorsGlobal;
this.jandexIndex = jandexIndex;
this.globalRegistrations = new GlobalRegistrationsImpl( classDetailsRegistry, descriptorRegistry );
this.globalRegistrations = (GlobalRegistrationsImpl) globalRegistrations;
}
public GlobalRegistrationsImpl getGlobalRegistrations() {
@ -99,7 +102,8 @@ public class DomainModelCategorizationCollector {
getGlobalRegistrations().collectIdGenerators( jaxbRoot );
// todo : named queries
getGlobalRegistrations().collectQueryReferences( jaxbRoot );
// todo : named graphs
}

View File

@ -10,7 +10,6 @@ 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;
@ -127,7 +126,7 @@ public class EntityHierarchyBuilder {
return accessAnnotation.getAttributeValue( "value" );
}
current = current.getSuperType();
current = current.getSuperClass();
}
return null;
@ -152,7 +151,7 @@ public class EntityHierarchyBuilder {
}
}
current = current.getSuperType();
current = current.getSuperClass();
}
return null;
@ -208,17 +207,17 @@ public class EntityHierarchyBuilder {
// 1) it has no super-types
// 2) its super types contain no entities (MappedSuperclasses are allowed)
if ( classInfo.getSuperType() == null ) {
if ( classInfo.getSuperClass() == null ) {
return true;
}
ClassDetails current = classInfo.getSuperType();
ClassDetails current = classInfo.getSuperClass();
while ( current != null ) {
if ( current.getAnnotationUsage( JpaAnnotations.ENTITY ) != null && !current.isAbstract() ) {
// a non-abstract super type has `@Entity` -> classInfo cannot be a root entity
return false;
}
current = current.getSuperType();
current = current.getSuperClass();
}
// if we hit no opt-outs we have a root

View File

@ -117,8 +117,8 @@ public class EntityHierarchyImpl implements EntityHierarchy {
}
private ClassDetails findRootRoot(ClassDetails rootEntityClassDetails) {
if ( rootEntityClassDetails.getSuperType() != null ) {
final ClassDetails match = walkSupers( rootEntityClassDetails.getSuperType() );
if ( rootEntityClassDetails.getSuperClass() != null ) {
final ClassDetails match = walkSupers( rootEntityClassDetails.getSuperClass() );
if ( match != null ) {
return match;
}
@ -129,8 +129,8 @@ public class EntityHierarchyImpl implements EntityHierarchy {
private ClassDetails walkSupers(ClassDetails type) {
assert type != null;
if ( type.getSuperType() != null ) {
final ClassDetails match = walkSupers( type.getSuperType() );
if ( type.getSuperClass() != null ) {
final ClassDetails match = walkSupers( type.getSuperClass() );
if ( match != null ) {
return match;
}

View File

@ -14,6 +14,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.GenericGenerator;
@ -21,20 +22,33 @@ import org.hibernate.annotations.Imported;
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.JaxbColumnResultImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbCompositeUserTypeRegistrationImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbConfigurationParameterImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbConstructorResultImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbConverterImpl;
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.JaxbEntityResultImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbFieldResultImpl;
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.JaxbNamedNativeQueryImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedQueryBase;
import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedQueryImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbNamedStoredProcedureQueryImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbQueryHint;
import org.hibernate.boot.jaxb.mapping.spi.JaxbSequenceGeneratorImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbSqlResultSetMappingImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbStoredProcedureParameterImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbTableGeneratorImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbUserTypeRegistrationImpl;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.JpaAnnotations;
import org.hibernate.boot.models.categorize.spi.CollectionTypeRegistration;
import org.hibernate.boot.models.categorize.spi.CompositeUserTypeRegistration;
import org.hibernate.boot.models.categorize.spi.ConversionRegistration;
@ -47,24 +61,38 @@ 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.NamedNativeQueryRegistration;
import org.hibernate.boot.models.categorize.spi.NamedQueryRegistration;
import org.hibernate.boot.models.categorize.spi.NamedStoredProcedureQueryRegistration;
import org.hibernate.boot.models.categorize.spi.SequenceGeneratorRegistration;
import org.hibernate.boot.models.categorize.spi.SqlResultSetMappingRegistration;
import org.hibernate.boot.models.categorize.spi.TableGeneratorRegistration;
import org.hibernate.boot.models.categorize.spi.UserTypeRegistration;
import org.hibernate.boot.models.xml.internal.XmlAnnotationHelper;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.AvailableHints;
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.MutableAnnotationUsage;
import org.hibernate.models.spi.SourceModelContext;
import jakarta.persistence.ColumnResult;
import jakarta.persistence.ConstructorResult;
import jakarta.persistence.EntityResult;
import jakarta.persistence.FieldResult;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.NamedStoredProcedureQuery;
import jakarta.persistence.QueryHint;
import jakarta.persistence.SequenceGenerator;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.StoredProcedureParameter;
import jakarta.persistence.TableGenerator;
import static java.util.Collections.emptyList;
@ -78,6 +106,7 @@ 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.arrayList;
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
/**
@ -104,6 +133,11 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
private Set<ConverterRegistration> jpaConverters;
private Map<String, SqlResultSetMappingRegistration> sqlResultSetMappingRegistrations;
private Map<String, NamedQueryRegistration> namedQueryRegistrations;
private Map<String, NamedNativeQueryRegistration> namedNativeQueryRegistrations;
private Map<String, NamedStoredProcedureQueryRegistration> namedStoredProcedureQueryRegistrations;
public GlobalRegistrationsImpl(SourceModelContext sourceModelContext) {
this( sourceModelContext.getClassDetailsRegistry(), sourceModelContext.getAnnotationDescriptorRegistry() );
}
@ -183,6 +217,26 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
return jpaConverters == null ? emptySet() : jpaConverters;
}
@Override
public Map<String, SqlResultSetMappingRegistration> getSqlResultSetMappingRegistrations() {
return sqlResultSetMappingRegistrations == null ? emptyMap() : sqlResultSetMappingRegistrations;
}
@Override
public Map<String, NamedQueryRegistration > getNamedQueryRegistrations() {
return namedQueryRegistrations == null ? emptyMap() : namedQueryRegistrations;
}
@Override
public Map<String, NamedNativeQueryRegistration> getNamedNativeQueryRegistrations() {
return namedNativeQueryRegistrations == null ? emptyMap() : namedNativeQueryRegistrations;
}
@Override
public Map<String, NamedStoredProcedureQueryRegistration> getNamedStoredProcedureQueryRegistrations() {
return namedStoredProcedureQueryRegistrations== null ? emptyMap() : namedStoredProcedureQueryRegistrations;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JavaTypeRegistration
@ -581,7 +635,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
}
sequenceGenerators.forEach( (generator) -> {
final MutableAnnotationUsage<SequenceGenerator> annotationUsage = makeAnnotation( SequenceGenerator.class );
final MutableAnnotationUsage<SequenceGenerator> annotationUsage = makeAnnotation( JpaAnnotations.SEQUENCE_GENERATOR );
annotationUsage.setAttributeValue( "name", generator.getName() );
annotationUsage.setAttributeValue( "sequenceName", generator.getSequenceName() );
annotationUsage.setAttributeValue( "catalog", generator.getCatalog() );
@ -593,9 +647,8 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
} );
}
private <A extends Annotation> MutableAnnotationUsage<A> makeAnnotation(Class<A> annotationType) {
final AnnotationDescriptor<A> descriptor = descriptorRegistry.getDescriptor( annotationType );
return new DynamicAnnotationUsage<>( descriptor );
private <A extends Annotation> MutableAnnotationUsage<A> makeAnnotation(AnnotationDescriptor<A> annotationDescriptor) {
return annotationDescriptor.createUsage( null, null );
}
public void collectSequenceGenerator(AnnotationUsage<SequenceGenerator> usage) {
@ -607,7 +660,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
sequenceGeneratorRegistrations = new HashMap<>();
}
sequenceGeneratorRegistrations.put( generatorRegistration.getName(), generatorRegistration );
sequenceGeneratorRegistrations.put( generatorRegistration.name(), generatorRegistration );
}
@ -620,7 +673,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
}
tableGenerators.forEach( (generator) -> {
final MutableAnnotationUsage<TableGenerator> annotationUsage = makeAnnotation( TableGenerator.class );
final MutableAnnotationUsage<TableGenerator> annotationUsage = makeAnnotation( JpaAnnotations.TABLE_GENERATOR );
annotationUsage.setAttributeValue( "name", generator.getName() );
annotationUsage.setAttributeValue( "table", generator.getTable() );
annotationUsage.setAttributeValue( "catalog", generator.getCatalog() );
@ -644,7 +697,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
tableGeneratorRegistrations = new HashMap<>();
}
tableGeneratorRegistrations.put( generatorRegistration.getName(), generatorRegistration );
tableGeneratorRegistrations.put( generatorRegistration.name(), generatorRegistration );
}
@ -657,7 +710,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
}
genericGenerators.forEach( (generator) -> {
final MutableAnnotationUsage<GenericGenerator> annotationUsage = makeAnnotation( GenericGenerator.class );
final MutableAnnotationUsage<GenericGenerator> annotationUsage = makeAnnotation( HibernateAnnotations.GENERIC_GENERATOR );
annotationUsage.setAttributeValue( "name", generator.getName() );
annotationUsage.setAttributeValue( "strategy", generator.getClazz() );
@ -676,7 +729,7 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
genericGeneratorRegistrations = new HashMap<>();
}
genericGeneratorRegistrations.put( generatorRegistration.getName(), generatorRegistration );
genericGeneratorRegistrations.put( generatorRegistration.name(), generatorRegistration );
}
@ -709,4 +762,317 @@ public class GlobalRegistrationsImpl implements GlobalRegistrations {
jpaConverters.add( new ConverterRegistration( converterType, autoApply ) );
} );
}
public void collectQueryReferences(JaxbEntityMappingsImpl jaxbRoot) {
collectNamedSqlResultSetMappings( jaxbRoot.getSqlResultSetMappings() );
collectNamedQueries( jaxbRoot.getNamedQueries() );
collectNamedNativeQueries( jaxbRoot.getNamedNativeQueries() );
collectStoredProcedureQueries( jaxbRoot.getNamedProcedureQueries() );
}
private void collectNamedSqlResultSetMappings(List<JaxbSqlResultSetMappingImpl> jaxbSqlResultSetMappings) {
if ( isEmpty( jaxbSqlResultSetMappings ) ) {
return;
}
if ( sqlResultSetMappingRegistrations == null ) {
sqlResultSetMappingRegistrations = new HashMap<>();
}
jaxbSqlResultSetMappings.forEach( (jaxbMapping) -> {
final MutableAnnotationUsage<SqlResultSetMapping> mappingAnnotation = JpaAnnotations.SQL_RESULT_SET_MAPPING.createUsage( null, null );
mappingAnnotation.setAttributeValue( "name", jaxbMapping.getName() );
sqlResultSetMappingRegistrations.put(
jaxbMapping.getName(),
new SqlResultSetMappingRegistration( jaxbMapping.getName(), mappingAnnotation )
);
applyEntityResults(
jaxbMapping.getEntityResult(),
(results) -> mappingAnnotation.setAttributeValue( "entities", results )
);
applyConstructorResults(
jaxbMapping.getConstructorResult(),
(results) -> mappingAnnotation.setAttributeValue( "classes", results )
);
applyColumnResults(
jaxbMapping.getColumnResult(),
(columnResults) -> mappingAnnotation.setAttributeValue( "columns", columnResults )
);
} );
}
private void applyEntityResults(
List<JaxbEntityResultImpl> jaxbEntityResults,
Consumer<List<AnnotationUsage<EntityResult>>> annotationListConsumer) {
if ( jaxbEntityResults.isEmpty() ) {
return;
}
final List<AnnotationUsage<EntityResult>> entityResults = arrayList( jaxbEntityResults.size() );
for ( JaxbEntityResultImpl jaxbEntityResult : jaxbEntityResults ) {
final MutableAnnotationUsage<EntityResult> entityResultAnnotation = makeAnnotation( JpaAnnotations.ENTITY_RESULT );
entityResults.add( entityResultAnnotation );
entityResultAnnotation.setAttributeValue( "entityClass", classDetailsRegistry.resolveClassDetails( jaxbEntityResult.getEntityClass() ) );
entityResultAnnotation.setAttributeValue( "lockMode", jaxbEntityResult.getLockMode() );
entityResultAnnotation.setAttributeValue( "discriminatorColumn", jaxbEntityResult.getDiscriminatorColumn() );
if ( !jaxbEntityResult.getFieldResult().isEmpty() ) {
final List<AnnotationUsage<FieldResult>> fieldResults = arrayList( jaxbEntityResult.getFieldResult().size() );
entityResultAnnotation.setAttributeValue( "fields", fieldResults );
for ( JaxbFieldResultImpl jaxbFieldResult : jaxbEntityResult.getFieldResult() ) {
final MutableAnnotationUsage<FieldResult> fieldResultAnnotation = makeAnnotation( JpaAnnotations.FIELD_RESULT );
fieldResultAnnotation.setAttributeValue( "name", jaxbFieldResult.getName() );
fieldResultAnnotation.setAttributeValue( "column", jaxbFieldResult.getColumn() );
}
}
}
annotationListConsumer.accept( entityResults );
}
private void applyConstructorResults(
List<JaxbConstructorResultImpl> jaxbConstructorResults,
Consumer<List<AnnotationUsage<ConstructorResult>>> annotationListConsumer) {
if ( isEmpty( jaxbConstructorResults ) ) {
return;
}
final List<AnnotationUsage<ConstructorResult>> results = arrayList( jaxbConstructorResults.size() );
for ( JaxbConstructorResultImpl jaxbConstructorResult : jaxbConstructorResults ) {
final MutableAnnotationUsage<ConstructorResult> result = makeAnnotation( JpaAnnotations.CONSTRUCTOR_RESULT );
results.add( result );
result.setAttributeValue(
"entityClass",
classDetailsRegistry.resolveClassDetails( jaxbConstructorResult.getTargetClass() )
);
if ( !jaxbConstructorResult.getColumns().isEmpty() ) {
applyColumnResults(
jaxbConstructorResult.getColumns(),
(columnResults) -> result.setAttributeValue( "columns", columnResults )
);
}
}
}
private void applyColumnResults(
List<JaxbColumnResultImpl> jaxbColumnResults,
Consumer<List<AnnotationUsage<ColumnResult>>> annotationListConsumer) {
if ( isEmpty( jaxbColumnResults ) ) {
return;
}
final List<AnnotationUsage<ColumnResult>> columnResults = arrayList( jaxbColumnResults.size() );
for ( JaxbColumnResultImpl jaxbColumn : jaxbColumnResults ) {
final MutableAnnotationUsage<ColumnResult> columnResultAnnotation = makeAnnotation( JpaAnnotations.COLUMN_RESULT );
columnResults.add( columnResultAnnotation );
columnResultAnnotation.setAttributeValue( "name", jaxbColumn.getName() );
columnResultAnnotation.setAttributeValue( "type", classDetailsRegistry.resolveClassDetails( jaxbColumn.getClazz() ) );
}
annotationListConsumer.accept( columnResults );
}
private void collectNamedQueries(List<JaxbNamedQueryImpl> jaxbNamedQueries) {
if ( isEmpty( jaxbNamedQueries ) ) {
return;
}
if ( namedQueryRegistrations == null ) {
namedQueryRegistrations = new HashMap<>();
}
for ( JaxbNamedQueryImpl jaxbNamedQuery : jaxbNamedQueries ) {
final MutableAnnotationUsage<NamedQuery> queryAnnotation = makeAnnotation( JpaAnnotations.NAMED_QUERY );
namedQueryRegistrations.put(
jaxbNamedQuery.getName(),
new NamedQueryRegistration( jaxbNamedQuery.getName(), queryAnnotation )
);
queryAnnotation.setAttributeValue( "name", jaxbNamedQuery.getName() );
queryAnnotation.setAttributeValue( "query", jaxbNamedQuery.getQuery() );
queryAnnotation.setAttributeValue( "lockMode", jaxbNamedQuery.getLockMode() );
final List<AnnotationUsage<QueryHint>> hints = extractQueryHints( jaxbNamedQuery );
queryAnnotation.setAttributeValue( "hints", hints );
if ( jaxbNamedQuery.isCacheable() == Boolean.TRUE ) {
final MutableAnnotationUsage<QueryHint> cacheableHint = makeAnnotation( JpaAnnotations.QUERY_HINT );
cacheableHint.setAttributeValue( "name", AvailableHints.HINT_CACHEABLE );
cacheableHint.setAttributeValue( "value", Boolean.TRUE.toString() );
if ( jaxbNamedQuery.getCacheMode() != null ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_CACHE_MODE );
hint.setAttributeValue( "value", jaxbNamedQuery.getCacheMode().name() );
}
if ( StringHelper.isNotEmpty( jaxbNamedQuery.getCacheRegion() ) ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_CACHE_REGION );
hint.setAttributeValue( "value", jaxbNamedQuery.getCacheRegion() );
}
}
if ( StringHelper.isNotEmpty( jaxbNamedQuery.getComment() ) ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_COMMENT );
hint.setAttributeValue( "value", jaxbNamedQuery.getComment() );
}
if ( jaxbNamedQuery.getFetchSize() != null ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_FETCH_SIZE );
hint.setAttributeValue( "value", jaxbNamedQuery.getFetchSize().toString() );
}
if ( jaxbNamedQuery.isReadOnly() == Boolean.TRUE ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_READ_ONLY );
hint.setAttributeValue( "value", Boolean.TRUE.toString() );
}
if ( jaxbNamedQuery.getFlushMode() != null ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_FLUSH_MODE );
hint.setAttributeValue( "value", jaxbNamedQuery.getFlushMode().name() );
}
}
}
private void collectNamedNativeQueries(List<JaxbNamedNativeQueryImpl> namedNativeQueries) {
if ( isEmpty( namedNativeQueries ) ) {
return;
}
if ( namedNativeQueryRegistrations == null ) {
namedNativeQueryRegistrations = new HashMap<>();
}
for ( JaxbNamedNativeQueryImpl jaxbNamedQuery : namedNativeQueries ) {
final MutableAnnotationUsage<NamedNativeQuery> queryAnnotation = makeAnnotation( JpaAnnotations.NAMED_NATIVE_QUERY );
namedNativeQueryRegistrations.put(
jaxbNamedQuery.getName(),
new NamedNativeQueryRegistration( jaxbNamedQuery.getName(), queryAnnotation )
);
queryAnnotation.setAttributeValue( "name", jaxbNamedQuery.getName() );
queryAnnotation.setAttributeValue( "query", jaxbNamedQuery.getQuery() );
if ( StringHelper.isNotEmpty( jaxbNamedQuery.getResultClass() ) ) {
queryAnnotation.setAttributeValue( "resultClass", classDetailsRegistry.resolveClassDetails( jaxbNamedQuery.getResultClass() ) );
}
if ( StringHelper.isNotEmpty( jaxbNamedQuery.getResultSetMapping() ) ) {
queryAnnotation.setAttributeValue( "resultSetMapping", jaxbNamedQuery.getResultSetMapping() );
}
applyEntityResults(
jaxbNamedQuery.getEntityResult(),
(results) -> queryAnnotation.setAttributeValue( "entities", results )
);
applyConstructorResults(
jaxbNamedQuery.getConstructorResult(),
(results) -> queryAnnotation.setAttributeValue( "classes", results )
);
applyColumnResults(
jaxbNamedQuery.getColumnResult(),
(columnResults) -> queryAnnotation.setAttributeValue( "columns", columnResults )
);
final List<AnnotationUsage<QueryHint>> hints = extractQueryHints( jaxbNamedQuery );
queryAnnotation.setAttributeValue( "hints", hints );
if ( jaxbNamedQuery.isCacheable() == Boolean.TRUE ) {
final MutableAnnotationUsage<QueryHint> cacheableHint = makeAnnotation( JpaAnnotations.QUERY_HINT );
cacheableHint.setAttributeValue( "name", AvailableHints.HINT_CACHEABLE );
cacheableHint.setAttributeValue( "value", Boolean.TRUE.toString() );
if ( jaxbNamedQuery.getCacheMode() != null ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_CACHE_MODE );
hint.setAttributeValue( "value", jaxbNamedQuery.getCacheMode().name() );
}
if ( StringHelper.isNotEmpty( jaxbNamedQuery.getCacheRegion() ) ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_CACHE_REGION );
hint.setAttributeValue( "value", jaxbNamedQuery.getCacheRegion() );
}
}
if ( jaxbNamedQuery.isReadOnly() == Boolean.TRUE ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_READ_ONLY );
hint.setAttributeValue( "value", Boolean.TRUE.toString() );
}
if ( StringHelper.isNotEmpty( jaxbNamedQuery.getComment() ) ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", AvailableHints.HINT_COMMENT );
hint.setAttributeValue( "value", jaxbNamedQuery.getComment() );
}
}
}
private void collectStoredProcedureQueries(List<JaxbNamedStoredProcedureQueryImpl> namedProcedureQueries) {
if ( isEmpty( namedProcedureQueries ) ) {
return;
}
if ( namedStoredProcedureQueryRegistrations == null ) {
namedStoredProcedureQueryRegistrations = new HashMap<>();
}
for ( JaxbNamedStoredProcedureQueryImpl jaxbQuery : namedProcedureQueries ) {
final MutableAnnotationUsage<NamedStoredProcedureQuery> queryAnnotation = makeAnnotation( JpaAnnotations.NAMED_STORED_PROCEDURE_QUERY );
namedStoredProcedureQueryRegistrations.put(
jaxbQuery.getName(),
new NamedStoredProcedureQueryRegistration( jaxbQuery.getName(), queryAnnotation )
);
queryAnnotation.setAttributeValue( "name", jaxbQuery.getName() );
queryAnnotation.setAttributeValue( "procedureName", jaxbQuery.getProcedureName() );
final ArrayList<ClassDetails> resultClasses = arrayList( jaxbQuery.getResultClasses().size() );
queryAnnotation.setAttributeValue( "resultClasses", resultClasses );
for ( String resultClassName : jaxbQuery.getResultClasses() ) {
resultClasses.add( classDetailsRegistry.resolveClassDetails( resultClassName ) );
}
queryAnnotation.setAttributeValue( "resultSetMappings", jaxbQuery.getResultSetMappings() );
queryAnnotation.setAttributeValue( "hints", extractQueryHints( jaxbQuery ) );
final ArrayList<AnnotationUsage<StoredProcedureParameter>> parameters = arrayList( jaxbQuery.getProcedureParameters().size() );
queryAnnotation.setAttributeValue( "parameters", parameters );
for ( JaxbStoredProcedureParameterImpl jaxbProcedureParameter : jaxbQuery.getProcedureParameters() ) {
final MutableAnnotationUsage<StoredProcedureParameter> parameterAnnotation = makeAnnotation( JpaAnnotations.STORED_PROCEDURE_PARAMETER );
parameters.add( parameterAnnotation );
parameterAnnotation.setAttributeValue( "name", jaxbProcedureParameter.getName() );
parameterAnnotation.setAttributeValue( "mode", jaxbProcedureParameter.getMode() );
parameterAnnotation.setAttributeValue( "type", classDetailsRegistry.resolveClassDetails( jaxbProcedureParameter.getClazz() ) );
}
}
}
private List<AnnotationUsage<QueryHint>> extractQueryHints(JaxbNamedQueryBase jaxbQuery) {
final List<AnnotationUsage<QueryHint>> hints = new ArrayList<>();
for ( JaxbQueryHint jaxbHint : jaxbQuery.getHints() ) {
final MutableAnnotationUsage<QueryHint> hint = makeAnnotation( JpaAnnotations.QUERY_HINT );
hint.setAttributeValue( "name", jaxbHint.getName() );
hint.setAttributeValue( "value", jaxbHint.getValue() );
}
return hints;
}
}

View File

@ -12,25 +12,9 @@ import org.hibernate.models.spi.AnnotationUsage;
/**
* Global registration of a generic generator
*
* @author Steve Ebersole
* @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;
}
public record GenericGeneratorRegistration(String name, AnnotationUsage<GenericGenerator> configuration) {
}

View File

@ -10,6 +10,8 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.boot.models.categorize.spi.NamedQueryRegistration;
/**
* Registrations which are considered global, collected across annotations
@ -46,6 +48,13 @@ public interface GlobalRegistrations {
Set<ConverterRegistration> getJpaConverters();
Map<String, SqlResultSetMappingRegistration> getSqlResultSetMappingRegistrations();
Map<String, NamedQueryRegistration> getNamedQueryRegistrations();
Map<String, NamedNativeQueryRegistration> getNamedNativeQueryRegistrations();
Map<String, NamedStoredProcedureQueryRegistration> getNamedStoredProcedureQueryRegistrations();
// todo : named entity graphs
// todo : named queries
}

View File

@ -10,7 +10,6 @@ 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;
@ -282,13 +281,13 @@ public class JpaEventListener {
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;
&& methodDetails.getReturnType() == ClassDetails.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;
&& methodDetails.getReturnType() == ClassDetails.VOID_CLASS_DETAILS;
}
}

View File

@ -12,10 +12,12 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.hibernate.annotations.TenantId;
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.GlobalRegistrationsImpl;
import org.hibernate.boot.models.categorize.internal.ModelCategorizationContextImpl;
import org.hibernate.boot.models.categorize.internal.OrmAnnotationHelper;
import org.hibernate.boot.models.xml.spi.XmlPreProcessingResult;
@ -123,10 +125,12 @@ public class ManagedResourcesProcessor {
final boolean areIdGeneratorsGlobal = true;
final ClassDetailsRegistry classDetailsRegistry = sourceModelBuildingContext.getClassDetailsRegistry();
final AnnotationDescriptorRegistry descriptorRegistry = sourceModelBuildingContext.getAnnotationDescriptorRegistry();
final GlobalRegistrationsImpl globalRegistrations = new GlobalRegistrationsImpl( sourceModelBuildingContext );
final DomainModelCategorizationCollector modelCategorizationCollector = new DomainModelCategorizationCollector(
areIdGeneratorsGlobal,
classDetailsRegistry,
descriptorRegistry,
globalRegistrations,
jandexIndex
);
@ -166,7 +170,7 @@ public class ManagedResourcesProcessor {
final ModelCategorizationContextImpl mappingBuildingContext = new ModelCategorizationContextImpl(
classDetailsRegistryImmutable,
annotationDescriptorRegistryImmutable,
modelCategorizationCollector.getGlobalRegistrations()
globalRegistrations
);
final Set<EntityHierarchy> entityHierarchies;
@ -240,6 +244,8 @@ public class ManagedResourcesProcessor {
public static void preFillRegistries(RegistryPrimer.Contributions contributions, SourceModelBuildingContext buildingContext) {
OrmAnnotationHelper.forEachOrmAnnotation( contributions::registerAnnotation );
buildingContext.getAnnotationDescriptorRegistry().getDescriptor( TenantId.class );
final IndexView jandexIndex = buildingContext.getJandexIndex();
if ( jandexIndex == null ) {
return;

View File

@ -0,0 +1,48 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.QueryHint;
/**
* @author Steve Ebersole
*/
public record NamedNativeQueryRegistration(String name, AnnotationUsage<NamedNativeQuery> configuration) {
public String getQueryString() {
return configuration.getString( "query" );
}
public ClassDetails getResultClass() {
return configuration.getClassDetails( "resultClass" );
}
public String getResultSetMapping() {
return configuration.getString( "resultSetMapping" );
}
public Map<String,String> getQueryHints() {
final List<AnnotationUsage<QueryHint>> hints = configuration.getList( "hints" );
if ( CollectionHelper.isEmpty( hints ) ) {
return Collections.emptyMap();
}
final Map<String,String> result = CollectionHelper.linkedMapOfSize( hints.size() );
for ( AnnotationUsage<QueryHint> hint : hints ) {
result.put( hint.getString( "name" ), hint.getString( "value" ) );
}
return result;
}
}

View File

@ -0,0 +1,44 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.models.categorize.spi;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.LockModeType;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.QueryHint;
/**
* @author Steve Ebersole
*/
public record NamedQueryRegistration(String name, AnnotationUsage<NamedQuery> configuration) {
public String getQueryString() {
return configuration.getString( "query" );
}
public LockModeType getLockModeType() {
return configuration.getEnum( "lockMode" );
}
public Map<String,String> getQueryHints() {
final List<AnnotationUsage<QueryHint>> hints = configuration.getList( "hints" );
if ( CollectionHelper.isEmpty( hints ) ) {
return Collections.emptyMap();
}
final Map<String,String> result = CollectionHelper.linkedMapOfSize( hints.size() );
for ( AnnotationUsage<QueryHint> hint : hints ) {
result.put( hint.getString( "name" ), hint.getString( "value" ) );
}
return result;
}
}

View File

@ -0,0 +1,17 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.NamedStoredProcedureQuery;
/**
* @author Steve Ebersole
*/
public record NamedStoredProcedureQueryRegistration(String name, AnnotationUsage<NamedStoredProcedureQuery> configuration) {
}

View File

@ -15,20 +15,5 @@ import jakarta.persistence.SequenceGenerator;
*
* @author Steve Ebersole
*/
public class SequenceGeneratorRegistration {
private final String name;
private final AnnotationUsage<SequenceGenerator> configuration;
public SequenceGeneratorRegistration(String name, AnnotationUsage<SequenceGenerator> configuration) {
this.name = name;
this.configuration = configuration;
}
public String getName() {
return name;
}
public AnnotationUsage<SequenceGenerator> getConfiguration() {
return configuration;
}
public record SequenceGeneratorRegistration(String name, AnnotationUsage<SequenceGenerator> configuration) {
}

View File

@ -19,7 +19,7 @@ public interface SingleAttributeKeyMapping extends KeyMapping {
}
default ClassDetails getKeyType() {
return getAttribute().getMember().getType();
return getAttribute().getMember().getType().determineRawClass();
}
@Override

View File

@ -0,0 +1,20 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
package org.hibernate.boot.models.categorize.spi;
import org.hibernate.models.spi.AnnotationUsage;
import jakarta.persistence.SqlResultSetMapping;
/**
* Registration of a SqlResultSetMapping while processing managed resources as part of
* building the domain metamodel
*
* @author Steve Ebersole
*/
public record SqlResultSetMappingRegistration(String name, AnnotationUsage<SqlResultSetMapping> configuration) {
}

View File

@ -15,20 +15,5 @@ import jakarta.persistence.TableGenerator;
*
* @author Steve Ebersole
*/
public class TableGeneratorRegistration {
private final String name;
private final AnnotationUsage<TableGenerator> configuration;
public TableGeneratorRegistration(String name, AnnotationUsage<TableGenerator> configuration) {
this.name = name;
this.configuration = configuration;
}
public String getName() {
return name;
}
public AnnotationUsage<TableGenerator> getConfiguration() {
return configuration;
}
public record TableGeneratorRegistration(String name, AnnotationUsage<TableGenerator> configuration) {
}

View File

@ -29,8 +29,8 @@ import org.hibernate.boot.models.xml.internal.attr.OneToManyAttributeProcessing;
import org.hibernate.boot.models.xml.internal.attr.OneToOneAttributeProcessing;
import org.hibernate.boot.models.xml.internal.attr.PluralAnyMappingAttributeProcessing;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.models.internal.MutableClassDetails;
import org.hibernate.models.internal.MutableMemberDetails;
import org.hibernate.models.spi.MutableClassDetails;
import org.hibernate.models.spi.MutableMemberDetails;
import jakarta.persistence.AccessType;

View File

@ -38,6 +38,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbOneToOneImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPersistentAttribute;
import org.hibernate.boot.jaxb.mapping.spi.JaxbPluralAttribute;
import org.hibernate.boot.jaxb.mapping.spi.JaxbTenantIdImpl;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.xml.internal.attr.BasicAttributeProcessing;
import org.hibernate.boot.models.xml.internal.attr.BasicIdAttributeProcessing;
@ -48,17 +49,19 @@ import org.hibernate.boot.models.xml.spi.XmlProcessingResult;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.ModelsException;
import org.hibernate.models.internal.ClassTypeDetailsImpl;
import org.hibernate.models.internal.ModelsClassLogging;
import org.hibernate.models.internal.MutableAnnotationUsage;
import org.hibernate.models.internal.MutableClassDetails;
import org.hibernate.models.internal.MutableMemberDetails;
import org.hibernate.models.spi.MutableClassDetails;
import org.hibernate.models.spi.MutableMemberDetails;
import org.hibernate.models.internal.dynamic.DynamicClassDetails;
import org.hibernate.models.internal.dynamic.MapModeFieldDetails;
import org.hibernate.models.internal.dynamic.DynamicFieldDetails;
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 org.hibernate.models.spi.MutableAnnotationUsage;
import org.hibernate.models.spi.TypeDetails;
import org.hibernate.property.access.spi.BuiltInPropertyAccessStrategies;
import jakarta.persistence.Access;
@ -111,6 +114,7 @@ public class ManagedTypeProcessor {
null,
false,
null,
null,
xmlDocumentContext.getModelBuildingContext()
)
);
@ -157,10 +161,11 @@ public class ManagedTypeProcessor {
if ( CollectionHelper.isNotEmpty( attributes.getIdAttributes() ) ) {
// <id/>
attributes.getIdAttributes().forEach( (jaxbId) -> {
final ClassDetails attributeJavaType = determineDynamicAttributeJavaType( jaxbId, xmlDocumentContext );
final MapModeFieldDetails member = new MapModeFieldDetails(
final TypeDetails attributeJavaType = determineDynamicAttributeJavaType( jaxbId, xmlDocumentContext );
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbId.getName(),
attributeJavaType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -170,10 +175,11 @@ public class ManagedTypeProcessor {
else {
// <embedded-id/>
final JaxbEmbeddedIdImpl embeddedId = attributes.getEmbeddedIdAttribute();
final ClassDetails attributeJavaType = determineDynamicAttributeJavaType( embeddedId, xmlDocumentContext );
final MapModeFieldDetails member = new MapModeFieldDetails(
final TypeDetails attributeJavaType = determineDynamicAttributeJavaType( embeddedId, xmlDocumentContext );
final DynamicFieldDetails member = new DynamicFieldDetails(
embeddedId.getName(),
attributeJavaType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -183,13 +189,14 @@ public class ManagedTypeProcessor {
// <natural-id/>
if ( attributes.getNaturalId() != null ) {
attributes.getNaturalId().getBasicAttributes().forEach( (jaxbBasic) -> {
final ClassDetails attributeJavaType = determineDynamicAttributeJavaType(
final TypeDetails attributeJavaType = determineDynamicAttributeJavaType(
jaxbBasic,
xmlDocumentContext
);
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbBasic.getName(),
attributeJavaType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -197,13 +204,14 @@ public class ManagedTypeProcessor {
} );
attributes.getNaturalId().getEmbeddedAttributes().forEach( (jaxbEmbedded) -> {
final ClassDetails attributeJavaType = determineDynamicAttributeJavaType(
final TypeDetails attributeJavaType = determineDynamicAttributeJavaType(
jaxbEmbedded,
xmlDocumentContext
);
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbEmbedded.getName(),
attributeJavaType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -211,10 +219,11 @@ public class ManagedTypeProcessor {
} );
attributes.getNaturalId().getManyToOneAttributes().forEach( (jaxbManyToOne) -> {
final ClassDetails attributeJavaType = determineDynamicAttributeJavaType( jaxbManyToOne, xmlDocumentContext );
final MapModeFieldDetails member = new MapModeFieldDetails(
final TypeDetails attributeJavaType = determineDynamicAttributeJavaType( jaxbManyToOne, xmlDocumentContext );
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbManyToOne.getName(),
attributeJavaType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -222,10 +231,11 @@ public class ManagedTypeProcessor {
} );
attributes.getNaturalId().getAnyMappingAttributes().forEach( (jaxbAnyMapping) -> {
final ClassDetails attributeJavaType = determineDynamicAttributeJavaType( jaxbAnyMapping, xmlDocumentContext );
final MapModeFieldDetails member = new MapModeFieldDetails(
final TypeDetails attributeJavaType = determineDynamicAttributeJavaType( jaxbAnyMapping, xmlDocumentContext );
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbAnyMapping.getName(),
attributeJavaType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -236,13 +246,14 @@ public class ManagedTypeProcessor {
// <tenant-id>
final JaxbTenantIdImpl tenantId = jaxbDynamicEntity.getTenantId();
if ( tenantId != null ) {
final ClassDetails attributeJavaType = determineDynamicAttributeJavaType(
final TypeDetails attributeJavaType = determineDynamicAttributeJavaType(
tenantId,
xmlDocumentContext
);
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
tenantId.getName(),
attributeJavaType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -252,9 +263,10 @@ public class ManagedTypeProcessor {
final JaxbAttributesContainer attributes = jaxbManagedType.getAttributes();
attributes.getBasicAttributes().forEach( (jaxbBasic) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbBasic.getName(),
determineDynamicAttributeJavaType( jaxbBasic, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -262,9 +274,10 @@ public class ManagedTypeProcessor {
} );
attributes.getEmbeddedAttributes().forEach( (jaxbEmbedded) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbEmbedded.getName(),
determineDynamicAttributeJavaType( jaxbEmbedded, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -272,9 +285,10 @@ public class ManagedTypeProcessor {
} );
attributes.getOneToOneAttributes().forEach( (jaxbOneToOne) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbOneToOne.getName(),
determineDynamicAttributeJavaType( jaxbOneToOne, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -282,9 +296,10 @@ public class ManagedTypeProcessor {
} );
attributes.getManyToOneAttributes().forEach( (jaxbManyToOne) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbManyToOne.getName(),
determineDynamicAttributeJavaType( jaxbManyToOne, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -292,9 +307,10 @@ public class ManagedTypeProcessor {
} );
attributes.getAnyMappingAttributes().forEach( (jaxbAnyMapping) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbAnyMapping.getName(),
determineDynamicAttributeJavaType( jaxbAnyMapping, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -302,9 +318,10 @@ public class ManagedTypeProcessor {
} );
attributes.getElementCollectionAttributes().forEach( (jaxbElementCollection) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbElementCollection.getName(),
determineDynamicAttributeJavaType( jaxbElementCollection, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -312,9 +329,10 @@ public class ManagedTypeProcessor {
} );
attributes.getOneToManyAttributes().forEach( (jaxbOneToMany) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbOneToMany.getName(),
determineDynamicAttributeJavaType( jaxbOneToMany, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -322,9 +340,10 @@ public class ManagedTypeProcessor {
} );
attributes.getManyToManyAttributes().forEach( (jaxbManyToMany) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbManyToMany.getName(),
determineDynamicAttributeJavaType( jaxbManyToMany, xmlDocumentContext ),
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -332,9 +351,14 @@ public class ManagedTypeProcessor {
} );
attributes.getPluralAnyMappingAttributes().forEach( (jaxbPluralAnyMapping) -> {
final MapModeFieldDetails member = new MapModeFieldDetails(
final TypeDetails attributeType = determineDynamicAttributeJavaType(
jaxbPluralAnyMapping,
xmlDocumentContext
);
final DynamicFieldDetails member = new DynamicFieldDetails(
jaxbPluralAnyMapping.getName(),
determineDynamicAttributeJavaType( jaxbPluralAnyMapping, xmlDocumentContext ),
attributeType,
classDetails,
MEMBER_MODIFIERS,
xmlDocumentContext.getModelBuildingContext()
);
@ -342,13 +366,14 @@ public class ManagedTypeProcessor {
} );
}
private static ClassDetails determineDynamicAttributeJavaType(
private static TypeDetails determineDynamicAttributeJavaType(
JaxbPersistentAttribute jaxbPersistentAttribute,
XmlDocumentContext xmlDocumentContext) {
final ClassDetailsRegistry classDetailsRegistry = xmlDocumentContext.getModelBuildingContext().getClassDetailsRegistry();
if ( jaxbPersistentAttribute instanceof JaxbIdImpl jaxbId ) {
return xmlDocumentContext.resolveJavaType( jaxbId.getTarget() );
final MutableClassDetails classDetails = xmlDocumentContext.resolveJavaType( jaxbId.getTarget() );
return new ClassTypeDetailsImpl( classDetails, TypeDetails.Kind.CLASS );
}
if ( jaxbPersistentAttribute instanceof JaxbEmbeddedIdImpl jaxbEmbeddedId ) {
@ -356,14 +381,16 @@ public class ManagedTypeProcessor {
if ( StringHelper.isEmpty( target ) ) {
return null;
}
return classDetailsRegistry.resolveClassDetails(
final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails(
target,
(name) -> new DynamicClassDetails( target, xmlDocumentContext.getModelBuildingContext() )
);
return new ClassTypeDetailsImpl( classDetails, TypeDetails.Kind.CLASS );
}
if ( jaxbPersistentAttribute instanceof JaxbBasicImpl jaxbBasic ) {
return xmlDocumentContext.resolveJavaType( jaxbBasic.getTarget() );
final MutableClassDetails classDetails = xmlDocumentContext.resolveJavaType( jaxbBasic.getTarget() );
return new ClassTypeDetailsImpl( classDetails, TypeDetails.Kind.CLASS );
}
if ( jaxbPersistentAttribute instanceof JaxbEmbeddedImpl jaxbEmbedded ) {
@ -371,10 +398,11 @@ public class ManagedTypeProcessor {
if ( StringHelper.isEmpty( target ) ) {
return null;
}
return classDetailsRegistry.resolveClassDetails(
final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails(
target,
(name) -> new DynamicClassDetails( target, xmlDocumentContext.getModelBuildingContext() )
);
return new ClassTypeDetailsImpl( classDetails, TypeDetails.Kind.CLASS );
}
if ( jaxbPersistentAttribute instanceof JaxbOneToOneImpl jaxbOneToOne ) {
@ -382,31 +410,39 @@ public class ManagedTypeProcessor {
if ( StringHelper.isEmpty( target ) ) {
return null;
}
return classDetailsRegistry.resolveClassDetails(
final ClassDetails classDetails = classDetailsRegistry.resolveClassDetails(
target,
(name) -> new DynamicClassDetails(
target,
null,
false,
null,
null,
xmlDocumentContext.getModelBuildingContext()
)
);
return new ClassTypeDetailsImpl( classDetails, TypeDetails.Kind.CLASS );
}
if ( jaxbPersistentAttribute instanceof JaxbAnyMappingImpl ) {
return classDetailsRegistry.getClassDetails( Object.class.getName() );
final ClassDetails classDetails = classDetailsRegistry.getClassDetails( Object.class.getName() );
return new ClassTypeDetailsImpl( classDetails, TypeDetails.Kind.CLASS );
}
if ( jaxbPersistentAttribute instanceof JaxbPluralAttribute jaxbPluralAttribute ) {
final LimitedCollectionClassification classification = nullif( jaxbPluralAttribute.getClassification(), LimitedCollectionClassification.BAG );
return switch ( classification ) {
final ClassDetails collectionClassDetails;
collectionClassDetails = switch ( classification ) {
case BAG -> classDetailsRegistry.resolveClassDetails( Collection.class.getName() );
case LIST -> classDetailsRegistry.resolveClassDetails( List.class.getName() );
case SET -> classDetailsRegistry.resolveClassDetails( Set.class.getName() );
case MAP -> classDetailsRegistry.resolveClassDetails( Map.class.getName() );
};
return new ClassTypeDetailsImpl( collectionClassDetails, TypeDetails.Kind.CLASS );
}
throw new UnsupportedOperationException( "Resolution of dynamic attribute Java type not yet implemented for " + jaxbPersistentAttribute );
}
@ -433,10 +469,9 @@ public class ManagedTypeProcessor {
}
if ( StringHelper.isNotEmpty( jaxbEntity.getExtends() ) ) {
final MutableAnnotationUsage<Extends> extendsAnn = XmlProcessingHelper.makeAnnotation(
Extends.class,
final MutableAnnotationUsage<Extends> extendsAnn = HibernateAnnotations.EXTENDS.createUsage(
classDetails,
xmlDocumentContext
xmlDocumentContext.getModelBuildingContext()
);
extendsAnn.setAttributeValue( "superType", jaxbEntity.getExtends() );
}

View File

@ -24,6 +24,7 @@ import org.hibernate.annotations.CascadeType;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.CollectionType;
import org.hibernate.annotations.DiscriminatorFormula;
import org.hibernate.annotations.DiscriminatorOptions;
import org.hibernate.annotations.Filter;
import org.hibernate.annotations.FilterJoinTable;
import org.hibernate.annotations.JavaType;
@ -55,6 +56,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbConfigurationParameterImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbConvertImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbCustomSqlImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbDiscriminatorColumnImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbDiscriminatorFormulaImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEmbeddedIdImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntity;
import org.hibernate.boot.jaxb.mapping.spi.JaxbEntityListenerImpl;
@ -85,6 +87,7 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbUuidGeneratorImpl;
import org.hibernate.boot.jaxb.mapping.spi.db.JaxbCheckable;
import org.hibernate.boot.jaxb.mapping.spi.db.JaxbColumnJoined;
import org.hibernate.boot.jaxb.mapping.spi.db.JaxbTableMapping;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.categorize.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.xml.internal.db.ColumnProcessing;
@ -94,15 +97,15 @@ import org.hibernate.internal.util.KeyedConsumer;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.ModelsException;
import org.hibernate.models.internal.MutableAnnotationTarget;
import org.hibernate.models.internal.MutableAnnotationUsage;
import org.hibernate.models.internal.MutableClassDetails;
import org.hibernate.models.internal.MutableMemberDetails;
import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.MethodDetails;
import org.hibernate.models.spi.MutableAnnotationTarget;
import org.hibernate.models.spi.MutableAnnotationUsage;
import org.hibernate.models.spi.MutableClassDetails;
import org.hibernate.models.spi.MutableMemberDetails;
import org.hibernate.models.spi.SourceModelBuildingContext;
import org.hibernate.type.SqlTypes;
@ -1583,15 +1586,27 @@ public class XmlAnnotationHelper {
}
public static void applyDiscriminatorFormula(
String discriminatorFormula,
JaxbDiscriminatorFormulaImpl jaxbDiscriminatorFormula,
MutableClassDetails target,
XmlDocumentContext xmlDocumentContext) {
if ( StringHelper.isNotEmpty( discriminatorFormula ) ) {
final MutableAnnotationUsage<DiscriminatorFormula> discriminatorFormulaAnn = XmlProcessingHelper
.getOrMakeAnnotation( DiscriminatorFormula.class, target, xmlDocumentContext );
discriminatorFormulaAnn.setAttributeValue( "value", discriminatorFormula );
if ( jaxbDiscriminatorFormula == null || StringHelper.isEmpty( jaxbDiscriminatorFormula.getFragment() ) ) {
return;
}
// todo add to mapping-3.2.0.xsd discriminatorType of @DiscriminatorFormula
final MutableAnnotationUsage<DiscriminatorFormula> discriminatorFormulaAnn = HibernateAnnotations.DISCRIMINATOR_FORMULA.createUsage(
target,
xmlDocumentContext.getModelBuildingContext()
);
discriminatorFormulaAnn.setAttributeValue( "value", jaxbDiscriminatorFormula.getFragment() );
discriminatorFormulaAnn.setAttributeValue( "discriminatorType", jaxbDiscriminatorFormula.getDiscriminatorType() );
if ( jaxbDiscriminatorFormula.isForceSelection() ) {
final MutableAnnotationUsage<DiscriminatorOptions> existingOptionsAnnotation = (MutableAnnotationUsage<DiscriminatorOptions>) target.getAnnotationUsage( DiscriminatorOptions.class );
final MutableAnnotationUsage<DiscriminatorOptions> optionsAnnotation = existingOptionsAnnotation != null
? existingOptionsAnnotation
: HibernateAnnotations.DISCRIMINATOR_OPTIONS.createUsage( target, xmlDocumentContext.getModelBuildingContext() );
optionsAnnotation.setAttributeValue( "force", true );
}
}
}

View File

@ -14,14 +14,14 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbManagedType;
import org.hibernate.boot.models.MemberResolutionException;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.internal.MutableAnnotationTarget;
import org.hibernate.models.internal.MutableAnnotationUsage;
import org.hibernate.models.internal.MutableClassDetails;
import org.hibernate.models.internal.MutableMemberDetails;
import org.hibernate.models.internal.dynamic.DynamicAnnotationUsage;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.FieldDetails;
import org.hibernate.models.spi.MethodDetails;
import org.hibernate.models.spi.MutableAnnotationTarget;
import org.hibernate.models.spi.MutableAnnotationUsage;
import org.hibernate.models.spi.MutableClassDetails;
import org.hibernate.models.spi.MutableMemberDetails;
import jakarta.persistence.AccessType;

View File

@ -13,22 +13,22 @@ import org.hibernate.annotations.Any;
import org.hibernate.annotations.AnyDiscriminator;
import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyDiscriminatorValues;
import org.hibernate.boot.internal.AnyKeyType;
import org.hibernate.boot.jaxb.mapping.spi.JaxbAnyDiscriminatorValueMappingImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbAnyMappingDiscriminatorImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbAnyMappingImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbAnyMappingKeyImpl;
import org.hibernate.boot.jaxb.mapping.spi.JaxbColumnImpl;
import org.hibernate.boot.internal.AnyKeyType;
import org.hibernate.boot.models.xml.internal.XmlProcessingHelper;
import org.hibernate.boot.models.xml.internal.db.ColumnProcessing;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.models.internal.MutableAnnotationUsage;
import org.hibernate.models.internal.MutableClassDetails;
import org.hibernate.models.internal.MutableMemberDetails;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.ClassDetailsRegistry;
import org.hibernate.models.spi.MutableAnnotationUsage;
import org.hibernate.models.spi.MutableClassDetails;
import org.hibernate.models.spi.MutableMemberDetails;
import jakarta.persistence.AccessType;
import jakarta.persistence.Column;

View File

@ -13,9 +13,9 @@ import org.hibernate.boot.models.xml.internal.XmlProcessingHelper;
import org.hibernate.boot.models.xml.internal.db.ColumnProcessing;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.models.internal.MutableAnnotationUsage;
import org.hibernate.models.internal.MutableClassDetails;
import org.hibernate.models.internal.MutableMemberDetails;
import org.hibernate.models.spi.MutableAnnotationUsage;
import org.hibernate.models.spi.MutableClassDetails;
import org.hibernate.models.spi.MutableMemberDetails;
import jakarta.persistence.AccessType;
import jakarta.persistence.Basic;

View File

@ -10,9 +10,9 @@ import org.hibernate.boot.jaxb.mapping.spi.JaxbIdImpl;
import org.hibernate.boot.models.xml.internal.XmlAnnotationHelper;
import org.hibernate.boot.models.xml.internal.XmlProcessingHelper;
import org.hibernate.boot.models.xml.spi.XmlDocumentContext;
import org.hibernate.models.internal.MutableAnnotationUsage;
import org.hibernate.models.internal.MutableClassDetails;
import org.hibernate.models.internal.MutableMemberDetails;
import org.hibernate.models.spi.MutableClassDetails;
import org.hibernate.models.spi.MutableMemberDetails;
import org.hibernate.models.spi.MutableAnnotationUsage;
import jakarta.persistence.AccessType;
import jakarta.persistence.Basic;

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