HHH-17167 Also use `@RowId` for deletes when available

This commit is contained in:
Marco Belladelli 2023-09-07 13:51:17 +02:00 committed by Christian Beikov
parent 3da12cae61
commit 0ecd5d8a45
4 changed files with 68 additions and 108 deletions

View File

@ -14,7 +14,7 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
/** /**
* Specifies that a {@code rowid}-like column or pseudo-column should be * Specifies that a {@code rowid}-like column or pseudo-column should be
* used as the row locator in SQL {@code update} statements for an entity, * used as the row locator in CRUD operations for an entity,
* instead of the primary key of the table. * instead of the primary key of the table.
* <p> * <p>
* If the {@linkplain org.hibernate.dialect.Dialect SQL dialect} does * If the {@linkplain org.hibernate.dialect.Dialect SQL dialect} does

View File

@ -28,6 +28,7 @@ import org.hibernate.sql.model.ast.MutationGroup;
import org.hibernate.sql.model.ast.TableMutation; import org.hibernate.sql.model.ast.TableMutation;
import org.hibernate.sql.model.ast.builder.ColumnValuesTableMutationBuilder; import org.hibernate.sql.model.ast.builder.ColumnValuesTableMutationBuilder;
import org.hibernate.sql.model.ast.builder.MutationGroupBuilder; import org.hibernate.sql.model.ast.builder.MutationGroupBuilder;
import org.hibernate.sql.model.ast.builder.RestrictedTableMutationBuilder;
import org.hibernate.sql.model.internal.MutationOperationGroupFactory; import org.hibernate.sql.model.internal.MutationOperationGroupFactory;
/** /**
@ -165,4 +166,51 @@ public abstract class AbstractMutationCoordinator {
} }
} }
} }
protected static boolean needsRowId(AbstractEntityPersister entityPersister, EntityTableMapping tableMapping) {
return entityPersister.getRowIdMapping() != null && tableMapping.isIdentifierTable();
}
protected static void applyKeyRestriction(
Object rowId,
AbstractEntityPersister entityPersister,
RestrictedTableMutationBuilder<?, ?> tableMutationBuilder,
EntityTableMapping tableMapping) {
if ( rowId != null && needsRowId( entityPersister, tableMapping ) ) {
tableMutationBuilder.addKeyRestrictionLeniently( entityPersister.getRowIdMapping() );
}
else {
tableMutationBuilder.addKeyRestrictions( tableMapping.getKeyMapping() );
}
}
protected void breakDownKeyJdbcValues(
Object id,
Object rowId,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
EntityTableMapping tableMapping) {
if ( rowId != null && needsRowId( entityPersister(), tableMapping ) ) {
jdbcValueBindings.bindValue(
rowId,
tableMapping.getTableName(),
entityPersister().getRowIdMapping().getRowIdName(),
ParameterUsage.RESTRICT
);
}
else {
tableMapping.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> {
jdbcValueBindings.bindValue(
jdbcValue,
tableMapping.getTableName(),
columnMapping.getColumnName(),
ParameterUsage.RESTRICT
);
},
session
);
}
}
} }

View File

@ -12,21 +12,18 @@ import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.MutationExecutor; import org.hibernate.engine.jdbc.mutation.MutationExecutor;
import org.hibernate.engine.jdbc.mutation.ParameterUsage; import org.hibernate.engine.jdbc.mutation.ParameterUsage;
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityEntry;
import org.hibernate.engine.spi.PersistenceContext; import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.persister.entity.AbstractEntityPersister; import org.hibernate.persister.entity.AbstractEntityPersister;
import org.hibernate.sql.model.MutationOperation; import org.hibernate.sql.model.MutationOperation;
import org.hibernate.sql.model.MutationOperationGroup; import org.hibernate.sql.model.MutationOperationGroup;
import org.hibernate.sql.model.MutationType; import org.hibernate.sql.model.MutationType;
import org.hibernate.sql.model.ast.ColumnValueBinding;
import org.hibernate.sql.model.ast.ColumnValueBindingList; import org.hibernate.sql.model.ast.ColumnValueBindingList;
import org.hibernate.sql.model.ast.builder.MutationGroupBuilder; import org.hibernate.sql.model.ast.builder.MutationGroupBuilder;
import org.hibernate.sql.model.ast.builder.RestrictedTableMutationBuilder; import org.hibernate.sql.model.ast.builder.RestrictedTableMutationBuilder;
@ -52,7 +49,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
public DeleteCoordinator(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) { public DeleteCoordinator(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) {
super( entityPersister, factory ); super( entityPersister, factory );
this.staticOperationGroup = generateOperationGroup( null, true, null ); this.staticOperationGroup = generateOperationGroup( "", null, true, null );
this.batchKey = new BasicBatchKey( entityPersister.getEntityName() + "#DELETE" ); this.batchKey = new BasicBatchKey( entityPersister.getEntityName() + "#DELETE" );
if ( !entityPersister.isVersioned() ) { if ( !entityPersister.isVersioned() ) {
@ -79,24 +76,23 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
final PersistenceContext persistenceContext = session.getPersistenceContextInternal(); final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
final EntityEntry entry = persistenceContext.getEntry( entity ); final EntityEntry entry = persistenceContext.getEntry( entity );
final Object[] loadedState = entry != null && isImpliedOptimisticLocking ? entry.getLoadedState() : null; final Object[] loadedState = entry != null ? entry.getLoadedState() : null;
final Object rowId = entry != null && entityPersister().hasRowId() ? entry.getRowId() : null; final Object rowId = entry != null ? entry.getRowId() : null;
if ( ( isImpliedOptimisticLocking && loadedState != null ) || rowId != null ) { if ( ( isImpliedOptimisticLocking && loadedState != null ) || ( rowId == null && entityPersister().hasRowId() ) ) {
doDynamicDelete( entity, id, rowId, loadedState, session ); doDynamicDelete( entity, id, loadedState, session );
} }
else { else {
doStaticDelete( entity, id, entry == null ? null : entry.getLoadedState(), version, session ); doStaticDelete( entity, id, rowId, loadedState, version, session );
} }
} }
protected void doDynamicDelete( protected void doDynamicDelete(
Object entity, Object entity,
Object id, Object id,
Object rowId,
Object[] loadedState, Object[] loadedState,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final MutationOperationGroup operationGroup = generateOperationGroup( loadedState, true, session ); final MutationOperationGroup operationGroup = generateOperationGroup( null, loadedState, true, session );
final MutationExecutor mutationExecutor = executor( session, operationGroup ); final MutationExecutor mutationExecutor = executor( session, operationGroup );
@ -110,7 +106,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
applyLocking( null, loadedState, mutationExecutor, session ); applyLocking( null, loadedState, mutationExecutor, session );
applyId( id, rowId, mutationExecutor, operationGroup, session ); applyId( id, null, mutationExecutor, operationGroup, session );
try { try {
mutationExecutor.execute( mutationExecutor.execute(
@ -217,14 +213,11 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
MutationExecutor mutationExecutor, MutationExecutor mutationExecutor,
MutationOperationGroup operationGroup, MutationOperationGroup operationGroup,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings(); final JdbcValueBindings jdbcValueBindings = mutationExecutor.getJdbcValueBindings();
final EntityRowIdMapping rowIdMapping = entityPersister().getRowIdMapping();
for ( int position = 0; position < operationGroup.getNumberOfOperations(); position++ ) { for ( int position = 0; position < operationGroup.getNumberOfOperations(); position++ ) {
final MutationOperation jdbcMutation = operationGroup.getOperation( position ); final MutationOperation jdbcMutation = operationGroup.getOperation( position );
final EntityTableMapping tableDetails = (EntityTableMapping) jdbcMutation.getTableDetails(); final EntityTableMapping tableDetails = (EntityTableMapping) jdbcMutation.getTableDetails();
breakDownIdJdbcValues( id, rowId, session, jdbcValueBindings, rowIdMapping, tableDetails ); breakDownKeyJdbcValues( id, rowId, session, jdbcValueBindings, tableDetails );
final PreparedStatementDetails statementDetails = mutationExecutor.getPreparedStatementDetails( tableDetails.getTableName() ); final PreparedStatementDetails statementDetails = mutationExecutor.getPreparedStatementDetails( tableDetails.getTableName() );
if ( statementDetails != null ) { if ( statementDetails != null ) {
// force creation of the PreparedStatement // force creation of the PreparedStatement
@ -234,40 +227,10 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
} }
} }
private static void breakDownIdJdbcValues(
Object id,
Object rowId,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
EntityRowIdMapping rowIdMapping,
EntityTableMapping tableDetails) {
if ( rowId != null && rowIdMapping != null && tableDetails.isIdentifierTable() ) {
jdbcValueBindings.bindValue(
rowId,
tableDetails.getTableName(),
rowIdMapping.getRowIdName(),
ParameterUsage.RESTRICT
);
}
else {
tableDetails.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> {
jdbcValueBindings.bindValue(
jdbcValue,
tableDetails.getTableName(),
columnMapping.getColumnName(),
ParameterUsage.RESTRICT
);
},
session
);
}
}
protected void doStaticDelete( protected void doStaticDelete(
Object entity, Object entity,
Object id, Object id,
Object rowId,
Object[] loadedState, Object[] loadedState,
Object version, Object version,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
@ -299,7 +262,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
bindPartitionColumnValueBindings( loadedState, session, jdbcValueBindings ); bindPartitionColumnValueBindings( loadedState, session, jdbcValueBindings );
applyId( id, null, mutationExecutor, staticOperationGroup, session ); applyId( id, rowId, mutationExecutor, staticOperationGroup, session );
mutationExecutor.execute( mutationExecutor.execute(
entity, entity,
@ -321,13 +284,14 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
protected MutationOperationGroup resolveNoVersionDeleteGroup(SharedSessionContractImplementor session) { protected MutationOperationGroup resolveNoVersionDeleteGroup(SharedSessionContractImplementor session) {
if ( noVersionDeleteGroup == null ) { if ( noVersionDeleteGroup == null ) {
noVersionDeleteGroup = generateOperationGroup( null, false, session ); noVersionDeleteGroup = generateOperationGroup( "", null, false, session );
} }
return noVersionDeleteGroup; return noVersionDeleteGroup;
} }
protected MutationOperationGroup generateOperationGroup( protected MutationOperationGroup generateOperationGroup(
Object rowId,
Object[] loadedState, Object[] loadedState,
boolean applyVersion, boolean applyVersion,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
@ -340,13 +304,14 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
deleteGroupBuilder.addTableDetailsBuilder( tableDeleteBuilder ); deleteGroupBuilder.addTableDetailsBuilder( tableDeleteBuilder );
} ); } );
applyTableDeleteDetails( deleteGroupBuilder, loadedState, applyVersion, session ); applyTableDeleteDetails( deleteGroupBuilder, rowId, loadedState, applyVersion, session );
return createOperationGroup( null, deleteGroupBuilder.buildMutationGroup() ); return createOperationGroup( null, deleteGroupBuilder.buildMutationGroup() );
} }
private void applyTableDeleteDetails( private void applyTableDeleteDetails(
MutationGroupBuilder deleteGroupBuilder, MutationGroupBuilder deleteGroupBuilder,
Object rowId,
Object[] loadedState, Object[] loadedState,
boolean applyVersion, boolean applyVersion,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
@ -354,7 +319,7 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
deleteGroupBuilder.forEachTableMutationBuilder( (builder) -> { deleteGroupBuilder.forEachTableMutationBuilder( (builder) -> {
final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping(); final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping();
final TableDeleteBuilder tableDeleteBuilder = (TableDeleteBuilder) builder; final TableDeleteBuilder tableDeleteBuilder = (TableDeleteBuilder) builder;
applyKeyDetails( tableDeleteBuilder, tableMapping ); applyKeyRestriction( rowId, entityPersister(), tableDeleteBuilder, tableMapping );
} ); } );
if ( applyVersion ) { if ( applyVersion ) {
@ -382,10 +347,6 @@ public class DeleteCoordinator extends AbstractMutationCoordinator {
// todo (6.2) : apply where + where-fragments // todo (6.2) : apply where + where-fragments
} }
private static void applyKeyDetails(TableDeleteBuilder tableDeleteBuilder, EntityTableMapping tableMapping) {
tableDeleteBuilder.addKeyRestrictions( tableMapping.getKeyMapping() );
}
protected void applyOptimisticLocking( protected void applyOptimisticLocking(
MutationGroupBuilder mutationGroupBuilder, MutationGroupBuilder mutationGroupBuilder,
Object[] loadedState, Object[] loadedState,

View File

@ -7,10 +7,8 @@
package org.hibernate.persister.entity.mutation; package org.hibernate.persister.entity.mutation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Set;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
@ -22,7 +20,6 @@ import org.hibernate.engine.jdbc.batch.spi.BatchKey;
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings; import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
import org.hibernate.engine.jdbc.mutation.MutationExecutor; import org.hibernate.engine.jdbc.mutation.MutationExecutor;
import org.hibernate.engine.jdbc.mutation.ParameterUsage; import org.hibernate.engine.jdbc.mutation.ParameterUsage;
import org.hibernate.sql.model.internal.MutationOperationGroupFactory;
import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions; import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions;
import org.hibernate.engine.jdbc.mutation.internal.NoBatchKeyAccess; import org.hibernate.engine.jdbc.mutation.internal.NoBatchKeyAccess;
import org.hibernate.engine.jdbc.mutation.spi.BatchKeyAccess; import org.hibernate.engine.jdbc.mutation.spi.BatchKeyAccess;
@ -37,7 +34,6 @@ import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.metamodel.mapping.AttributeMapping; import org.hibernate.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMappingsList; import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.metamodel.mapping.EntityVersionMapping; import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.SelectableMapping; import org.hibernate.metamodel.mapping.SelectableMapping;
import org.hibernate.metamodel.mapping.SingularAttributeMapping; import org.hibernate.metamodel.mapping.SingularAttributeMapping;
@ -53,6 +49,7 @@ import org.hibernate.sql.model.ast.builder.RestrictedTableMutationBuilder;
import org.hibernate.sql.model.ast.builder.TableUpdateBuilder; import org.hibernate.sql.model.ast.builder.TableUpdateBuilder;
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderSkipped; import org.hibernate.sql.model.ast.builder.TableUpdateBuilderSkipped;
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard; import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard;
import org.hibernate.sql.model.internal.MutationOperationGroupFactory;
import org.hibernate.sql.model.jdbc.JdbcMutationOperation; import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
import org.hibernate.tuple.entity.EntityMetamodel; import org.hibernate.tuple.entity.EntityMetamodel;
@ -874,38 +871,6 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
} ); } );
} }
private void breakDownKeyJdbcValues(
Object id,
Object rowId,
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
EntityTableMapping tableMapping) {
// if the mutation is against the identifier table and we need to use row-id...
if ( tableMapping.isIdentifierTable() && entityPersister().hasRowId() && rowId != null ) {
// todo (mutation) : make sure the SQL uses row-id in this case
jdbcValueBindings.bindValue(
rowId,
tableMapping.getTableName(),
entityPersister().getRowIdMapping().getRowIdName(),
ParameterUsage.RESTRICT
);
}
else {
tableMapping.getKeyMapping().breakDownKeyJdbcValues(
id,
(jdbcValue, columnMapping) -> {
jdbcValueBindings.bindValue(
jdbcValue,
tableMapping.getTableName(),
columnMapping.getColumnName(),
ParameterUsage.RESTRICT
);
},
session
);
}
}
private static void decomposeAttributeMapping( private static void decomposeAttributeMapping(
SharedSessionContractImplementor session, SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings, JdbcValueBindings jdbcValueBindings,
@ -1100,7 +1065,6 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
DirtinessChecker dirtinessChecker, DirtinessChecker dirtinessChecker,
SharedSessionContractImplementor session) { SharedSessionContractImplementor session) {
final EntityVersionMapping versionMapping = entityPersister().getVersionMapping(); final EntityVersionMapping versionMapping = entityPersister().getVersionMapping();
final EntityRowIdMapping rowIdMapping = entityPersister().getRowIdMapping();
final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings(); final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings();
final boolean[] versionability = entityPersister().getPropertyVersionability(); final boolean[] versionability = entityPersister().getPropertyVersionability();
final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle(); final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle();
@ -1155,7 +1119,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
updateGroupBuilder.forEachTableMutationBuilder( (builder) -> { updateGroupBuilder.forEachTableMutationBuilder( (builder) -> {
final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping(); final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping();
final TableUpdateBuilder<?> tableUpdateBuilder = (TableUpdateBuilder<?>) builder; final TableUpdateBuilder<?> tableUpdateBuilder = (TableUpdateBuilder<?>) builder;
applyKeyRestriction( rowId, rowIdMapping, tableUpdateBuilder, tableMapping ); applyKeyRestriction( rowId, entityPersister(), tableUpdateBuilder, tableMapping );
applyPartitionKeyRestriction( tableUpdateBuilder ); applyPartitionKeyRestriction( tableUpdateBuilder );
} ); } );
} }
@ -1177,19 +1141,6 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
} }
} }
private static void applyKeyRestriction(
Object rowId,
EntityRowIdMapping rowIdMapping,
TableUpdateBuilder<?> tableUpdateBuilder,
EntityTableMapping tableMapping) {
if ( rowIdMapping != null && rowId != null && tableMapping.isIdentifierTable() ) {
tableUpdateBuilder.addKeyRestrictionLeniently( rowIdMapping );
}
else {
tableUpdateBuilder.addKeyRestrictions( tableMapping.getKeyMapping() );
}
}
private static void applyAttributeLockingDetails( private static void applyAttributeLockingDetails(
Object[] oldValues, Object[] oldValues,
SharedSessionContractImplementor session, SharedSessionContractImplementor session,
@ -1344,7 +1295,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|| entityPersister().optimisticLockStyle() == DIRTY ) { || entityPersister().optimisticLockStyle() == DIRTY ) {
tablesNeedingDynamicUpdate.add( tableMapping ); tablesNeedingDynamicUpdate.add( tableMapping );
} }
else if ( rowId == null && entityPersister().getRowIdMapping() != null && tableMapping.isIdentifierTable() ) { else if ( rowId == null && needsRowId( entityPersister(), tableMapping ) ) {
tablesNeedingDynamicUpdate.add( tableMapping ); tablesNeedingDynamicUpdate.add( tableMapping );
} }
} }