HHH-16394 Statement Batch + Version + Dirty Collection leads to OptimisticLockException: Batch update returned unexpected row count from update

This commit is contained in:
Andrea Boriero 2023-04-12 10:10:01 +02:00 committed by Andrea Boriero
parent b2f2547d3c
commit 0b54c1d083
2 changed files with 30 additions and 4 deletions

View File

@ -22,7 +22,6 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
import org.hibernate.engine.jdbc.spi.JdbcServices; import org.hibernate.engine.jdbc.spi.JdbcServices;
import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; import org.hibernate.engine.jdbc.spi.SqlExceptionHelper;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.resource.jdbc.spi.JdbcObserver; import org.hibernate.resource.jdbc.spi.JdbcObserver;
import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER; import static org.hibernate.engine.jdbc.JdbcLogging.JDBC_MESSAGE_LOGGER;
@ -108,8 +107,6 @@ public class BatchImpl implements Batch {
); );
} }
final SharedSessionContractImplementor session = (SharedSessionContractImplementor) jdbcCoordinator.getJdbcSessionOwner();
try { try {
getStatementGroup().forEachStatement( (tableName, statementDetails) -> { getStatementGroup().forEachStatement( (tableName, statementDetails) -> {
if ( inclusionChecker != null && !inclusionChecker.include( statementDetails.getMutatingTableDetails() ) ) { if ( inclusionChecker != null && !inclusionChecker.include( statementDetails.getMutatingTableDetails() ) ) {

View File

@ -23,6 +23,8 @@ 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.internal.MutationQueryOptions; import org.hibernate.engine.jdbc.mutation.internal.MutationQueryOptions;
import org.hibernate.engine.jdbc.mutation.internal.NoBatchKeyAccess;
import org.hibernate.engine.jdbc.mutation.spi.BatchKeyAccess;
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService; 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.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -81,6 +83,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
private final BatchKey batchKey; private final BatchKey batchKey;
private final MutationOperationGroup versionUpdateGroup; private final MutationOperationGroup versionUpdateGroup;
private final BatchKey versionUpdateBatchkey;
public UpdateCoordinatorStandard(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) { public UpdateCoordinatorStandard(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) {
super( entityPersister, factory ); super( entityPersister, factory );
@ -92,12 +95,17 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
if ( entityPersister.hasUpdateGeneratedProperties() ) { if ( entityPersister.hasUpdateGeneratedProperties() ) {
// disable batching in case of update generated properties // disable batching in case of update generated properties
this.batchKey = null; this.batchKey = null;
this.versionUpdateBatchkey = null;
} }
else { else {
this.batchKey = new BasicBatchKey( this.batchKey = new BasicBatchKey(
entityPersister.getEntityName() + "#UPDATE", entityPersister.getEntityName() + "#UPDATE",
null null
); );
this.versionUpdateBatchkey = new BasicBatchKey(
entityPersister.getEntityName() + "#UPDATE_VERSION",
null
);
} }
} }
@ -447,7 +455,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
final EntityTableMapping mutatingTableDetails = (EntityTableMapping) versionUpdateGroup.getSingleOperation().getTableDetails(); final EntityTableMapping mutatingTableDetails = (EntityTableMapping) versionUpdateGroup.getSingleOperation().getTableDetails();
final MutationExecutor mutationExecutor = executor( session, versionUpdateGroup, false ); final MutationExecutor mutationExecutor = updateVersionExecutor( session, versionUpdateGroup, false );
final EntityVersionMapping versionMapping = entityPersister().getVersionMapping(); final EntityVersionMapping versionMapping = entityPersister().getVersionMapping();
@ -973,6 +981,27 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
.createExecutor( resolveBatchKeyAccess( dynamicUpdate, session ), group, session ); .createExecutor( resolveBatchKeyAccess( dynamicUpdate, session ), group, session );
} }
private MutationExecutor updateVersionExecutor(SharedSessionContractImplementor session, MutationOperationGroup group, boolean dynamicUpdate) {
return session.getSessionFactory()
.getServiceRegistry()
.getService( MutationExecutorService.class )
.createExecutor( resolveUpdateVersionBatchKeyAccess( dynamicUpdate, session ), group, session );
}
protected BatchKeyAccess resolveUpdateVersionBatchKeyAccess(boolean dynamicUpdate, SharedSessionContractImplementor session) {
if ( !dynamicUpdate
&& session.getTransactionCoordinator() != null
&& session.getTransactionCoordinator().isTransactionActive() ) {
return this::getVersionUpdateBatchkey;
}
return NoBatchKeyAccess.INSTANCE;
}
private BatchKey getVersionUpdateBatchkey(){
return versionUpdateBatchkey;
}
protected MutationOperationGroup generateDynamicUpdateGroup( protected MutationOperationGroup generateDynamicUpdateGroup(
Object id, Object id,
Object rowId, Object rowId,