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();
}
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 {
final String owner = "collection '" + getReferencedPropertyName() + "'";
final HashSet<String> cols = new HashSet<>();
checkColumnDuplication( cols, getKey() );
getKey().checkColumnDuplication( cols, owner );
if ( isIndexed() ) {
checkColumnDuplication(
cols,
( (IndexedCollection) this ).getIndex()
);
( (IndexedCollection) this ).getIndex().checkColumnDuplication( cols, owner );
}
if ( isIdentified() ) {
checkColumnDuplication(
cols,
( (IdentifierCollection) this ).getIdentifier()
);
( (IdentifierCollection) this ).getIdentifier().checkColumnDuplication( cols, owner );
}
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 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}.
@ -283,47 +284,14 @@ public class Component extends SimpleValue implements MetaAttributable, Sortable
this.structName = structName;
}
protected void checkColumnDuplication() throws MappingException {
checkPropertyColumnDuplication( new HashSet<>(), getProperties() );
}
protected void checkColumnDuplication(java.util.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 component '" + getRoleName()
+ "' (use '@Column(insertable=false, updatable=false)' when mapping multiple properties to the same column)"
);
}
}
}
@Override
public void checkColumnDuplication(Set<String> distinctColumns, String owner) {
if ( aggregateColumn == null ) {
checkPropertyColumnDuplication( distinctColumns, getProperties(), owner );
}
}
protected void checkPropertyColumnDuplication(Set<String> distinctColumns, List<Property> properties)
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);
}
}
else {
checkPropertyColumnDuplication( new HashSet<>(), getProperties(), "component '" + getRoleName() + "'" );
aggregateColumn.getValue().checkColumnDuplication( distinctColumns, owner );
}
}

View File

@ -6,18 +6,20 @@
*/
package org.hibernate.mapping;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.function.Supplier;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.boot.BootLogging;
import org.hibernate.boot.model.internal.DelayedParameterizedTypeBean;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.boot.spi.MetadataImplementor;
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.spi.ManagedBean;
import org.hibernate.resource.beans.spi.ManagedBeanRegistry;
@ -237,4 +239,15 @@ public final class MappingHelper {
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 org.hibernate.internal.util.StringHelper.qualify;
import static org.hibernate.internal.util.StringHelper.root;
import static org.hibernate.mapping.MappingHelper.checkPropertyColumnDuplication;
import static org.hibernate.sql.Template.collectColumnNames;
/**
@ -1042,47 +1043,6 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut
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")
protected Iterator<Property> getNonDuplicatedPropertyIterator() {
return getUnjoinedPropertyIterator();
@ -1098,20 +1058,21 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut
}
protected void checkColumnDuplication() {
final String owner = "entity '" + getEntityName() + "'";
final HashSet<String> cols = new HashSet<>();
if ( getIdentifierMapper() == null ) {
//an identifier mapper => getKey will be included in the getNonDuplicatedPropertyIterator()
//and checked later, so it needs to be excluded
checkColumnDuplication( cols, getKey() );
getKey().checkColumnDuplication( cols, owner );
}
if ( isDiscriminatorInsertable() ) {
checkColumnDuplication( cols, getDiscriminator() );
if ( isDiscriminatorInsertable() && getDiscriminator() != null ) {
getDiscriminator().checkColumnDuplication( cols, owner );
}
checkPropertyColumnDuplication( cols, getNonDuplicatedProperties() );
checkPropertyColumnDuplication( cols, getNonDuplicatedProperties(), owner );
for ( Join join : getJoins() ) {
cols.clear();
checkColumnDuplication( cols, join.getKey() );
checkPropertyColumnDuplication( cols, join.getProperties() );
join.getKey().checkColumnDuplication( cols, owner );
checkPropertyColumnDuplication( cols, join.getProperties(), owner );
}
}

View File

@ -9,9 +9,11 @@ package org.hibernate.mapping;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.hibernate.FetchMode;
import org.hibernate.Incubating;
import org.hibernate.Internal;
import org.hibernate.MappingException;
import org.hibernate.boot.spi.MetadataBuildingContext;
import org.hibernate.engine.spi.Mapping;
@ -179,4 +181,31 @@ public interface Value extends Serializable {
default String getExtraCreateTableInfo() {
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)"
);
}
}
}
}
}