move methods onto EntityBinder and CollectionBinder

This commit is contained in:
Gavin King 2022-09-30 14:19:08 +02:00
parent 4f4fd736dc
commit ee08db2a1e
10 changed files with 1654 additions and 1578 deletions

View File

@ -37,7 +37,7 @@ import org.hibernate.mapping.Table;
import org.jboss.logging.Logger;
import static org.hibernate.cfg.AnnotationBinder.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getRelativePath;
import static org.hibernate.internal.util.StringHelper.isNotEmpty;
@ -851,7 +851,7 @@ public class AnnotatedColumn {
private void applyCheckConstraint(PropertyData inferredData, int length) {
final XProperty xProperty = inferredData.getProperty();
if ( xProperty != null ) {
Check check = AnnotationBinder.getOverridableAnnotation( xProperty, Check.class, context );
Check check = getOverridableAnnotation( xProperty, Check.class, context );
if ( check != null ) {
if (length!=1) {
throw new MappingException("@Check may only be applied to single-column mappings (use a table-level @Check)");

View File

@ -7,7 +7,10 @@
package org.hibernate.cfg;
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.Collections;
import java.util.HashMap;
import java.util.HashSet;
@ -18,23 +21,30 @@ import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.function.Consumer;
import java.util.stream.Stream;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToOne;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.MappingException;
import org.hibernate.annotations.AnyDiscriminatorValue;
import org.hibernate.annotations.AnyDiscriminatorValues;
import org.hibernate.annotations.DialectOverride;
import org.hibernate.annotations.Formula;
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.XProperty;
import org.hibernate.boot.model.IdGeneratorStrategyInterpreter;
import org.hibernate.boot.model.IdGeneratorStrategyInterpreter.GeneratorNameDeterminationContext;
import org.hibernate.boot.model.IdentifierGeneratorDefinition;
import org.hibernate.boot.spi.InFlightMetadataCollector;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.cfg.annotations.BasicValueBinder;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.cfg.annotations.Nullability;
import org.hibernate.dialect.Dialect;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.id.PersistentIdentifierGenerator;
import org.hibernate.internal.CoreLogging;
@ -938,21 +948,17 @@ public class BinderHelper {
MetadataBuildingContext buildingContext) {
final XClass persistentXClass = buildingContext.getBootstrapContext().getReflectionManager()
.toXClass( propertyHolder.getPersistentClass().getMappedClass() );
final InFlightMetadataCollector metadataCollector = buildingContext.getMetadataCollector();
if ( propertyHolder.isInIdClass() ) {
PropertyData pd = buildingContext.getMetadataCollector().getPropertyAnnotatedWithIdAndToOne(
persistentXClass,
propertyName
);
PropertyData pd = metadataCollector.getPropertyAnnotatedWithIdAndToOne( persistentXClass, propertyName );
if ( pd == null && buildingContext.getBuildingOptions().isSpecjProprietarySyntaxEnabled() ) {
pd = buildingContext.getMetadataCollector().getPropertyAnnotatedWithMapsId(
persistentXClass,
propertyName
);
pd = metadataCollector.getPropertyAnnotatedWithMapsId( persistentXClass, propertyName );
}
return pd;
}
return buildingContext.getMetadataCollector()
.getPropertyAnnotatedWithMapsId( persistentXClass, isId ? "" : propertyName);
else {
return metadataCollector.getPropertyAnnotatedWithMapsId( persistentXClass, isId ? "" : propertyName);
}
}
public static Map<String,String> toAliasTableMap(SqlFragmentAlias[] aliases){
@ -974,4 +980,62 @@ public class BinderHelper {
}
return ret;
}
public static boolean hasToOneAnnotation(XAnnotatedElement property) {
return property.isAnnotationPresent(ManyToOne.class)
|| property.isAnnotationPresent(OneToOne.class);
}
public static <T extends Annotation> T getOverridableAnnotation(
XAnnotatedElement element,
Class<T> annotationType,
MetadataBuildingContext context) {
Dialect dialect = context.getMetadataCollector().getDatabase().getDialect();
Iterator<Annotation> annotations =
Arrays.stream( element.getAnnotations() )
.flatMap(annotation -> {
try {
Method value = annotation.annotationType().getDeclaredMethod("value");
Class<?> returnType = value.getReturnType();
if ( returnType.isArray()
&& returnType.getComponentType().isAnnotationPresent(Repeatable.class)
&& returnType.getComponentType().isAnnotationPresent(DialectOverride.OverridesAnnotation.class) ) {
return Stream.of( (Annotation[]) value.invoke(annotation) );
}
}
catch (NoSuchMethodException ignored) {}
catch (Exception e) {
throw new AssertionFailure("could not read @DialectOverride annotation", e);
}
return Stream.of(annotation);
}).iterator();
while ( annotations.hasNext() ) {
Annotation annotation = annotations.next();
Class<? extends Annotation> type = annotation.annotationType();
DialectOverride.OverridesAnnotation overridesAnnotation = type.getAnnotation(DialectOverride.OverridesAnnotation.class);
if ( overridesAnnotation != null
&& overridesAnnotation.value().equals(annotationType) ) {
try {
//noinspection unchecked
Class<? extends Dialect> overrideDialect = (Class<? extends Dialect>)
type.getDeclaredMethod("dialect").invoke(annotation);
if ( overrideDialect.isAssignableFrom( dialect.getClass() ) ) {
DialectOverride.Version before = (DialectOverride.Version)
type.getDeclaredMethod("before").invoke(annotation);
DialectOverride.Version sameOrAfter = (DialectOverride.Version)
type.getDeclaredMethod("sameOrAfter").invoke(annotation);
if ( dialect.getVersion().isBefore( before.major(), before.minor() )
&& dialect.getVersion().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 );
}
}

View File

@ -32,7 +32,7 @@ import static org.hibernate.cfg.AnnotatedColumn.buildColumnFromAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnFromNoAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnsFromAnnotations;
import static org.hibernate.cfg.AnnotatedColumn.buildFormulaFromAnnotation;
import static org.hibernate.cfg.AnnotationBinder.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getPath;
import static org.hibernate.cfg.BinderHelper.getPropertyOverriddenByMapperOrMapsId;

View File

@ -5,6 +5,7 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cfg;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -74,11 +75,11 @@ public class InheritanceState {
}
}
boolean hasTable() {
public boolean hasTable() {
return !hasParents() || !InheritanceType.SINGLE_TABLE.equals( getType() );
}
boolean hasDenormalizedTable() {
public boolean hasDenormalizedTable() {
return hasParents() && InheritanceType.TABLE_PER_CLASS.equals( getType() );
}
@ -150,7 +151,7 @@ public class InheritanceState {
isEmbeddableSuperclass = embeddableSuperclass;
}
void postProcess(PersistentClass persistenceClass, EntityBinder entityBinder) {
public void postProcess(PersistentClass persistenceClass, EntityBinder entityBinder) {
//make sure we run elements to process
getElementsToProcess();
addMappedSuperClassInMetadata( persistenceClass );
@ -319,7 +320,7 @@ public class InheritanceState {
}
}
static final class ElementsToProcess {
public static final class ElementsToProcess {
private final List<PropertyData> properties;
private final int idPropertyCount;

View File

@ -48,10 +48,6 @@ import jakarta.persistence.Transient;
* @author Hardy Ferentschik
*/
class PropertyContainer {
//
// static {
// System.setProperty("jboss.i18n.generate-proxies", "true");
// }
private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, PropertyContainer.class.getName());

View File

@ -19,6 +19,7 @@ import jakarta.persistence.AttributeOverrides;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.ConstraintMode;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Embedded;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinColumns;
@ -26,9 +27,12 @@ import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.MapKey;
import jakarta.persistence.MapKeyColumn;
import jakarta.persistence.MapKeyJoinColumn;
import jakarta.persistence.MapKeyJoinColumns;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OrderColumn;
import jakarta.persistence.UniqueConstraint;
import org.hibernate.AnnotationException;
import org.hibernate.AssertionFailure;
import org.hibernate.FetchMode;
@ -36,11 +40,14 @@ import org.hibernate.MappingException;
import org.hibernate.annotations.Bag;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CollectionId;
import org.hibernate.annotations.CollectionIdJavaType;
import org.hibernate.annotations.CollectionIdJdbcType;
import org.hibernate.annotations.CollectionIdJdbcTypeCode;
import org.hibernate.annotations.CollectionType;
import org.hibernate.annotations.Columns;
import org.hibernate.annotations.Comment;
import org.hibernate.annotations.CompositeType;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.Filter;
@ -59,6 +66,12 @@ import org.hibernate.annotations.ListIndexJdbcType;
import org.hibernate.annotations.ListIndexJdbcTypeCode;
import org.hibernate.annotations.Loader;
import org.hibernate.annotations.ManyToAny;
import org.hibernate.annotations.MapKeyJavaType;
import org.hibernate.annotations.MapKeyJdbcType;
import org.hibernate.annotations.MapKeyJdbcTypeCode;
import org.hibernate.annotations.MapKeyMutability;
import org.hibernate.annotations.MapKeyType;
import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;
import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;
@ -74,6 +87,7 @@ import org.hibernate.annotations.SortComparator;
import org.hibernate.annotations.SortNatural;
import org.hibernate.annotations.Where;
import org.hibernate.annotations.WhereJoinTable;
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.BootLogging;
@ -91,14 +105,17 @@ import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.CollectionPropertyHolder;
import org.hibernate.cfg.CollectionSecondPass;
import org.hibernate.cfg.ComponentPropertyHolder;
import org.hibernate.cfg.IndexColumn;
import org.hibernate.cfg.InheritanceState;
import org.hibernate.cfg.NotYetImplementedException;
import org.hibernate.cfg.PropertyData;
import org.hibernate.cfg.PropertyHolder;
import org.hibernate.cfg.PropertyHolderBuilder;
import org.hibernate.cfg.PropertyInferredData;
import org.hibernate.cfg.PropertyPreloadedData;
import org.hibernate.cfg.SecondPass;
import org.hibernate.cfg.WrappedInferredData;
import org.hibernate.engine.config.spi.ConfigurationService;
import org.hibernate.engine.spi.FilterDefinition;
import org.hibernate.internal.CoreMessageLogger;
@ -130,12 +147,20 @@ import org.hibernate.usertype.UserCollectionType;
import org.jboss.logging.Logger;
import static jakarta.persistence.AccessType.PROPERTY;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnFromAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnFromNoAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.buildColumnsFromAnnotations;
import static org.hibernate.cfg.AnnotatedColumn.buildFormulaFromAnnotation;
import static org.hibernate.cfg.AnnotatedColumn.checkPropertyConsistency;
import static org.hibernate.cfg.AnnotatedJoinColumn.buildJoinColumnsWithDefaultColumnSuffix;
import static org.hibernate.cfg.AnnotatedJoinColumn.buildJoinTableJoinColumns;
import static org.hibernate.cfg.AnnotationBinder.fillComponent;
import static org.hibernate.cfg.AnnotationBinder.getOverridableAnnotation;
import static org.hibernate.cfg.AnnotationBinder.getCascadeStrategy;
import static org.hibernate.cfg.BinderHelper.PRIMITIVE_NAMES;
import static org.hibernate.cfg.BinderHelper.buildAnyValue;
import static org.hibernate.cfg.BinderHelper.createSyntheticPropertyReference;
import static org.hibernate.cfg.BinderHelper.getOverridableAnnotation;
import static org.hibernate.cfg.BinderHelper.getPath;
import static org.hibernate.cfg.BinderHelper.isEmptyAnnotationValue;
import static org.hibernate.cfg.BinderHelper.toAliasEntityMap;
import static org.hibernate.cfg.BinderHelper.toAliasTableMap;
@ -219,6 +244,438 @@ public abstract class CollectionBinder {
this.buildingContext = buildingContext;
}
public static void bindCollection(
PropertyHolder propertyHolder,
Nullability nullability,
PropertyData inferredData,
Map<String, IdentifierGeneratorDefinition> classGenerators,
EntityBinder entityBinder,
boolean isIdentifierMapper,
MetadataBuildingContext context,
Map<XClass, InheritanceState> inheritanceStatePerClass,
XProperty property,
AnnotatedJoinColumn[] joinColumns) {
final OneToMany oneToManyAnn = property.getAnnotation( OneToMany.class );
final ManyToMany manyToManyAnn = property.getAnnotation( ManyToMany.class );
final ElementCollection elementCollectionAnn = property.getAnnotation( ElementCollection.class );
if ( ( oneToManyAnn != null || manyToManyAnn != null || elementCollectionAnn != null )
&& isToManyAssociationWithinEmbeddableCollection(propertyHolder) ) {
throw new AnnotationException(
"@OneToMany, @ManyToMany or @ElementCollection cannot be used inside an @Embeddable that is also contained within an @ElementCollection: "
+ getPath(propertyHolder, inferredData)
);
}
if ( property.isAnnotationPresent( OrderColumn.class )
&& manyToManyAnn != null && !manyToManyAnn.mappedBy().isEmpty() ) {
throw new AnnotationException(
"Explicit @OrderColumn on inverse side of @ManyToMany is illegal: "
+ getPath(propertyHolder, inferredData)
);
}
final IndexColumn indexColumn = IndexColumn.fromAnnotations(
property.getAnnotation( OrderColumn.class ),
property.getAnnotation( org.hibernate.annotations.IndexColumn.class ),
property.getAnnotation( ListIndexBase.class ),
propertyHolder,
inferredData,
entityBinder.getSecondaryTables(),
context
);
CollectionBinder collectionBinder = getCollectionBinder( property, hasMapKeyAnnotation( property ), context );
collectionBinder.setIndexColumn( indexColumn );
collectionBinder.setMapKey( property.getAnnotation( MapKey.class ) );
collectionBinder.setPropertyName( inferredData.getPropertyName() );
collectionBinder.setBatchSize( property.getAnnotation( BatchSize.class ) );
collectionBinder.setJpaOrderBy( property.getAnnotation( jakarta.persistence.OrderBy.class ) );
collectionBinder.setSqlOrderBy( getOverridableAnnotation( property, OrderBy.class, context ) );
collectionBinder.setNaturalSort( property.getAnnotation( SortNatural.class ) );
collectionBinder.setComparatorSort( property.getAnnotation( SortComparator.class ) );
collectionBinder.setCache( property.getAnnotation( Cache.class ) );
collectionBinder.setPropertyHolder(propertyHolder);
Cascade hibernateCascade = property.getAnnotation( Cascade.class );
NotFound notFound = property.getAnnotation( NotFound.class );
if ( notFound != null ) {
if ( manyToManyAnn == null ) {
throw new AnnotationException("collection annotated @NotFound is not a @ManyToMany association: "
+ getPath(propertyHolder, inferredData) );
}
collectionBinder.setNotFoundAction( notFound.action() );
}
collectionBinder.setCollectionType( inferredData.getProperty().getElementClass() );
collectionBinder.setAccessType( inferredData.getDefaultAccess() );
//do not use "element" if you are a JPA 2 @ElementCollection, only for legacy Hibernate mappings
final PropertyData virtualProperty = property.isAnnotationPresent( ElementCollection.class )
? inferredData
: new WrappedInferredData( inferredData, "element" );
final Comment comment = property.getAnnotation(Comment.class);
final AnnotatedColumn[] elementColumns = elementColumns(
propertyHolder,
nullability,
entityBinder,
context,
property,
virtualProperty,
comment
);
final JoinColumn[] joinKeyColumns = mapKeyColumns(
propertyHolder,
inferredData,
entityBinder,
context,
property,
collectionBinder,
comment
);
final AnnotatedJoinColumn[] mapJoinColumns = buildJoinColumnsWithDefaultColumnSuffix(
joinKeyColumns,
comment,
null,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
"_KEY",
context
);
collectionBinder.setMapKeyManyToManyColumns( mapJoinColumns );
//potential element
collectionBinder.setEmbedded( property.isAnnotationPresent( Embedded.class ) );
collectionBinder.setElementColumns( elementColumns );
collectionBinder.setProperty(property);
final String mappedBy = handleTargetEntity(
propertyHolder,
inferredData,
context,
property,
joinColumns,
oneToManyAnn,
manyToManyAnn,
elementCollectionAnn,
collectionBinder,
hibernateCascade
);
bindJoinedTableAssociation(
property,
context,
entityBinder,
collectionBinder,
propertyHolder,
inferredData,
mappedBy
);
OnDelete onDeleteAnn = property.getAnnotation( OnDelete.class );
boolean onDeleteCascade = onDeleteAnn != null && OnDeleteAction.CASCADE == onDeleteAnn.action();
collectionBinder.setCascadeDeleteEnabled( onDeleteCascade );
if ( isIdentifierMapper ) {
collectionBinder.setInsertable( false );
collectionBinder.setUpdatable( false );
}
if ( property.isAnnotationPresent( CollectionId.class ) ) { //do not compute the generators unless necessary
HashMap<String, IdentifierGeneratorDefinition> localGenerators = new HashMap<>(classGenerators);
localGenerators.putAll( AnnotationBinder.buildGenerators(property, context) );
collectionBinder.setLocalGenerators( localGenerators );
}
collectionBinder.setInheritanceStatePerClass(inheritanceStatePerClass);
collectionBinder.setDeclaringClass( inferredData.getDeclaringClass() );
collectionBinder.bind();
}
private static String handleTargetEntity(
PropertyHolder propertyHolder,
PropertyData inferredData,
MetadataBuildingContext context,
XProperty property,
AnnotatedJoinColumn[] joinColumns,
OneToMany oneToManyAnn,
ManyToMany manyToManyAnn,
ElementCollection elementCollectionAnn,
CollectionBinder collectionBinder,
Cascade hibernateCascade) {
//TODO enhance exception with @ManyToAny and @CollectionOfElements
if ( oneToManyAnn != null && manyToManyAnn != null ) {
throw new AnnotationException(
"@OneToMany and @ManyToMany on the same property is not allowed: "
+ propertyHolder.getEntityName() + "." + inferredData.getPropertyName()
);
}
String mappedBy = null;
ReflectionManager reflectionManager = context.getBootstrapContext().getReflectionManager();
if ( oneToManyAnn != null ) {
for ( AnnotatedJoinColumn column : joinColumns) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns(joinColumns);
mappedBy = oneToManyAnn.mappedBy();
//noinspection unchecked
collectionBinder.setTargetEntity( reflectionManager.toXClass( oneToManyAnn.targetEntity() ) );
collectionBinder.setCascadeStrategy(
getCascadeStrategy( oneToManyAnn.cascade(), hibernateCascade, oneToManyAnn.orphanRemoval(), false )
);
collectionBinder.setOneToMany( true );
}
else if ( elementCollectionAnn != null ) {
for ( AnnotatedJoinColumn column : joinColumns) {
if ( column.isSecondary() ) {
throw new NotYetImplementedException( "Collections having FK in secondary table" );
}
}
collectionBinder.setFkJoinColumns(joinColumns);
mappedBy = "";
final Class<?> targetElement = elementCollectionAnn.targetClass();
collectionBinder.setTargetEntity( reflectionManager.toXClass( targetElement ) );
//collectionBinder.setCascadeStrategy( getCascadeStrategy( embeddedCollectionAnn.cascade(), hibernateCascade ) );
collectionBinder.setOneToMany( true );
}
else if ( manyToManyAnn != null ) {
mappedBy = manyToManyAnn.mappedBy();
//noinspection unchecked
collectionBinder.setTargetEntity( reflectionManager.toXClass( manyToManyAnn.targetEntity() ) );
collectionBinder.setCascadeStrategy(
getCascadeStrategy( manyToManyAnn.cascade(), hibernateCascade, false, false )
);
collectionBinder.setOneToMany( false );
}
else if ( property.isAnnotationPresent( ManyToAny.class ) ) {
mappedBy = "";
collectionBinder.setTargetEntity( reflectionManager.toXClass( void.class ) );
collectionBinder.setCascadeStrategy(
getCascadeStrategy( null, hibernateCascade, false, false )
);
collectionBinder.setOneToMany( false );
}
collectionBinder.setMappedBy( mappedBy );
return mappedBy;
}
private static boolean hasMapKeyAnnotation(XProperty property) {
return property.isAnnotationPresent(MapKeyJavaType.class)
|| property.isAnnotationPresent(MapKeyJdbcType.class)
|| property.isAnnotationPresent(MapKeyJdbcTypeCode.class)
|| property.isAnnotationPresent(MapKeyMutability.class)
|| property.isAnnotationPresent(MapKey.class)
|| property.isAnnotationPresent(MapKeyType.class);
}
private static boolean isToManyAssociationWithinEmbeddableCollection(PropertyHolder propertyHolder) {
if(propertyHolder instanceof ComponentPropertyHolder) {
ComponentPropertyHolder componentPropertyHolder = (ComponentPropertyHolder) propertyHolder;
return componentPropertyHolder.isWithinElementCollection();
}
return false;
}
private static AnnotatedColumn[] elementColumns(
PropertyHolder propertyHolder,
Nullability nullability,
EntityBinder entityBinder,
MetadataBuildingContext context,
XProperty property,
PropertyData virtualProperty,
Comment comment) {
if ( property.isAnnotationPresent( jakarta.persistence.Column.class ) ) {
return buildColumnFromAnnotation(
property.getAnnotation( jakarta.persistence.Column.class ),
comment,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
context
);
}
else if ( property.isAnnotationPresent( Formula.class ) ) {
return buildFormulaFromAnnotation(
getOverridableAnnotation(property, Formula.class, context),
comment,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
context
);
}
else if ( property.isAnnotationPresent( Columns.class ) ) {
return buildColumnsFromAnnotations(
property.getAnnotation( Columns.class ).columns(),
comment,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
context
);
}
else {
return buildColumnFromNoAnnotation(
comment,
nullability,
propertyHolder,
virtualProperty,
entityBinder.getSecondaryTables(),
context
);
}
}
private static JoinColumn[] mapKeyColumns(
PropertyHolder propertyHolder,
PropertyData inferredData,
EntityBinder entityBinder,
MetadataBuildingContext context,
XProperty property,
CollectionBinder collectionBinder,
Comment comment) {
final jakarta.persistence.Column[] keyColumns = property.isAnnotationPresent(MapKeyColumn.class)
? new jakarta.persistence.Column[]{ new MapKeyColumnDelegator( property.getAnnotation(MapKeyColumn.class) ) }
: null;
final AnnotatedColumn[] mapColumns = buildColumnsFromAnnotations(
keyColumns,
comment,
Nullability.FORCED_NOT_NULL,
propertyHolder,
inferredData,
"_KEY",
entityBinder.getSecondaryTables(),
context
);
collectionBinder.setMapKeyColumns( mapColumns );
if ( property.isAnnotationPresent( MapKeyJoinColumns.class ) ) {
final MapKeyJoinColumn[] mapKeyJoinColumns = property.getAnnotation( MapKeyJoinColumns.class ).value();
JoinColumn[] joinKeyColumns = new JoinColumn[mapKeyJoinColumns.length];
int index = 0;
for ( MapKeyJoinColumn joinColumn : mapKeyJoinColumns ) {
joinKeyColumns[index] = new MapKeyJoinColumnDelegator( joinColumn );
index++;
}
if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
throw new AnnotationException(
"@MapKeyJoinColumn and @MapKeyJoinColumns used on the same property: "
+ getPath(propertyHolder, inferredData)
);
}
return joinKeyColumns;
}
else if ( property.isAnnotationPresent( MapKeyJoinColumn.class ) ) {
return new JoinColumn[] {
new MapKeyJoinColumnDelegator(
property.getAnnotation(
MapKeyJoinColumn.class
)
)
};
}
return null;
}
private static void bindJoinedTableAssociation(
XProperty property,
MetadataBuildingContext buildingContext,
EntityBinder entityBinder,
CollectionBinder collectionBinder,
PropertyHolder propertyHolder,
PropertyData inferredData,
String mappedBy) {
TableBinder associationTableBinder = new TableBinder();
JoinColumn[] annJoins;
JoinColumn[] annInverseJoins;
JoinTable assocTable = propertyHolder.getJoinTable( property );
CollectionTable collectionTable = property.getAnnotation( CollectionTable.class );
if ( assocTable != null || collectionTable != null ) {
final String catalog;
final String schema;
final String tableName;
final UniqueConstraint[] uniqueConstraints;
final JoinColumn[] joins;
final JoinColumn[] inverseJoins;
final jakarta.persistence.Index[] jpaIndexes;
//JPA 2 has priority
if ( collectionTable != null ) {
catalog = collectionTable.catalog();
schema = collectionTable.schema();
tableName = collectionTable.name();
uniqueConstraints = collectionTable.uniqueConstraints();
joins = collectionTable.joinColumns();
inverseJoins = null;
jpaIndexes = collectionTable.indexes();
}
else {
catalog = assocTable.catalog();
schema = assocTable.schema();
tableName = assocTable.name();
uniqueConstraints = assocTable.uniqueConstraints();
joins = assocTable.joinColumns();
inverseJoins = assocTable.inverseJoinColumns();
jpaIndexes = assocTable.indexes();
}
collectionBinder.setExplicitAssociationTable( true );
if ( jpaIndexes != null && jpaIndexes.length > 0 ) {
associationTableBinder.setJpaIndex( jpaIndexes );
}
if ( !isEmptyAnnotationValue( schema ) ) {
associationTableBinder.setSchema( schema );
}
if ( !isEmptyAnnotationValue( catalog ) ) {
associationTableBinder.setCatalog( catalog );
}
if ( !isEmptyAnnotationValue( tableName ) ) {
associationTableBinder.setName( tableName );
}
associationTableBinder.setUniqueConstraints( uniqueConstraints );
associationTableBinder.setJpaIndex( jpaIndexes );
//set check constraint in the second pass
annJoins = joins.length == 0 ? null : joins;
annInverseJoins = inverseJoins == null || inverseJoins.length == 0 ? null : inverseJoins;
}
else {
annJoins = null;
annInverseJoins = null;
}
AnnotatedJoinColumn[] joinColumns = buildJoinTableJoinColumns(
annJoins,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
mappedBy,
buildingContext
);
AnnotatedJoinColumn[] inverseJoinColumns = buildJoinTableJoinColumns(
annInverseJoins,
entityBinder.getSecondaryTables(),
propertyHolder,
inferredData.getPropertyName(),
mappedBy,
buildingContext
);
associationTableBinder.setBuildingContext( buildingContext );
collectionBinder.setTableBinder( associationTableBinder );
collectionBinder.setJoinColumns( joinColumns );
collectionBinder.setInverseJoinColumns( inverseJoinColumns );
}
protected MetadataBuildingContext getBuildingContext() {
return buildingContext;
}
@ -2231,32 +2688,12 @@ public abstract class CollectionBinder {
final String mappedBy = columns[0].getMappedBy();
if ( isNotEmpty( mappedBy ) ) {
final Property property = referencedEntity.getRecursiveProperty( mappedBy );
List<Selectable> mappedByColumns;
if ( property.getValue() instanceof Collection ) {
mappedByColumns = ( (Collection) property.getValue() ).getKey().getSelectables();
}
else {
//find the appropriate reference key, can be in a join
KeyValue key = null;
for ( Join join : referencedEntity.getJoins() ) {
if ( join.containsProperty( property ) ) {
key = join.getKey();
break;
}
}
if ( key == null ) {
key = property.getPersistentClass().getIdentifier();
}
mappedByColumns = key.getSelectables();
}
final List<Selectable> mappedByColumns = mappedByColumns( referencedEntity, property );
for ( Selectable selectable: mappedByColumns ) {
Column column = (Column) selectable;
columns[0].linkValueUsingAColumnCopy( column, value );
columns[0].linkValueUsingAColumnCopy( (Column) selectable, value );
}
String referencedPropertyName =
buildingContext.getMetadataCollector().getPropertyReferencedAssociation(
referencedEntity.getEntityName(), mappedBy
);
final String referencedPropertyName = buildingContext.getMetadataCollector()
.getPropertyReferencedAssociation( referencedEntity.getEntityName(), mappedBy );
if ( referencedPropertyName != null ) {
//TODO always a many to one?
( (ManyToOne) value ).setReferencedPropertyName( referencedPropertyName );
@ -2277,6 +2714,26 @@ public abstract class CollectionBinder {
}
}
private static List<Selectable> mappedByColumns(PersistentClass referencedEntity, Property property) {
if ( property.getValue() instanceof Collection ) {
return ( (Collection) property.getValue() ).getKey().getSelectables();
}
else {
//find the appropriate reference key, can be in a join
KeyValue key = null;
for ( Join join : referencedEntity.getJoins() ) {
if ( join.containsProperty(property) ) {
key = join.getKey();
break;
}
}
if ( key == null ) {
key = property.getPersistentClass().getIdentifier();
}
return key.getSelectables();
}
}
public void setFkJoinColumns(AnnotatedJoinColumn[] annotatedJoinColumns) {
this.fkJoinColumns = annotatedJoinColumns;
}

View File

@ -17,6 +17,7 @@ import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.testing.TestForIssue;
@ -39,7 +40,7 @@ public class EntityInheritanceAttributeOverrideTest extends EntityManagerFactory
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
Logger.getMessageLogger( CoreMessageLogger.class, AnnotationBinder.class.getName() ) );
Logger.getMessageLogger( CoreMessageLogger.class, EntityBinder.class.getName() ) );
@Override
public Class<?>[] getAnnotatedClasses() {

View File

@ -16,6 +16,7 @@ import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AnnotationBinder;
import org.hibernate.cfg.annotations.EntityBinder;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.logger.LoggerInspectionRule;
@ -32,7 +33,7 @@ public class AnnotationBinderTest {
@Rule
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
Logger.getMessageLogger( CoreMessageLogger.class, AnnotationBinder.class.getName() ) );
Logger.getMessageLogger( CoreMessageLogger.class, EntityBinder.class.getName() ) );
@Test
public void testInvalidPrimaryKeyJoinColumnAnnotationMessageContainsClassName() throws Exception {
@ -47,7 +48,7 @@ public class AnnotationBinderTest {
assertTrue( "Expected warning HHH00137 but it wasn't triggered", triggerable.wasTriggered() );
assertTrue(
"Expected invalid class name in warning HHH00137 message but it does not apper to be present; got " + triggerable.triggerMessage(),
"Expected invalid class name in warning HHH00137 message but it does not appear to be present; got " + triggerable.triggerMessage(),
triggerable.triggerMessage()
.matches( ".*\\b\\Q" + InvalidPrimaryKeyJoinColumnAnnotationEntity.class.getName() + "\\E\\b.*" )
);