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-05 08:38:54 -06:00
parent 3e20e0939f
commit 2eb3da331b
10 changed files with 326 additions and 143 deletions

View File

@ -10,6 +10,12 @@ import org.hibernate.Internal;
import org.jboss.logging.BasicLogger; import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.ValidIdRange;
import static org.jboss.logging.Logger.Level.INFO;
/** /**
* todo : find the proper min/max id range * todo : find the proper min/max id range
@ -17,8 +23,19 @@ import org.jboss.logging.Logger;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Internal @Internal
@MessageLogger( projectCode = "HHH" )
@ValidIdRange( min = 999980, max = 999999 )
public interface ModelBindingLogging extends BasicLogger { public interface ModelBindingLogging extends BasicLogger {
String NAME = "org.hibernate.models.orm"; String NAME = "org.hibernate.models.orm";
Logger MODEL_BINDING_LOGGER = Logger.getLogger( NAME ); Logger MODEL_BINDING_LOGGER = Logger.getLogger( NAME );
ModelBindingLogging MODEL_BINDING_MSG_LOGGER = Logger.getMessageLogger( ModelBindingLogging.class, NAME );
@LogMessage(level = INFO)
@Message( id = 999980, value = "Entity `%s` used both @DynamicInsert and @SQLInsert" )
void dynamicAndCustomInsert(String entityName);
@LogMessage(level = INFO)
@Message( id = 999981, value = "Entity `%s` used both @DynamicUpdate and @SQLUpdate" )
void dynamicAndCustomUpdate(String entityName);
} }

View File

@ -35,6 +35,7 @@ import jakarta.persistence.Column;
import jakarta.persistence.Convert; import jakarta.persistence.Convert;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.BASIC; import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.BASIC;
import static org.hibernate.boot.models.categorize.spi.AttributeMetadata.AttributeNature.EMBEDDED;
/** /**
* Binding for an attribute * Binding for an attribute
@ -61,16 +62,21 @@ public class AttributeBinding extends Binding {
this.property = new Property(); this.property = new Property();
this.property.setName( attributeMetadata.getName() ); this.property.setName( attributeMetadata.getName() );
final Value value;
if ( attributeMetadata.getNature() == BASIC ) { if ( attributeMetadata.getNature() == BASIC ) {
final var basicValue = createBasicValue( primaryTable ); value = createBasicValue( primaryTable );
property.setValue( basicValue ); }
attributeTable = basicValue.getTable(); else if ( attributeMetadata.getNature() == EMBEDDED ) {
mappingValue = basicValue; value = createComponentValue( primaryTable, owner );
} }
else { else {
throw new UnsupportedOperationException( "Not yet implemented" ); throw new UnsupportedOperationException( "Not yet implemented" );
} }
property.setValue( value );
attributeTable = value.getTable();
mappingValue = value;
applyNaturalId( attributeMetadata, property ); applyNaturalId( attributeMetadata, property );
} }
@ -132,6 +138,16 @@ public class AttributeBinding extends Binding {
return basicValue; return basicValue;
} }
private Component createComponentValue(Table primaryTable, PersistentClass persistentClass) {
final Component component = new Component( bindingState.getMetadataBuildingContext(), persistentClass );
// 1. embeddable (attributes, etc)
// 2. overrides
final MemberDetails member = attributeMetadata.getMember();
return component;
}
public Property getProperty() { public Property getProperty() {
return property; return property;
} }

View File

@ -13,7 +13,6 @@ import java.util.function.Supplier;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.ObjectNameNormalizer; import org.hibernate.boot.model.naming.ObjectNameNormalizer;
import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions; import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState; import org.hibernate.boot.models.bind.spi.BindingState;
import org.hibernate.boot.models.bind.spi.QuotedIdentifierTarget; import org.hibernate.boot.models.bind.spi.QuotedIdentifierTarget;
@ -21,7 +20,6 @@ import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.models.ModelsException; import org.hibernate.models.ModelsException;
import org.hibernate.models.spi.AnnotationDescriptor; import org.hibernate.models.spi.AnnotationDescriptor;
import org.hibernate.models.spi.AnnotationUsage; import org.hibernate.models.spi.AnnotationUsage;
import org.hibernate.models.spi.AttributeDescriptor;
import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER; import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_LOGGER;
@ -29,69 +27,6 @@ import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_L
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class BindingHelper { public class BindingHelper {
public static <T, A extends Annotation> T getValue(
AnnotationUsage<A> annotationUsage,
String attributeName,
Class<A> annotationType,
BindingContext context) {
final T valueOrNull = getValueOrNull( annotationUsage, attributeName );
if ( valueOrNull != null ) {
return valueOrNull;
}
// resolve its default
return getDefaultValue( attributeName, annotationType, context );
}
public static <T, A extends Annotation> T getValueOrNull(
AnnotationUsage<A> annotationUsage,
String attributeName) {
if ( annotationUsage != null ) {
// allow to return null if missing
return annotationUsage.getAttributeValue( attributeName );
}
// there was no annotation...
return null;
}
public static <T,A extends Annotation> T getDefaultValue(
String attributeName,
Class<A> annotationType,
BindingContext context) {
final AnnotationDescriptor<A> annotationDescriptor = context.getAnnotationDescriptorRegistry().getDescriptor( annotationType );
final AttributeDescriptor<Object> attributeDescriptor = annotationDescriptor.getAttribute( attributeName );
//noinspection unchecked
return (T) attributeDescriptor.getAttributeMethod().getDefaultValue();
}
public static <A extends Annotation> String getString(
AnnotationUsage<A> annotationUsage,
String attributeName,
Class<A> annotationType,
BindingContext context) {
return getValue( annotationUsage, attributeName, annotationType, context );
}
public static <A extends Annotation> String getStringOrNull(
AnnotationUsage<A> annotationUsage,
String attributeName) {
return getValueOrNull( annotationUsage, attributeName );
}
public static <A extends Annotation> Identifier getIdentifier(
AnnotationUsage<A> annotationUsage,
String attributeName,
Class<A> annotationType,
QuotedIdentifierTarget target,
BindingOptions options,
JdbcEnvironment jdbcEnvironment,
BindingContext context) {
final String name = getString( annotationUsage, attributeName, annotationType, context );
final boolean globallyQuoted = options.getGloballyQuotedIdentifierTargets().contains( target );
return jdbcEnvironment.getIdentifierHelper().toIdentifier( name, globallyQuoted );
}
public static <A extends Annotation> Identifier toIdentifier( public static <A extends Annotation> Identifier toIdentifier(
String name, String name,
@ -116,17 +51,12 @@ public class BindingHelper {
return (T) descriptor.getAttribute( attributeName ).getAttributeMethod().getDefaultValue(); return (T) descriptor.getAttribute( attributeName ).getAttributeMethod().getDefaultValue();
} }
//noinspection unchecked return ann.getAttributeValue( attributeName );
return getValue(
ann,
attributeName,
() -> (T) descriptor.getAttribute( attributeName ).getAttributeMethod().getDefaultValue()
);
} }
public static <T,A extends Annotation> T getValue(AnnotationUsage<A> ann, String attributeName, Supplier<T> defaultValueSupplier) { public static <T,A extends Annotation> T getValue(AnnotationUsage<A> ann, String attributeName, Supplier<T> defaultValueSupplier) {
if ( ann == null ) { if ( ann == null ) {
return (T) defaultValueSupplier.get(); return defaultValueSupplier.get();
} }
return ann.getAttributeValue( attributeName, defaultValueSupplier ); return ann.getAttributeValue( attributeName, defaultValueSupplier );

View File

@ -6,14 +6,26 @@
*/ */
package org.hibernate.boot.models.bind.internal; package org.hibernate.boot.models.bind.internal;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Filter; import org.hibernate.annotations.Filter;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLInsert;
import org.hibernate.annotations.SQLUpdate;
import org.hibernate.annotations.Synchronize;
import org.hibernate.boot.models.HibernateAnnotations;
import org.hibernate.boot.models.bind.spi.BindingContext; import org.hibernate.boot.models.bind.spi.BindingContext;
import org.hibernate.boot.models.bind.spi.BindingOptions; import org.hibernate.boot.models.bind.spi.BindingOptions;
import org.hibernate.boot.models.bind.spi.BindingState; import org.hibernate.boot.models.bind.spi.BindingState;
@ -24,6 +36,7 @@ import org.hibernate.boot.models.categorize.spi.EntityTypeMetadata;
import org.hibernate.boot.models.categorize.spi.JpaEventListener; import org.hibernate.boot.models.categorize.spi.JpaEventListener;
import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle; import org.hibernate.boot.models.categorize.spi.JpaEventListenerStyle;
import org.hibernate.boot.models.categorize.spi.MappedSuperclassTypeMetadata; import org.hibernate.boot.models.categorize.spi.MappedSuperclassTypeMetadata;
import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle;
import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.jpa.event.internal.EntityCallback; import org.hibernate.jpa.event.internal.EntityCallback;
@ -44,6 +57,8 @@ import jakarta.persistence.DiscriminatorValue;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.SharedCacheMode; import jakarta.persistence.SharedCacheMode;
import static org.hibernate.boot.models.bind.ModelBindingLogging.MODEL_BINDING_MSG_LOGGER;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ -96,6 +111,15 @@ public abstract class EntityBinding extends IdentifiableTypeBinding {
bindingState.getMetadataBuildingContext().getMetadataCollector().addImport( importName, entityName ); bindingState.getMetadataBuildingContext().getMetadataCollector().addImport( importName, entityName );
} }
protected static void applyCommonInformation(EntityTypeMetadata typeMetadata, PersistentClass persistentClass, BindingState bindingState) {
applyCaching( typeMetadata, persistentClass, bindingState );
applyFilters( typeMetadata, persistentClass );
applyJpaEventListeners( typeMetadata, persistentClass );
applyBatchSize( typeMetadata, persistentClass, bindingState );
applySqlCustomizations( typeMetadata, persistentClass, bindingState );
applySynchronizedTableNames( typeMetadata, persistentClass, bindingState );
}
protected static void applyDiscriminatorValue( protected static void applyDiscriminatorValue(
EntityTypeMetadata typeMetadata, EntityTypeMetadata typeMetadata,
PersistentClass persistentClass) { PersistentClass persistentClass) {
@ -357,6 +381,153 @@ public abstract class EntityBinding extends IdentifiableTypeBinding {
} }
} }
private static void applyBatchSize(
EntityTypeMetadata typeMetadata,
PersistentClass persistentClass,
BindingState bindingState) {
final AnnotationUsage<BatchSize> batchSizeAnnotation = typeMetadata
.getClassDetails()
.getAnnotationUsage( HibernateAnnotations.BATCH_SIZE );
if ( batchSizeAnnotation == null ) {
return;
}
persistentClass.setBatchSize( batchSizeAnnotation.getInteger( "size" ) );
}
private static void applySqlCustomizations(
EntityTypeMetadata typeMetadata,
PersistentClass persistentClass,
BindingState bindingState) {
final AnnotationUsage<DynamicInsert> dynamicInsert = typeMetadata
.getClassDetails()
.getAnnotationUsage( HibernateAnnotations.DYNAMIC_INSERT );
final AnnotationUsage<DynamicUpdate> dynamicUpdate = typeMetadata
.getClassDetails()
.getAnnotationUsage( HibernateAnnotations.DYNAMIC_UPDATE );
final List<AnnotationUsage<SQLInsert>> customInserts = typeMetadata
.getClassDetails()
.getRepeatedAnnotationUsages( HibernateAnnotations.SQL_INSERT );
final List<AnnotationUsage<SQLUpdate>> customUpdates = typeMetadata
.getClassDetails()
.getRepeatedAnnotationUsages( HibernateAnnotations.SQL_UPDATE );
final List<AnnotationUsage<SQLDelete>> customDeletes = typeMetadata
.getClassDetails()
.getRepeatedAnnotationUsages( HibernateAnnotations.SQL_DELETE );
if ( dynamicInsert != null ) {
if ( CollectionHelper.isNotEmpty( customInserts ) ) {
MODEL_BINDING_MSG_LOGGER.dynamicAndCustomInsert( persistentClass.getEntityName() );
}
persistentClass.setDynamicInsert( dynamicInsert.getBoolean( "value" ) );
}
if ( dynamicUpdate != null ) {
if ( CollectionHelper.isNotEmpty( customUpdates ) ) {
MODEL_BINDING_MSG_LOGGER.dynamicAndCustomUpdate( persistentClass.getEntityName() );
}
persistentClass.setDynamicUpdate( dynamicUpdate.getBoolean( "value" ) );
}
if ( CollectionHelper.isNotEmpty( customInserts )
|| CollectionHelper.isNotEmpty( customUpdates )
|| CollectionHelper.isNotEmpty( customDeletes ) ) {
final Map<String,Join> joinMap = extractJoinMap( persistentClass );
applyCustomSql(
customInserts,
persistentClass,
joinMap,
PersistentClass::setCustomSQLInsert,
Join::setCustomSQLInsert
);
applyCustomSql(
customUpdates,
persistentClass,
joinMap,
PersistentClass::setCustomSQLUpdate,
Join::setCustomSQLUpdate
);
applyCustomSql(
customDeletes,
persistentClass,
joinMap,
PersistentClass::setCustomSQLDelete,
Join::setCustomSQLDelete
);
}
}
private static Map<String, Join> extractJoinMap(PersistentClass persistentClass) {
final List<Join> joins = persistentClass.getJoins();
if ( CollectionHelper.isEmpty( joins ) ) {
return Collections.emptyMap();
}
final HashMap<String, Join> joinMap = CollectionHelper.mapOfSize( joins.size() );
joins.forEach( (join) -> joinMap.put( join.getTable().getName(), join ) );
return joinMap;
}
private static <A extends Annotation> void applyCustomSql(
List<AnnotationUsage<A>> annotationUsages,
PersistentClass persistentClass,
Map<String,Join> joinMap,
PrimaryCustomSqlInjector primaryTableInjector,
SecondaryCustomSqlInjector secondaryTableInjector) {
if ( CollectionHelper.isEmpty( annotationUsages ) ) {
return;
}
annotationUsages.forEach( annotationUsage -> {
final String tableName = annotationUsage.getString( "table" );
if ( StringHelper.isEmpty( tableName ) ) {
primaryTableInjector.injectCustomSql(
persistentClass,
annotationUsage.getString( "sql" ),
annotationUsage.getBoolean( "callable" ),
ExecuteUpdateResultCheckStyle.fromResultCheckStyle( annotationUsage.getEnum( "", ResultCheckStyle.class ) )
);
}
else {
final Join join = joinMap.get( tableName );
secondaryTableInjector.injectCustomSql(
join,
annotationUsage.getString( "sql" ),
annotationUsage.getBoolean( "callable" ),
ExecuteUpdateResultCheckStyle.fromResultCheckStyle( annotationUsage.getEnum( "", ResultCheckStyle.class ) )
);
}
} );
}
@FunctionalInterface
private interface PrimaryCustomSqlInjector {
void injectCustomSql(PersistentClass persistentClass, String sql, boolean callable, ExecuteUpdateResultCheckStyle checkStyle);
}
@FunctionalInterface
private interface SecondaryCustomSqlInjector {
void injectCustomSql(Join join, String sql, boolean callable, ExecuteUpdateResultCheckStyle checkStyle);
}
private static void applySynchronizedTableNames(
EntityTypeMetadata typeMetadata,
PersistentClass persistentClass,
BindingState bindingState) {
final AnnotationUsage<Synchronize> usage = typeMetadata
.getClassDetails()
.getAnnotationUsage( HibernateAnnotations.SYNCHRONIZE );
if ( usage == null ) {
return;
}
// todo : handle Synchronize#logical - for now assume it is logical
final List<String> names = usage.getList( "value" );
names.forEach( persistentClass::addSynchronizedTable );
}
protected void processSecondaryTables(TableReference primaryTableReference) { protected void processSecondaryTables(TableReference primaryTableReference) {
TableHelper.bindSecondaryTables( TableHelper.bindSecondaryTables(
this, this,

View File

@ -107,12 +107,8 @@ public class RootEntityBinding extends EntityBinding {
applyCacheRegions( typeMetadata, rootClass ); applyCacheRegions( typeMetadata, rootClass );
applySoftDelete( typeMetadata, rootClass, tableReference.table() ); applySoftDelete( typeMetadata, rootClass, tableReference.table() );
applyCaching( typeMetadata, rootClass, bindingState ); applyCommonInformation( typeMetadata, rootClass, bindingState );
applyFilters( typeMetadata, rootClass );
applyJpaEventListeners( typeMetadata, rootClass );
prepareAttributeBindings( tableReference.table() ); prepareAttributeBindings( tableReference.table() );
prepareSubclassBindings(); prepareSubclassBindings();
} }

View File

@ -40,21 +40,21 @@ public class SubclassEntityBinding extends EntityBinding {
private final Subclass subclass; private final Subclass subclass;
public SubclassEntityBinding( public SubclassEntityBinding(
EntityTypeMetadata entityTypeMetadata, EntityTypeMetadata typeMetadata,
IdentifiableTypeBinding superTypeBinding, IdentifiableTypeBinding superTypeBinding,
EntityHierarchy.HierarchyRelation hierarchyRelation, EntityHierarchy.HierarchyRelation hierarchyRelation,
BindingOptions bindingOptions, BindingOptions bindingOptions,
BindingState bindingState, BindingState bindingState,
BindingContext bindingContext) { BindingContext bindingContext) {
super( entityTypeMetadata, superTypeBinding, hierarchyRelation, bindingOptions, bindingState, bindingContext ); super( typeMetadata, superTypeBinding, hierarchyRelation, bindingOptions, bindingState, bindingContext );
this.subclass = createSubclass(); this.subclass = createSubclass();
applyNaming( entityTypeMetadata, subclass, bindingState ); applyNaming( typeMetadata, subclass, bindingState );
bindingState.registerTypeBinding( getTypeMetadata(), this ); bindingState.registerTypeBinding( getTypeMetadata(), this );
if ( subclass instanceof TableOwner ) { if ( subclass instanceof TableOwner ) {
final var primaryTable = TableHelper.bindPrimaryTable( final var primaryTable = TableHelper.bindPrimaryTable(
entityTypeMetadata, typeMetadata,
EntityHierarchy.HierarchyRelation.SUB, EntityHierarchy.HierarchyRelation.SUB,
bindingOptions, bindingOptions,
bindingState, bindingState,
@ -64,7 +64,11 @@ public class SubclassEntityBinding extends EntityBinding {
( (TableOwner) subclass ).setTable( table ); ( (TableOwner) subclass ).setTable( table );
} }
applyDiscriminatorValue( getTypeMetadata(), subclass ); applyDiscriminatorValue( typeMetadata, subclass );
applyCommonInformation( typeMetadata, subclass, bindingState );
prepareAttributeBindings( subclass.getTable() );
prepareSubclassBindings();
} }
@Override @Override
@ -182,10 +186,10 @@ public class SubclassEntityBinding extends EntityBinding {
final AnnotationUsage<ForeignKey> foreignKeyAnn = BindingHelper.getValue( joinTableAnn, "foreignKey", (AnnotationUsage<ForeignKey>) null ); final AnnotationUsage<ForeignKey> foreignKeyAnn = BindingHelper.getValue( joinTableAnn, "foreignKey", (AnnotationUsage<ForeignKey>) null );
final String foreignKeyName = foreignKeyAnn == null final String foreignKeyName = foreignKeyAnn == null
? "" ? ""
: BindingHelper.getString( foreignKeyAnn, "name", ForeignKey.class, bindingContext ); : foreignKeyAnn.getString( "name" );
final String foreignKeyDefinition = foreignKeyAnn == null final String foreignKeyDefinition = foreignKeyAnn == null
? "" ? ""
: BindingHelper.getString( foreignKeyAnn, "foreignKeyDefinition", ForeignKey.class, bindingContext ); : foreignKeyAnn.getString( "foreignKeyDefinition" );
final org.hibernate.mapping.ForeignKey foreignKey = targetTable.createForeignKey( final org.hibernate.mapping.ForeignKey foreignKey = targetTable.createForeignKey(
foreignKeyName, foreignKeyName,

View File

@ -7,7 +7,6 @@
package org.hibernate.boot.models.bind.internal; package org.hibernate.boot.models.bind.internal;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.hibernate.annotations.Comment; import org.hibernate.annotations.Comment;
@ -75,7 +74,6 @@ public class TableHelper {
tableReference = bindPhysicalTable( tableReference = bindPhysicalTable(
entityTypeMetadata, entityTypeMetadata,
tableAnn, tableAnn,
Table.class,
true, true,
bindingOptions, bindingOptions,
bindingState, bindingState,
@ -178,7 +176,6 @@ public class TableHelper {
final Identifier schemaName = resolveDatabaseIdentifier( final Identifier schemaName = resolveDatabaseIdentifier(
secondaryTableAnn, secondaryTableAnn,
"schema", "schema",
jakarta.persistence.SecondaryTable.class,
bindingOptions.getDefaultSchemaName(), bindingOptions.getDefaultSchemaName(),
QuotedIdentifierTarget.SCHEMA_NAME, QuotedIdentifierTarget.SCHEMA_NAME,
bindingOptions, bindingOptions,
@ -188,7 +185,6 @@ public class TableHelper {
final Identifier catalogName = resolveDatabaseIdentifier( final Identifier catalogName = resolveDatabaseIdentifier(
secondaryTableAnn, secondaryTableAnn,
"catalog", "catalog",
jakarta.persistence.SecondaryTable.class,
bindingOptions.getDefaultCatalogName(), bindingOptions.getDefaultCatalogName(),
QuotedIdentifierTarget.CATALOG_NAME, QuotedIdentifierTarget.CATALOG_NAME,
bindingOptions, bindingOptions,
@ -249,7 +245,7 @@ public class TableHelper {
} }
else { else {
// either an explicit or implicit @Table // either an explicit or implicit @Table
tableReference = bindPhysicalTable( type, tableAnn, annotationType, true, bindingOptions, bindingState, bindingContext ); tableReference = bindPhysicalTable( type, tableAnn, true, bindingOptions, bindingState, bindingContext );
} }
return tableReference; return tableReference;
} }
@ -281,7 +277,7 @@ public class TableHelper {
null, null,
null, null,
logicalName.getCanonicalName(), logicalName.getCanonicalName(),
BindingHelper.getString( subselectAnn, "value", Subselect.class, bindingContext ), subselectAnn.getString( "value" ),
true, true,
bindingState.getMetadataBuildingContext() bindingState.getMetadataBuildingContext()
) )
@ -291,13 +287,12 @@ public class TableHelper {
private static <A extends Annotation> PhysicalTableReference bindPhysicalTable( private static <A extends Annotation> PhysicalTableReference bindPhysicalTable(
EntityTypeMetadata type, EntityTypeMetadata type,
AnnotationUsage<A> tableAnn, AnnotationUsage<A> tableAnn,
Class<A> annotationType,
boolean isPrimary, boolean isPrimary,
BindingOptions bindingOptions, BindingOptions bindingOptions,
BindingState bindingState, BindingState bindingState,
BindingContext bindingContext) { BindingContext bindingContext) {
if ( tableAnn != null ) { if ( tableAnn != null ) {
return bindExplicitPhysicalTable( type, tableAnn, annotationType, isPrimary, bindingOptions, bindingState, bindingContext ); return bindExplicitPhysicalTable( type, tableAnn, isPrimary, bindingOptions, bindingState, bindingContext );
} }
else { else {
return bindImplicitPhysicalTable( type, isPrimary, bindingOptions, bindingState, bindingContext ); return bindImplicitPhysicalTable( type, isPrimary, bindingOptions, bindingState, bindingContext );
@ -307,7 +302,6 @@ public class TableHelper {
private static <A extends Annotation> PhysicalTable bindExplicitPhysicalTable( private static <A extends Annotation> PhysicalTable bindExplicitPhysicalTable(
EntityTypeMetadata type, EntityTypeMetadata type,
AnnotationUsage<A> tableAnn, AnnotationUsage<A> tableAnn,
Class<A> annotationType,
boolean isPrimary, boolean isPrimary,
BindingOptions bindingOptions, BindingOptions bindingOptions,
BindingState bindingState, BindingState bindingState,
@ -316,7 +310,6 @@ public class TableHelper {
final Identifier logicalSchemaName = resolveDatabaseIdentifier( final Identifier logicalSchemaName = resolveDatabaseIdentifier(
tableAnn, tableAnn,
"schema", "schema",
annotationType,
bindingOptions.getDefaultSchemaName(), bindingOptions.getDefaultSchemaName(),
QuotedIdentifierTarget.SCHEMA_NAME, QuotedIdentifierTarget.SCHEMA_NAME,
bindingOptions, bindingOptions,
@ -326,7 +319,6 @@ public class TableHelper {
final Identifier logicalCatalogName = resolveDatabaseIdentifier( final Identifier logicalCatalogName = resolveDatabaseIdentifier(
tableAnn, tableAnn,
"catalog", "catalog",
annotationType,
bindingOptions.getDefaultCatalogName(), bindingOptions.getDefaultCatalogName(),
QuotedIdentifierTarget.CATALOG_NAME, QuotedIdentifierTarget.CATALOG_NAME,
bindingOptions, bindingOptions,
@ -412,7 +404,6 @@ public class TableHelper {
final Identifier logicalSchemaName = resolveDatabaseIdentifier( final Identifier logicalSchemaName = resolveDatabaseIdentifier(
tableAnn, tableAnn,
"schema", "schema",
Table.class,
bindingOptions.getDefaultSchemaName(), bindingOptions.getDefaultSchemaName(),
QuotedIdentifierTarget.SCHEMA_NAME, QuotedIdentifierTarget.SCHEMA_NAME,
bindingOptions, bindingOptions,
@ -422,7 +413,6 @@ public class TableHelper {
final Identifier logicalCatalogName = resolveDatabaseIdentifier( final Identifier logicalCatalogName = resolveDatabaseIdentifier(
tableAnn, tableAnn,
"catalog", "catalog",
Table.class,
bindingOptions.getDefaultCatalogName(), bindingOptions.getDefaultCatalogName(),
QuotedIdentifierTarget.CATALOG_NAME, QuotedIdentifierTarget.CATALOG_NAME,
bindingOptions, bindingOptions,
@ -486,23 +476,24 @@ public class TableHelper {
private static <A extends Annotation> Identifier resolveDatabaseIdentifier( private static <A extends Annotation> Identifier resolveDatabaseIdentifier(
AnnotationUsage<A> annotationUsage, AnnotationUsage<A> annotationUsage,
String attributeName, String attributeName,
Class<A> annotationType,
Identifier fallback, Identifier fallback,
QuotedIdentifierTarget target, QuotedIdentifierTarget target,
BindingOptions bindingOptions, BindingOptions bindingOptions,
BindingState bindingState, @SuppressWarnings("unused") BindingState bindingState,
BindingContext bindingContext) { BindingContext bindingContext) {
final String explicit = BindingHelper.getStringOrNull( annotationUsage, attributeName ); if ( annotationUsage == null ) {
if ( StringHelper.isNotEmpty( explicit ) ) {
return BindingHelper.toIdentifier( explicit, target, bindingOptions, jdbcEnvironment( bindingContext ) );
}
if ( fallback != null ) {
return fallback; return fallback;
} }
final String explicitValue = annotationUsage.getString( attributeName );
final String defaultValue = BindingHelper.getDefaultValue( attributeName, annotationType, bindingContext ); if ( StringHelper.isEmpty( explicitValue ) ) {
return BindingHelper.toIdentifier(defaultValue, target, bindingOptions, jdbcEnvironment( bindingContext ) ); return fallback;
}
return BindingHelper.toIdentifier(
explicitValue,
target,
bindingOptions,
jdbcEnvironment( bindingContext )
);
} }
private static AnnotationUsage<Comment> findCommentAnnotation( private static AnnotationUsage<Comment> findCommentAnnotation(

View File

@ -8,14 +8,7 @@ package org.hibernate.boot.models.categorize;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import org.jboss.logging.annotations.ValidIdRange;
import static org.jboss.logging.Logger.Level.INFO;
/** /**
* todo : find the proper min/max id range * todo : find the proper min/max id range
@ -23,19 +16,8 @@ import static org.jboss.logging.Logger.Level.INFO;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Internal @Internal
@MessageLogger( projectCode = "HHH" ) public interface ModelCategorizationLogging {
@ValidIdRange( min = 999901, max = 999999 )
public interface ModelCategorizationLogging extends BasicLogger {
String NAME = "org.hibernate.models.orm"; String NAME = "org.hibernate.models.orm";
Logger MODEL_CATEGORIZATION_LOGGER = Logger.getLogger( NAME ); Logger MODEL_CATEGORIZATION_LOGGER = Logger.getLogger( NAME );
ModelCategorizationLogging MODEL_CATEGORIZATION_MSG_LOGGER = Logger.getMessageLogger( ModelCategorizationLogging.class, NAME );
@LogMessage(level = INFO)
@Message( id = 999901, value = "Entity `%s` used both @DynamicInsert and @SQLInsert" )
void dynamicAndCustomInsert(String entityName);
@LogMessage(level = INFO)
@Message( id = 999902, value = "Entity `%s` used both @DynamicUpdate and @SQLUpdate" )
void dynamicAndCustomUpdate(String entityName);
} }

View File

@ -28,13 +28,13 @@ import static org.assertj.core.api.Assertions.assertThat;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@ServiceRegistry( settingProviders = @SettingProvider(
settingName = AvailableSettings.PHYSICAL_NAMING_STRATEGY,
provider = CustomNamingStrategyProvider.class
) )
public class SimpleBindingCoordinatorTests { public class SimpleBindingCoordinatorTests {
@Test @Test
@ServiceRegistry( settingProviders = @SettingProvider( void testCollectorState(ServiceRegistryScope scope) {
settingName = AvailableSettings.PHYSICAL_NAMING_STRATEGY,
provider = CustomNamingStrategyProvider.class
) )
void testIt(ServiceRegistryScope scope) {
BindingTestingHelper.checkDomainModel( BindingTestingHelper.checkDomainModel(
(context) -> { (context) -> {
final var bindingState = context.getBindingState(); final var bindingState = context.getBindingState();
@ -75,15 +75,17 @@ public class SimpleBindingCoordinatorTests {
assertThat( namespaceItr.hasNext() ).isFalse(); assertThat( namespaceItr.hasNext() ).isFalse();
assertThat( namespace1.getTables() ).hasSize( 1 ); assertThat( namespace1.getTables() ).hasSize( 1 );
assertThat( namespace2.getTables() ).hasSize( 1 ); assertThat( namespace2.getTables() ).hasSize( 1 );
},
scope.getRegistry(),
SimpleEntity.class
);
}
@Test
void testAttributes(ServiceRegistryScope scope) {
BindingTestingHelper.checkDomainModel(
(context) -> {
final RootClass entityBinding = (RootClass) context.getMetadataCollector().getEntityBinding( SimpleEntity.class.getName() ); final RootClass entityBinding = (RootClass) context.getMetadataCollector().getEntityBinding( SimpleEntity.class.getName() );
assertThat( entityBinding.isCached() ).isFalse();
final Column softDeleteColumn = entityBinding.getSoftDeleteColumn();
assertThat( softDeleteColumn ).isNotNull();
assertThat( softDeleteColumn.getName() ).isEqualTo( "ACTIVE" );
assertThat( entityBinding.getFilters() ).hasSize( 1 );
assertThat( entityBinding.getCacheRegionName() ).isEqualTo( "my-region" );
assertThat( entityBinding.getCacheConcurrencyStrategy() ).isEqualTo( CacheConcurrencyStrategy.READ_ONLY.toAccessType().getExternalName() );
final Property id = entityBinding.getProperty( "id" ); final Property id = entityBinding.getProperty( "id" );
assertThat( id.getValue().getTable().getName() ).isEqualTo( "SIMPLETONS" ); assertThat( id.getValue().getTable().getName() ).isEqualTo( "SIMPLETONS" );
@ -125,4 +127,74 @@ public class SimpleBindingCoordinatorTests {
SimpleEntity.class SimpleEntity.class
); );
} }
@Test
void testCaching(ServiceRegistryScope scope) {
BindingTestingHelper.checkDomainModel(
(context) -> {
final RootClass entityBinding = (RootClass) context.getMetadataCollector().getEntityBinding( SimpleEntity.class.getName() );
assertThat( entityBinding.isCached() ).isFalse();
assertThat( entityBinding.getCacheRegionName() ).isEqualTo( "my-region" );
assertThat( entityBinding.getCacheConcurrencyStrategy() ).isEqualTo( CacheConcurrencyStrategy.READ_ONLY.toAccessType().getExternalName() );
},
scope.getRegistry(),
SimpleEntity.class
);
}
@Test
void testBatchSize(ServiceRegistryScope scope) {
BindingTestingHelper.checkDomainModel(
(context) -> {
final RootClass entityBinding = (RootClass) context.getMetadataCollector().getEntityBinding( SimpleEntity.class.getName() );
assertThat( entityBinding.getBatchSize() ).isEqualTo( 32 );
},
scope.getRegistry(),
SimpleEntity.class
);
}
@Test
void testSoftDelete(ServiceRegistryScope scope) {
BindingTestingHelper.checkDomainModel(
(context) -> {
final RootClass entityBinding = (RootClass) context.getMetadataCollector().getEntityBinding( SimpleEntity.class.getName() );
final Column softDeleteColumn = entityBinding.getSoftDeleteColumn();
assertThat( softDeleteColumn ).isNotNull();
assertThat( softDeleteColumn.getName() ).isEqualTo( "ACTIVE" );
},
scope.getRegistry(),
SimpleEntity.class
);
}
@Test
void testSynchronization(ServiceRegistryScope scope) {
BindingTestingHelper.checkDomainModel(
(context) -> {
final RootClass entityBinding = (RootClass) context.getMetadataCollector().getEntityBinding( SimpleEntity.class.getName() );
assertThat( entityBinding.getSynchronizedTables() ).hasSize( 1 );
assertThat( entityBinding.getFilters() ).hasSize( 1 );
},
scope.getRegistry(),
SimpleEntity.class
);
}
@Test
void testFilters(ServiceRegistryScope scope) {
BindingTestingHelper.checkDomainModel(
(context) -> {
final RootClass entityBinding = (RootClass) context.getMetadataCollector().getEntityBinding( SimpleEntity.class.getName() );
assertThat( entityBinding.getFilters() ).hasSize( 1 );
},
scope.getRegistry(),
SimpleEntity.class
);
}
} }

View File

@ -6,6 +6,7 @@
*/ */
package org.hibernate.orm.test.boot.models.bind; package org.hibernate.orm.test.boot.models.bind;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache; import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Filter; import org.hibernate.annotations.Filter;
@ -13,6 +14,7 @@ import org.hibernate.annotations.FilterDef;
import org.hibernate.annotations.ParamDef; import org.hibernate.annotations.ParamDef;
import org.hibernate.annotations.SoftDelete; import org.hibernate.annotations.SoftDelete;
import org.hibernate.annotations.SoftDeleteType; import org.hibernate.annotations.SoftDeleteType;
import org.hibernate.annotations.Synchronize;
import org.hibernate.annotations.TenantId; import org.hibernate.annotations.TenantId;
import jakarta.persistence.Basic; import jakarta.persistence.Basic;
@ -39,6 +41,8 @@ import jakarta.persistence.Version;
@SoftDelete(strategy = SoftDeleteType.ACTIVE) @SoftDelete(strategy = SoftDeleteType.ACTIVE)
@Cacheable(false) @Cacheable(false)
@Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "my-region") @Cache(usage = CacheConcurrencyStrategy.READ_ONLY, region = "my-region")
@BatchSize(size = 32)
@Synchronize("some_other_table")
public class SimpleEntity { public class SimpleEntity {
@Id @Id
private Integer id; private Integer id;