HHH-16918 Unify column duplication checking logic under Value

This commit is contained in:
Marco Belladelli 2023-08-07 16:11:57 +02:00
parent bafc2ae88e
commit d3b6eaea53
5 changed files with 64 additions and 121 deletions

View File

@ -392,46 +392,18 @@ public abstract class Collection implements Fetchable, Value, Filterable {
checkColumnDuplication(); checkColumnDuplication();
} }
private void checkColumnDuplication(java.util.Set<String> distinctColumns, Value value)
throws MappingException {
final boolean[] insertability = value.getColumnInsertability();
final boolean[] updatability = value.getColumnUpdateability();
int i = 0;
for ( Selectable selectable : value.getSelectables() ) {
// exclude formulas and columns that are not insertable or updatable
// since these values can be repeated (HHH-5393)
if ( !selectable.isFormula() && ( insertability[i] || updatability[i] ) ) {
Column col = (Column) selectable;
if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException(
"Repeated column in mapping for collection: "
+ getRole()
+ " column: "
+ col.getName()
);
}
}
i++;
}
}
private void checkColumnDuplication() throws MappingException { private void checkColumnDuplication() throws MappingException {
final String owner = "collection '" + getReferencedPropertyName() + "'";
final HashSet<String> cols = new HashSet<>(); final HashSet<String> cols = new HashSet<>();
checkColumnDuplication( cols, getKey() ); getKey().checkColumnDuplication( cols, owner );
if ( isIndexed() ) { if ( isIndexed() ) {
checkColumnDuplication( ( (IndexedCollection) this ).getIndex().checkColumnDuplication( cols, owner );
cols,
( (IndexedCollection) this ).getIndex()
);
} }
if ( isIdentified() ) { if ( isIdentified() ) {
checkColumnDuplication( ( (IdentifierCollection) this ).getIdentifier().checkColumnDuplication( cols, owner );
cols,
( (IdentifierCollection) this ).getIdentifier()
);
} }
if ( !isOneToMany() ) { if ( !isOneToMany() ) {
checkColumnDuplication( cols, getElement() ); getElement().checkColumnDuplication( cols, owner );
} }
} }

View File

@ -53,6 +53,7 @@ import org.hibernate.usertype.CompositeUserType;
import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toList;
import static org.hibernate.generator.EventType.INSERT; import static org.hibernate.generator.EventType.INSERT;
import static org.hibernate.mapping.MappingHelper.checkPropertyColumnDuplication;
/** /**
* A mapping model object that represents an {@linkplain jakarta.persistence.Embeddable embeddable class}. * A mapping model object that represents an {@linkplain jakarta.persistence.Embeddable embeddable class}.
@ -283,47 +284,14 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
this.structName = structName; this.structName = structName;
} }
protected void checkColumnDuplication() throws MappingException { @Override
checkPropertyColumnDuplication( new HashSet<>(), getProperties() ); public void checkColumnDuplication(Set<String> distinctColumns, String owner) {
} if ( aggregateColumn == null ) {
protected void checkColumnDuplication(java.util.Set<String> distinctColumns, Value value) checkPropertyColumnDuplication( distinctColumns, getProperties(), owner );
throws MappingException {
if ( value != null ) {
for ( Selectable columnOrFormula : value.getSelectables() ) {
if ( !columnOrFormula.isFormula() ) {
final Column col = (Column) columnOrFormula;
if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException(
"Column '" + col.getName()
+ "' is duplicated in mapping for component '" + getRoleName()
+ "' (use '@Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)"
);
}
}
}
} }
} else {
checkPropertyColumnDuplication( new HashSet<>(), getProperties(), "component '" + getRoleName() + "'" );
protected void checkPropertyColumnDuplication(Set<String> distinctColumns, List<Property> properties) aggregateColumn.getValue().checkColumnDuplication( distinctColumns, owner );
throws MappingException {
for ( Property prop : properties ) {
Value value = prop.getValue();
if ( value instanceof Component ) {
Component component = (Component) value;
AggregateColumn aggregateColumn = component.getAggregateColumn();
if ( aggregateColumn == null ) {
checkPropertyColumnDuplication( distinctColumns, component.getProperties() );
}
else {
component.checkColumnDuplication();
checkColumnDuplication( distinctColumns, aggregateColumn.getValue() );
}
}
else {
if ( prop.isUpdateable() || prop.isInsertable() ) {
checkColumnDuplication( distinctColumns, value);
}
}
} }
} }

View File

@ -6,18 +6,20 @@
*/ */
package org.hibernate.mapping; package org.hibernate.mapping;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.hibernate.Internal; import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.boot.BootLogging; import org.hibernate.boot.BootLogging;
import org.hibernate.boot.model.internal.DelayedParameterizedTypeBean; import org.hibernate.boot.model.internal.DelayedParameterizedTypeBean;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService; import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.MappingModelCreationLogging;
import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer; import org.hibernate.resource.beans.internal.FallbackBeanInstanceProducer;
import org.hibernate.resource.beans.spi.ManagedBean; import org.hibernate.resource.beans.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry; import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
@ -237,4 +239,15 @@ public final class MappingHelper {
return new ProvidedInstanceManagedBeanImpl<>( userCollectionType ); return new ProvidedInstanceManagedBeanImpl<>( userCollectionType );
} }
public static void checkPropertyColumnDuplication(
Set<String> distinctColumns,
List<Property> properties,
String owner) throws MappingException {
for ( Property prop : properties ) {
if ( prop.isUpdateable() || prop.isInsertable() ) {
prop.getValue().checkColumnDuplication( distinctColumns, owner );
}
}
}
} }

