HHH-17504 - Ongoing JPA 32 work

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

View File

@ -16,8 +16,6 @@ import org.hibernate.annotations.OptimisticLock;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.AnnotationPlacementException;
import org.hibernate.boot.models.bind.internal.binders.BasicValueBinder;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
@ -93,41 +91,38 @@ public class AttributeBinding extends Binding {
final BasicValue basicValue = new BasicValue( bindingState.getMetadataBuildingContext() );
final MemberDetails member = attributeMetadata.getMember();
bindImplicitJavaType( member, property, basicValue );
BasicValueHelper.bindImplicitJavaType( member, basicValue, bindingOptions, bindingState, bindingContext );
bindMutability( member, property, basicValue );
bindOptimisticLocking( member, property, basicValue );
bindConversion( member, property, basicValue );
processColumn( member, property, basicValue, primaryTable, Column.class );
BasicValueBinder.bindJavaType( member, property, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueBinder.bindJdbcType( member, property, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueBinder.bindLob( member, property, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueBinder.bindNationalized(
BasicValueHelper.bindJavaType( member, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueHelper.bindJdbcType( member, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueHelper.bindLob( member, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueHelper.bindNationalized(
member,
property,
basicValue,
bindingOptions,
bindingState,
bindingContext
);
BasicValueBinder.bindEnumerated(
BasicValueHelper.bindEnumerated(
member,
property,
basicValue,
bindingOptions,
bindingState,
bindingContext
);
BasicValueBinder.bindTemporalPrecision(
BasicValueHelper.bindTemporalPrecision(
member,
property,
basicValue,
bindingOptions,
bindingState,
bindingContext
);
BasicValueBinder.bindTimeZoneStorage(
BasicValueHelper.bindTimeZoneStorage(
member,
property,
basicValue,
@ -156,13 +151,6 @@ public class AttributeBinding extends Binding {
return getProperty();
}
public static void bindImplicitJavaType(
MemberDetails member,
@SuppressWarnings("unused") Property property,
BasicValue basicValue) {
basicValue.setImplicitJavaTypeAccess( (typeConfiguration) -> member.getType().toJavaClass() );
}
private void bindMutability(MemberDetails member, Property property, BasicValue basicValue) {
final var mutabilityAnn = member.getAnnotationUsage( Mutability.class );
final var immutableAnn = member.getAnnotationUsage( Immutable.class );
@ -236,9 +224,18 @@ public class AttributeBinding extends Binding {
BasicValue basicValue,
Table primaryTable,
Class<A> annotation) {
BasicValueHelper.bindColumn(
member,
property::getName,
basicValue,
primaryTable,
bindingOptions,
bindingState,
bindingContext
);
// todo : implicit column
final var columnAnn = member.getAnnotationUsage( annotation );
final var column = ColumnBinder.bindColumn( columnAnn, property::getName );
final var column = ColumnHelper.bindColumn( columnAnn, property::getName );
var tableName = BindingHelper.getValue( columnAnn, "table", "" );
if ( "".equals( tableName ) || tableName == null ) {

View File

@ -0,0 +1,411 @@
/*
* 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.bind.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Supplier;
import org.hibernate.MappingException;
import org.hibernate.annotations.JavaType;
import org.hibernate.annotations.JdbcType;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Nationalized;
import org.hibernate.annotations.TimeZoneColumn;
import org.hibernate.annotations.TimeZoneStorage;
import org.hibernate.annotations.TimeZoneStorageType;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.models.AnnotationPlacementException;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.TableReference;
import org.hibernate.boot.models.categorize.spi.AttributeMetadata;
import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.util.StringHelper;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.RootClass;
import org.hibernate.mapping.Table;
import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.ClassDetails;
import org.hibernate.models.spi.MemberDetails;
import org.hibernate.type.BasicType;
import org.hibernate.type.descriptor.java.BasicJavaType;
import org.hibernate.type.spi.TypeConfiguration;
import jakarta.persistence.AttributeConverter;
import jakarta.persistence.Column;
import jakarta.persistence.Convert;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Lob;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import static jakarta.persistence.EnumType.ORDINAL;
import static java.util.Collections.singletonMap;
import static org.hibernate.annotations.TimeZoneStorageType.AUTO;
import static org.hibernate.annotations.TimeZoneStorageType.COLUMN;
/**
* @author Steve Ebersole
*/
public class BasicValueHelper {
public static final String TENANT_FILTER_NAME = "_tenantId";
public static final String TENANT_PARAMETER_NAME = "tenantId";
public static void bindImplicitJavaType(
MemberDetails member,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
basicValue.setImplicitJavaTypeAccess( (typeConfiguration) -> member.getType().toJavaClass() );
}
public static void bindJavaType(
MemberDetails member,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
// todo : do we need to account for JavaTypeRegistration here?
final var javaTypeAnn = member.getAnnotationUsage( JavaType.class );
if ( javaTypeAnn == null ) {
return;
}
basicValue.setExplicitJavaTypeAccess( (typeConfiguration) -> {
final var classDetails = javaTypeAnn.getClassDetails( "value" );
final Class<BasicJavaType<?>> javaClass = classDetails.toJavaClass();
try {
return javaClass.getConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
final ModelsException modelsException = new ModelsException( "Error instantiating local @JavaType - " + member.getName() );
modelsException.addSuppressed( e );
throw modelsException;
}
} );
}
public static void bindJdbcType(
MemberDetails member,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
// todo : do we need to account for JdbcTypeRegistration here?
final var jdbcTypeAnn = member.getAnnotationUsage( JdbcType.class );
final var jdbcTypeCodeAnn = member.getAnnotationUsage( JdbcTypeCode.class );
if ( jdbcTypeAnn != null ) {
if ( jdbcTypeCodeAnn != null ) {
throw new AnnotationPlacementException(
"Illegal combination of @JdbcType and @JdbcTypeCode - " + member.getName()
);
}
basicValue.setExplicitJdbcTypeAccess( (typeConfiguration) -> {
final var classDetails = jdbcTypeAnn.getClassDetails( "value" );
final Class<org.hibernate.type.descriptor.jdbc.JdbcType> javaClass = classDetails.toJavaClass();
try {
return javaClass.getConstructor().newInstance();
}
catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
final ModelsException modelsException = new ModelsException( "Error instantiating local @JdbcType - " + member.getName() );
modelsException.addSuppressed( e );
throw modelsException;
}
} );
}
else if ( jdbcTypeCodeAnn != null ) {
final Integer typeCode = jdbcTypeCodeAnn.getInteger( "value" );
basicValue.setExplicitJdbcTypeCode( typeCode );
}
}
public static void bindNationalized(
MemberDetails member,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
if ( member.getAnnotationUsage( Nationalized.class ) != null ) {
basicValue.makeNationalized();
}
}
public static void bindLob(
MemberDetails member,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
if ( member.getAnnotationUsage( Lob.class ) != null ) {
basicValue.makeLob();
}
}
public static void bindEnumerated(
MemberDetails member,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
final AnnotationUsage<Enumerated> enumerated = member.getAnnotationUsage( Enumerated.class );
if ( enumerated == null ) {
return;
}
basicValue.setEnumerationStyle( enumerated.getEnum( "value", ORDINAL ) );
}
public static void bindTemporalPrecision(
MemberDetails member,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
final AnnotationUsage<Temporal> temporalAnn = member.getAnnotationUsage( Temporal.class );
if ( temporalAnn == null ) {
return;
}
//noinspection deprecation
final TemporalType precision = temporalAnn.getEnum( "value" );
basicValue.setTemporalPrecision( precision );
}
public static void bindTimeZoneStorage(
MemberDetails member,
Property property,
BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
final AnnotationUsage<TimeZoneStorage> storageAnn = member.getAnnotationUsage( TimeZoneStorage.class );
final AnnotationUsage<TimeZoneColumn> columnAnn = member.getAnnotationUsage( TimeZoneColumn.class );
if ( storageAnn != null ) {
final TimeZoneStorageType strategy = storageAnn.getEnum( "value", AUTO );
if ( strategy != COLUMN && columnAnn != null ) {
throw new AnnotationPlacementException(
"Illegal combination of @TimeZoneStorage(" + strategy.name() + ") and @TimeZoneColumn"
);
}
basicValue.setTimeZoneStorageType( strategy );
}
if ( columnAnn != null ) {
final org.hibernate.mapping.Column column = (org.hibernate.mapping.Column) basicValue.getColumn();
column.setName( columnAnn.getString( "name", property.getName() + "_tz" ) );
column.setSqlType( columnAnn.getString( "columnDefinition", (String) null ) );
final var tableName = columnAnn.getString( "table", (String) null );
TableReference tableByName = null;
if ( tableName != null ) {
final Identifier identifier = Identifier.toIdentifier( tableName );
tableByName = bindingState.getTableByName( identifier.getCanonicalName() );
basicValue.setTable( tableByName.table() );
}
property.setInsertable( columnAnn.getBoolean( "insertable", true ) );
property.setUpdateable( columnAnn.getBoolean( "updatable", true ) );
}
}
public static void bindConversion(
MemberDetails member,
@SuppressWarnings("unused") BasicValue basicValue,
@SuppressWarnings("unused") BindingOptions bindingOptions,
@SuppressWarnings("unused") BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
// todo : do we need to account for auto-applied converters here?
final var convertAnn = member.getAnnotationUsage( Convert.class );
if ( convertAnn == null ) {
return;
}
if ( convertAnn.getBoolean( "disableConversion" ) ) {
return;
}
if ( convertAnn.getString( "attributeName" ) != null ) {
throw new ModelsException( "@Convert#attributeName should not be specified on basic mappings - " + member.getName() );
}
final ClassDetails converterClassDetails = convertAnn.getClassDetails( "converter" );
final Class<AttributeConverter<?, ?>> javaClass = converterClassDetails.toJavaClass();
basicValue.setJpaAttributeConverterDescriptor( new ClassBasedConverterDescriptor(
javaClass,
bindingContext.getClassmateContext()
) );
}
public static org.hibernate.mapping.Column bindColumn(
MemberDetails member,
Supplier<String> defaultNameSupplier,
BasicValue basicValue,
Table primaryTable,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
return bindColumn(
member,
Column.class,
defaultNameSupplier,
basicValue,
primaryTable,
bindingOptions,
bindingState,
bindingContext
);
}
public static <A extends Annotation> org.hibernate.mapping.Column bindColumn(
MemberDetails member,
Class<A> annotationType,
Supplier<String> defaultNameSupplier,
BasicValue basicValue,
Table primaryTable,
@SuppressWarnings("unused") BindingOptions bindingOptions,
BindingState bindingState,
@SuppressWarnings("unused") BindingContext bindingContext) {
final var columnAnn = member.getAnnotationUsage( annotationType );
final var column = ColumnHelper.bindColumn( columnAnn, defaultNameSupplier );
var tableName = BindingHelper.getValue( columnAnn, "table", "" );
if ( StringHelper.isEmpty( tableName ) ) {
basicValue.setTable( primaryTable );
}
else {
final Identifier identifier = Identifier.toIdentifier( tableName );
final TableReference tableByName = bindingState.getTableByName( identifier.getCanonicalName() );
basicValue.setTable( tableByName.table() );
}
basicValue.addColumn( column );
return column;
}
public static void bindTenantId(
EntityTypeMetadata managedType,
RootClass rootClass,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final AttributeMetadata tenantIdAttribute = managedType.getHierarchy().getTenantIdAttribute();
if ( tenantIdAttribute == null ) {
return;
}
final InFlightMetadataCollector collector = bindingState.getMetadataBuildingContext().getMetadataCollector();
final TypeConfiguration typeConfiguration = collector.getTypeConfiguration();
final MemberDetails memberDetails = tenantIdAttribute.getMember();
final String returnedClassName = memberDetails.getType().getClassName();
final BasicType<Object> tenantIdType = typeConfiguration
.getBasicTypeRegistry()
.getRegisteredType( returnedClassName );
final FilterDefinition filterDefinition = collector.getFilterDefinition( TENANT_FILTER_NAME );
if ( filterDefinition == null ) {
collector.addFilterDefinition( new FilterDefinition(
TENANT_FILTER_NAME,
"",
singletonMap( TENANT_PARAMETER_NAME, tenantIdType )
) );
}
else {
final org.hibernate.type.descriptor.java.JavaType<?> tenantIdTypeJtd = tenantIdType.getJavaTypeDescriptor();
final org.hibernate.type.descriptor.java.JavaType<?> parameterJtd = filterDefinition
.getParameterJdbcMapping( TENANT_PARAMETER_NAME )
.getJavaTypeDescriptor();
if ( !parameterJtd.getJavaTypeClass().equals( tenantIdTypeJtd.getJavaTypeClass() ) ) {
throw new MappingException(
"all @TenantId fields must have the same type: "
+ parameterJtd.getJavaType().getTypeName()
+ " differs from "
+ tenantIdTypeJtd.getJavaType().getTypeName()
);
}
}
final Property property = new Property();
rootClass.addProperty( property );
property.setName( tenantIdAttribute.getName() );
final BasicValue basicValue = new BasicValue( bindingState.getMetadataBuildingContext(), rootClass.getRootTable() );
property.setValue( basicValue );
bindImplicitJavaType( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
bindJavaType( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
bindJdbcType( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
bindConversion( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
bindEnumerated( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
BasicValueHelper.bindColumn(
memberDetails,
property::getName,
basicValue,
rootClass.getRootTable(),
bindingOptions,
bindingState,
bindingContext
);
property.resetUpdateable( false );
property.resetOptional( false );
}
public static void bindVersion(
EntityTypeMetadata typeMetadata,
RootClass rootClass,
BindingOptions bindingOptions,
BindingState bindingState,
BindingContext bindingContext) {
final AttributeMetadata versionAttribute = typeMetadata.getHierarchy().getVersionAttribute();
if ( versionAttribute == null ) {
return;
}
final Property property = new Property();
property.setName( versionAttribute.getName() );
rootClass.setVersion( property );
rootClass.addProperty( property );
final BasicValue basicValue = new BasicValue(
bindingState.getMetadataBuildingContext(),
rootClass.getRootTable()
);
property.setValue( basicValue );
final MemberDetails memberDetails = versionAttribute.getMember();
bindImplicitJavaType( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
bindJavaType( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
bindJdbcType( memberDetails, basicValue, bindingOptions, bindingState, bindingContext );
final org.hibernate.mapping.Column column = bindColumn(
memberDetails,
property::getName,
basicValue,
rootClass.getRootTable(),
bindingOptions,
bindingState,
bindingContext
);
// force it to be non-nullable
column.setNullable( false );
}
}

View File

@ -1,10 +1,10 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* SPDX-License-Identifier: Apache-2.0
* Copyright: Red Hat Inc. and Hibernate Authors
* 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.bind.internal.binders;
package org.hibernate.boot.models.bind.internal;
import java.util.function.Supplier;
@ -28,7 +28,7 @@ import static org.hibernate.internal.util.NullnessHelper.nullif;
/**
* @author Steve Ebersole
*/
public class ColumnBinder {
public class ColumnHelper {
public static Column bindColumn(
AnnotationUsage<?> annotationUsage,
Supplier<String> defaultNameSupplier) {
@ -90,7 +90,7 @@ public class ColumnBinder {
return nullif( columnAnnotation.getAttributeValue( "name" ), defaultNameSupplier );
}
private ColumnBinder() {
private ColumnHelper() {
}
public static DiscriminatorType bindDiscriminatorColumn(

View File

@ -6,8 +6,6 @@
*/
package org.hibernate.boot.models.bind.internal;
import org.hibernate.boot.models.bind.internal.binders.BasicValueBinder;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
@ -106,7 +104,7 @@ public class IdentifierBinding extends Binding {
final PrimaryKey primaryKey = table.getPrimaryKey();
final AnnotationUsage<Column> idColumnAnn = idAttributeMember.getAnnotationUsage( Column.class );
final org.hibernate.mapping.Column column = ColumnBinder.bindColumn(
final org.hibernate.mapping.Column column = ColumnHelper.bindColumn(
idColumnAnn,
() -> "id",
true,
@ -115,10 +113,10 @@ public class IdentifierBinding extends Binding {
idValue.addColumn( column, true, false );
primaryKey.addColumn( column );
AttributeBinding.bindImplicitJavaType( idAttributeMember, idProperty, idValue );
BasicValueBinder.bindJavaType( idAttributeMember, idProperty, idValue, options, state, context );
BasicValueBinder.bindJdbcType( idAttributeMember, idProperty, idValue, options, state, context );
BasicValueBinder.bindNationalized( idAttributeMember, idProperty, idValue, options, state, context );
BasicValueHelper.bindImplicitJavaType( idAttributeMember, idValue, options, state, context );
BasicValueHelper.bindJavaType( idAttributeMember, idValue, options, state, context );
BasicValueHelper.bindJdbcType( idAttributeMember, idValue, options, state, context );
BasicValueHelper.bindNationalized( idAttributeMember, idValue, options, state, context );
return idValue;
}

View File

@ -16,7 +16,6 @@ import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.boot.model.relational.Database;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
@ -40,8 +39,8 @@ import jakarta.persistence.DiscriminatorType;
import jakarta.persistence.InheritanceType;
import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER;
import static org.hibernate.boot.models.bind.internal.binders.TenantIdBinder.bindTenantId;
import static org.hibernate.boot.models.bind.internal.binders.VersionBinder.bindVersion;
import static org.hibernate.boot.models.bind.internal.BasicValueHelper.bindTenantId;
import static org.hibernate.boot.models.bind.internal.BasicValueHelper.bindVersion;
import static org.hibernate.internal.util.StringHelper.coalesce;
/**
@ -304,7 +303,7 @@ public class RootEntityBinding extends EntityBinding {
final BasicValue value = new BasicValue( bindingState.getMetadataBuildingContext(), rootClass.getIdentityTable() );
rootClass.setDiscriminator( value );
final DiscriminatorType discriminatorType = ColumnBinder.bindDiscriminatorColumn(
final DiscriminatorType discriminatorType = ColumnHelper.bindDiscriminatorColumn(
bindingContext,
formulaAnn,
value,

View File

@ -10,7 +10,6 @@ import java.util.Collections;
import java.util.List;
import org.hibernate.MappingException;
import org.hibernate.boot.models.bind.internal.binders.ColumnBinder;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState;
@ -172,10 +171,10 @@ public class SubclassEntityBinding extends EntityBinding {
targetColumn,
joinColumnAnns
);
pkColumn = ColumnBinder.bindColumn( joinColumnAnn, targetColumn::getName, true, false );
pkColumn = ColumnHelper.bindColumn( joinColumnAnn, targetColumn::getName, true, false );
}
else {
pkColumn = ColumnBinder.bindColumn( null, targetColumn::getName, true, false );
pkColumn = ColumnHelper.bindColumn( null, targetColumn::getName, true, false );
}
primaryKey.addColumn( pkColumn );
}

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
package org.hibernate.orm.test.boot.models.bind.tenancy;
import org.hibernate.annotations.TenantId;
import org.hibernate.boot.models.bind.internal.binders.TenantIdBinder;
import org.hibernate.boot.models.bind.internal.BasicValueHelper;
import org.hibernate.mapping.BasicValue;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
@ -38,7 +38,7 @@ public class SimpleTenancyTests {
(context) -> {
var metadataCollector = context.getMetadataCollector();
assertThat( metadataCollector.getFilterDefinition( TenantIdBinder.FILTER_NAME ) ).isNotNull();
assertThat( metadataCollector.getFilterDefinition( BasicValueHelper.TENANT_FILTER_NAME ) ).isNotNull();
final PersistentClass entityBinding = metadataCollector.getEntityBinding( ProtectedEntity.class.getName() );
final Property tenantProperty = entityBinding.getProperty( "tenant" );
@ -65,7 +65,7 @@ public class SimpleTenancyTests {
(context) -> {
var metadataCollector = context.getMetadataCollector();
assertThat( metadataCollector.getFilterDefinition( TenantIdBinder.FILTER_NAME ) ).isNotNull();
assertThat( metadataCollector.getFilterDefinition( BasicValueHelper.TENANT_FILTER_NAME ) ).isNotNull();
final PersistentClass entityBinding = metadataCollector.getEntityBinding( ProtectedEntityWithColumn.class.getName() );
final Property tenantProperty = entityBinding.getProperty( "tenant" );