[HHH-17065] Unique Index on PrimaryKey orders primary key columns.

This commit is contained in:
The-Huginn 2023-08-28 14:24:29 +02:00 committed by Christian Beikov
parent 3a84e408d6
commit b431029bff
6 changed files with 55 additions and 9 deletions

View File

@ -211,6 +211,8 @@ The second INSERT statement fails because of the unique constraint violation.
The {jpaJavadocUrlPrefix}Index.html[`@Index`] annotation is used by the automated schema generation tool to create a database index.
TIP: Creating unique index containing all primary key columns will result in ordering primary key columns specified by `columnList`
Considering the following entity mapping. Hibernate generates the index when creating the database schema:
[[schema-generation-columns-index-mapping-example]]

View File

@ -8,6 +8,7 @@ package org.hibernate.boot.model.relational;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
@ -17,7 +18,9 @@ import org.hibernate.dialect.temptable.TemporaryTableColumn;
import org.hibernate.engine.jdbc.Size;
import org.hibernate.mapping.Column;
import org.hibernate.mapping.Constraint;
import org.hibernate.mapping.PrimaryKey;
import org.hibernate.mapping.Table;
import org.hibernate.mapping.UniqueKey;
import org.hibernate.mapping.UserDefinedType;
import static java.lang.Math.log;
@ -45,6 +48,15 @@ public class ColumnOrderingStrategyStandard implements ColumnOrderingStrategy {
@Override
public List<Column> orderConstraintColumns(Constraint constraint, Metadata metadata) {
// We try to find uniqueKey constraint containing only primary key.
// This uniqueKey then orders primaryKey columns. Otherwise, order as usual.
if ( constraint instanceof PrimaryKey ) {
UniqueKey uniqueKey = ((PrimaryKey) constraint).getOrderingUniqueKey();
if ( uniqueKey != null ) {
return uniqueKey.getColumns();
}
}
return orderColumns( constraint.getColumns(), metadata );
}

View File

@ -424,9 +424,9 @@ public abstract class PersistentClass implements IdentifiableTypeClass, Attribut
final Table table = getTable();
final PrimaryKey pk = new PrimaryKey( table );
pk.setName( PK_ALIAS.toAliasString( table.getName() ) );
table.setPrimaryKey( pk );
pk.addColumns( getKey() );
table.setPrimaryKey( pk );
}
public abstract String getWhere();

View File

@ -25,6 +25,7 @@ import org.jboss.logging.Logger;
public class PrimaryKey extends Constraint {
private static final Logger log = Logger.getLogger( PrimaryKey.class );
private UniqueKey orderingUniqueKey = null;
private int[] originalOrder;
public PrimaryKey(Table table){
@ -117,6 +118,14 @@ public class PrimaryKey extends Constraint {
return Arrays.asList( columnsInOriginalOrder );
}
public void setOrderingUniqueKey(UniqueKey uniqueKey) {
this.orderingUniqueKey = uniqueKey;
}
public UniqueKey getOrderingUniqueKey() {
return this.orderingUniqueKey;
}
@Internal
public void reorderColumns(List<Column> reorderedColumns) {
if ( originalOrder != null ) {
@ -126,12 +135,13 @@ public class PrimaryKey extends Constraint {
assert getColumns().size() == reorderedColumns.size() && getColumns().containsAll( reorderedColumns );
final List<Column> columns = getColumns();
originalOrder = new int[columns.size()];
for ( int i = 0; i < reorderedColumns.size(); i++ ) {
final Column reorderedColumn = reorderedColumns.get( i );
List<Column> newColumns = getOrderingUniqueKey() != null ? getOrderingUniqueKey().getColumns() : reorderedColumns;
for ( int i = 0; i < newColumns.size(); i++ ) {
final Column reorderedColumn = newColumns.get( i );
originalOrder[i] = columns.indexOf( reorderedColumn );
}
columns.clear();
columns.addAll( reorderedColumns );
columns.addAll( newColumns );
}
@Internal

View File

@ -357,10 +357,10 @@ public class Table implements Serializable, ContributableDatabaseObject {
// any sharing the same columns as other defined unique keys; this is needed for the annotation
// processor since it creates unique constraints automagically for the user
// 2) Remove any unique keys that share the same columns as the primary key; again, this is
// needed for the annotation processor to handle @Id @OneToOne cases. In such cases the
// unique key is unnecessary because a primary key is already unique by definition. We handle
// needed for the annotation processor to handle @Id @OneToOne cases. In such cases we handle
// this case specifically because some databases fail if you try to apply a unique key to
// the primary key columns which causes schema export to fail in these cases.
// the primary key columns which causes schema export to fail in these cases. Furthermore, we
// pass the unique key to a primary key for reordering columns specified by the unique key.
if ( !uniqueKeys.isEmpty() ) {
if ( uniqueKeys.size() == 1 ) {
// we have to worry about condition 2 above, but not condition 1
@ -395,6 +395,7 @@ public class Table implements Serializable, ContributableDatabaseObject {
// condition 2 : check against pk
if ( !removeIt && isSameAsPrimaryKeyColumns( uniqueKeyEntry.getValue() ) ) {
primaryKey.setOrderingUniqueKey(uniqueKeyEntry.getValue());
removeIt = true;
}
@ -413,7 +414,7 @@ public class Table implements Serializable, ContributableDatabaseObject {
return false;
}
return primaryKey.getColumns().containsAll( uniqueKey.getColumns() )
&& uniqueKey.getColumns().containsAll( primaryKey.getColumns() );
&& primaryKey.getColumns().size() == uniqueKey.getColumns().size();
}
@Override
@ -471,6 +472,7 @@ public class Table implements Serializable, ContributableDatabaseObject {
public void setPrimaryKey(PrimaryKey primaryKey) {
this.primaryKey = primaryKey;
checkPrimaryKeyUniqueKey();
}
public Index getOrCreateIndex(String indexName) {
@ -580,6 +582,21 @@ public class Table implements Serializable, ContributableDatabaseObject {
return foreignKey;
}
/**
* Checks for uniqueKey containing only whole primary key and sets
* order of the columns accordingly
*/
private void checkPrimaryKeyUniqueKey() {
final Iterator<Map.Entry<String,UniqueKey>> uniqueKeyEntries = uniqueKeys.entrySet().iterator();
while ( uniqueKeyEntries.hasNext() ) {
final Map.Entry<String,UniqueKey> uniqueKeyEntry = uniqueKeyEntries.next();
if ( isSameAsPrimaryKeyColumns( uniqueKeyEntry.getValue() ) ) {
primaryKey.setOrderingUniqueKey(uniqueKeyEntry.getValue());
uniqueKeyEntries.remove();
}
}
}
// This must be done outside of Table, rather than statically, to ensure
// deterministic alias names. See HHH-2448.

View File

@ -14,6 +14,8 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import jakarta.persistence.Index;
import jakarta.persistence.Table;
import org.hibernate.boot.Metadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.dialect.H2Dialect;
@ -71,6 +73,9 @@ class CompositePrimaryKeyColumnOrderTest {
}
@Entity
@Table(
indexes = @Index(unique = true, columnList = "b, a")
)
@IdClass( CompositePrimaryKey.class )
static class TestEntity {