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

View File

@ -7,10 +7,8 @@
package org.hibernate.persister.entity.mutation;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import org.hibernate.AssertionFailure;
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.MutationExecutor;
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.NoBatchKeyAccess;
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.metamodel.mapping.AttributeMapping;
import org.hibernate.metamodel.mapping.AttributeMappingsList;
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
import org.hibernate.metamodel.mapping.EntityVersionMapping;
import org.hibernate.metamodel.mapping.SelectableMapping;
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.TableUpdateBuilderSkipped;
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.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(
SharedSessionContractImplementor session,
JdbcValueBindings jdbcValueBindings,
@ -1100,7 +1065,6 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
DirtinessChecker dirtinessChecker,
SharedSessionContractImplementor session) {
final EntityVersionMapping versionMapping = entityPersister().getVersionMapping();
final EntityRowIdMapping rowIdMapping = entityPersister().getRowIdMapping();
final AttributeMappingsList attributeMappings = entityPersister().getAttributeMappings();
final boolean[] versionability = entityPersister().getPropertyVersionability();
final OptimisticLockStyle optimisticLockStyle = entityPersister().optimisticLockStyle();
@ -1155,7 +1119,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
updateGroupBuilder.forEachTableMutationBuilder( (builder) -> {
final EntityTableMapping tableMapping = (EntityTableMapping) builder.getMutatingTable().getTableMapping();
final TableUpdateBuilder<?> tableUpdateBuilder = (TableUpdateBuilder<?>) builder;
applyKeyRestriction( rowId, rowIdMapping, tableUpdateBuilder, tableMapping );
applyKeyRestriction( rowId, entityPersister(), tableUpdateBuilder, tableMapping );
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(
Object[] oldValues,
SharedSessionContractImplementor session,
@ -1344,7 +1295,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|| entityPersister().optimisticLockStyle() == DIRTY ) {
tablesNeedingDynamicUpdate.add( tableMapping );
}
else if ( rowId == null && entityPersister().getRowIdMapping() != null && tableMapping.isIdentifierTable() ) {
else if ( rowId == null && needsRowId( entityPersister(), tableMapping ) ) {
tablesNeedingDynamicUpdate.add( tableMapping );
}
}