View File

@ -47,6 +47,7 @@ import static java.util.Collections.unmodifiableList;
import static java.util.Comparator.comparing; import static java.util.Comparator.comparing;
import static org.hibernate.internal.util.StringHelper.qualify; import static org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.internal.util.StringHelper.root; import static org.hibernate.internal.util.StringHelper.root;
import static org.hibernate.mapping.MappingHelper.checkPropertyColumnDuplication;
import static org.hibernate.sql.Template.collectColumnNames; import static org.hibernate.sql.Template.collectColumnNames;
/** /**
@ -1042,47 +1043,6 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut
this.isAbstract = isAbstract; this.isAbstract = isAbstract;
} }
protected void checkColumnDuplication(Set<String> distinctColumns, Value value)
throws MappingException {
if ( value != null ) {
for ( Selectable columnOrFormula : value.getSelectables() ) {
if ( !columnOrFormula.isFormula() ) {
final Column col = (Column) columnOrFormula;
if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException(
"Column '" + col.getName()
+ "' is duplicated in mapping for entity '" + getEntityName()
+ "' (use '@Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)"
);
}
}
}
}
}
protected void checkPropertyColumnDuplication(Set<String> distinctColumns, List<Property> properties)
throws MappingException {
for ( Property prop : properties ) {
final Value value = prop.getValue();
if ( value instanceof Component ) {
final Component component = (Component) value;
final AggregateColumn aggregateColumn = component.getAggregateColumn();
if ( aggregateColumn == null ) {
checkPropertyColumnDuplication( distinctColumns, component.getProperties() );
}
else {
component.checkColumnDuplication();
checkColumnDuplication( distinctColumns, aggregateColumn.getValue() );
}
}
else {
if ( prop.isUpdateable() || prop.isInsertable() ) {
checkColumnDuplication( distinctColumns, value);
}
}
}
}
@Deprecated(since = "6.0") @Deprecated(since = "6.0")
protected Iterator<Property> getNonDuplicatedPropertyIterator() { protected Iterator<Property> getNonDuplicatedPropertyIterator() {
return getUnjoinedPropertyIterator(); return getUnjoinedPropertyIterator();
@ -1098,20 +1058,21 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut
} }
protected void checkColumnDuplication() { protected void checkColumnDuplication() {
final String owner = "entity '" + getEntityName() + "'";
final HashSet<String> cols = new HashSet<>(); final HashSet<String> cols = new HashSet<>();
if ( getIdentifierMapper() == null ) { if ( getIdentifierMapper() == null ) {
//an identifier mapper => getKey will be included in the getNonDuplicatedPropertyIterator() //an identifier mapper => getKey will be included in the getNonDuplicatedPropertyIterator()
//and checked later, so it needs to be excluded //and checked later, so it needs to be excluded
checkColumnDuplication( cols, getKey() ); getKey().checkColumnDuplication( cols, owner );
} }
if ( isDiscriminatorInsertable() ) { if ( isDiscriminatorInsertable() && getDiscriminator() != null ) {
checkColumnDuplication( cols, getDiscriminator() ); getDiscriminator().checkColumnDuplication( cols, owner );
} }
checkPropertyColumnDuplication( cols, getNonDuplicatedProperties() ); checkPropertyColumnDuplication( cols, getNonDuplicatedProperties(), owner );
for ( Join join : getJoins() ) { for ( Join join : getJoins() ) {
cols.clear(); cols.clear();
checkColumnDuplication( cols, join.getKey() ); join.getKey().checkColumnDuplication( cols, owner );
checkPropertyColumnDuplication( cols, join.getProperties() ); checkPropertyColumnDuplication( cols, join.getProperties(), owner );
} }
} }

View File

@ -9,9 +9,11 @@ package org.hibernate.mapping;
import java.io.Serializable; import java.io.Serializable;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Set;
import org.hibernate.FetchMode; import org.hibernate.FetchMode;
import org.hibernate.Incubating; import org.hibernate.Incubating;
import org.hibernate.Internal;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext; import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.Mapping;
@ -179,4 +181,31 @@ public interface Value extends Serializable {
default String getExtraCreateTableInfo() { default String getExtraCreateTableInfo() {
return ""; return "";
} }
/**
* Checks if this value contains any duplicate column. A column
* is considered duplicate when its {@link Column#getName() name} is
* already contained in the {@code distinctColumn} set.
* <p>
* If a duplicate column is found, a {@link MappingException} is thrown.
*
* @param distinctColumns set containing the names of the columns to check
* @param owner the owner of this value, used just for error reporting
*/
@Internal
default void checkColumnDuplication(Set<String> distinctColumns, String owner) {
for ( int i = 0; i < getSelectables().size(); i++ ) {
final Selectable selectable = getSelectables().get( i );
if ( isColumnInsertable( i ) || isColumnUpdateable( i ) ) {
final Column col = (Column) selectable;
if ( !distinctColumns.add( col.getName() ) ) {
throw new MappingException(
"Column '" + col.getName()
+ "' is duplicated in mapping for " + owner
+ " (use '@Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)"
);
}
}
}
}
} }