Skip to content

Commit

Permalink
[HHH-17065] Unique Index on PrimaryKey orders primary key columns.
Browse files Browse the repository at this point in the history
  • Loading branch information
The-Huginn authored and beikov committed Oct 11, 2023
1 parent 3a84e40 commit b431029
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

Expand All @@ -17,7 +18,9 @@
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;
Expand Down Expand Up @@ -45,6 +48,15 @@ public List<Column> orderUserDefinedTypeColumns(UserDefinedType userDefinedType,

@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 );
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,9 +424,9 @@ public void createPrimaryKey() {
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();
Expand Down
16 changes: 13 additions & 3 deletions hibernate-core/src/main/java/org/hibernate/mapping/PrimaryKey.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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){
Expand Down Expand Up @@ -117,6 +118,14 @@ public List<Column> getColumnsInOriginalOrder() {
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 ) {
Expand All @@ -126,12 +135,13 @@ public void reorderColumns(List<Column> reorderedColumns) {
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
Expand Down
25 changes: 21 additions & 4 deletions hibernate-core/src/main/java/org/hibernate/mapping/Table.java
Original file line number Diff line number Diff line change
Expand Up @@ -357,10 +357,10 @@ private void cleanseUniqueKeyMap() {
// 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
Expand Down Expand Up @@ -395,6 +395,7 @@ private void cleanseUniqueKeyMap() {

// condition 2 : check against pk
if ( !removeIt && isSameAsPrimaryKeyColumns( uniqueKeyEntry.getValue() ) ) {
primaryKey.setOrderingUniqueKey(uniqueKeyEntry.getValue());
removeIt = true;
}

Expand All @@ -413,7 +414,7 @@ private boolean isSameAsPrimaryKeyColumns(UniqueKey uniqueKey) {
return false;
}
return primaryKey.getColumns().containsAll( uniqueKey.getColumns() )
&& uniqueKey.getColumns().containsAll( primaryKey.getColumns() );
&& primaryKey.getColumns().size() == uniqueKey.getColumns().size();
}

@Override
Expand Down Expand Up @@ -471,6 +472,7 @@ public PrimaryKey getPrimaryKey() {

public void setPrimaryKey(PrimaryKey primaryKey) {
this.primaryKey = primaryKey;
checkPrimaryKeyUniqueKey();
}

public Index getOrCreateIndex(String indexName) {
Expand Down Expand Up @@ -580,6 +582,21 @@ public ForeignKey createForeignKey(
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
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;
Expand Down Expand Up @@ -71,6 +73,9 @@ public List<String> getManagedClassNames() {
}

@Entity
@Table(
indexes = @Index(unique = true, columnList = "b, a")
)
@IdClass( CompositePrimaryKey.class )
static class TestEntity {

Expand Down

0 comments on commit b431029

Please sign in to comment.