HHH-15393 - Improve write-paths to use mapping model

This commit is contained in:
Steve Ebersole 2022-11-29 12:40:30 -06:00
parent 7461100c39
commit 94e2b599e4
9 changed files with 46 additions and 106 deletions

View File

@ -8,14 +8,9 @@ package org.hibernate.engine.jdbc.mutation.internal;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import java.util.function.Supplier;
import org.hibernate.engine.jdbc.batch.spi.Batch;
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.OperationResultChecker; import org.hibernate.engine.jdbc.mutation.OperationResultChecker;
@ -59,26 +54,14 @@ public class MutationExecutorPostInsert implements MutationExecutor {
private final PreparedStatementDetails identityInsertStatementDetails; private final PreparedStatementDetails identityInsertStatementDetails;
/**
* The batched statements
*/
private final Batch batch;
/** /**
* Any non-batched JDBC statements * Any non-batched JDBC statements
*/ */
private final PreparedStatementGroup nonBatchedStatementGroup; private final PreparedStatementGroup secondaryTablesStatementGroup;
private final JdbcValueBindingsImpl valueBindings; private final JdbcValueBindingsImpl valueBindings;
private enum StatementLocation { IDENTITY, BATCHED, NON_BATCHED } public MutationExecutorPostInsert(MutationOperationGroup mutationOperationGroup, SharedSessionContractImplementor session) {
private final Map<String, StatementLocation> statementLocationMap = new HashMap<>();
public MutationExecutorPostInsert(
MutationOperationGroup mutationOperationGroup,
Supplier<BatchKey> batchKeySupplier,
int batchSize,
SharedSessionContractImplementor session) {
this.mutationTarget = (EntityMutationTarget) mutationOperationGroup.getMutationTarget(); this.mutationTarget = (EntityMutationTarget) mutationOperationGroup.getMutationTarget();
this.valueBindings = new JdbcValueBindingsImpl( this.valueBindings = new JdbcValueBindingsImpl(
MutationType.INSERT, MutationType.INSERT,
@ -92,12 +75,8 @@ public class MutationExecutorPostInsert implements MutationExecutor {
identityInsertOperation, identityInsertOperation,
session session
); );
statementLocationMap.put( mutationTarget.getIdentifierTableName(), StatementLocation.IDENTITY );
final BatchKey batchKey = batchKeySupplier.get(); List<PreparableMutationOperation> secondaryTableMutations = null;
List<PreparableMutationOperation> batchedJdbcMutations = null;
List<PreparableMutationOperation> nonBatchedJdbcMutations = null;
final List<MutationOperation> operations = mutationOperationGroup.getOperations(); final List<MutationOperation> operations = mutationOperationGroup.getOperations();
for ( int i = 0; i < operations.size(); i++ ) { for ( int i = 0; i < operations.size(); i++ ) {
@ -112,48 +91,17 @@ public class MutationExecutorPostInsert implements MutationExecutor {
assert ! (operation instanceof SelfExecutingUpdateOperation ); assert ! (operation instanceof SelfExecutingUpdateOperation );
final PreparableMutationOperation preparableMutationOperation = (PreparableMutationOperation) operation; final PreparableMutationOperation preparableMutationOperation = (PreparableMutationOperation) operation;
if ( preparableMutationOperation.canBeBatched( batchKey, batchSize ) ) { if ( secondaryTableMutations == null ) {
if ( batchedJdbcMutations == null ) { secondaryTableMutations = new ArrayList<>();
batchedJdbcMutations = new ArrayList<>();
}
batchedJdbcMutations.add( preparableMutationOperation );
statementLocationMap.put( operation.getTableDetails().getTableName(), StatementLocation.BATCHED );
}
else {
if ( nonBatchedJdbcMutations == null ) {
nonBatchedJdbcMutations = new ArrayList<>();
}
nonBatchedJdbcMutations.add( preparableMutationOperation );
statementLocationMap.put( operation.getTableDetails().getTableName(), StatementLocation.NON_BATCHED );
} }
secondaryTableMutations.add( preparableMutationOperation );
} }
// todo (mutation) : consider creating single PreparedStatementGroup for all
// batched and non-batched statements. we then need a way to know whether a
// statement is batched or not. `PreparedStatementDetails#isBatched`?
if ( batchedJdbcMutations == null || batchedJdbcMutations.isEmpty() ) { this.secondaryTablesStatementGroup = ModelMutationHelper.toPreparedStatementGroup(
this.batch = null;
}
else {
final List<PreparableMutationOperation> batchedMutationsRef = batchedJdbcMutations;
this.batch = session.getJdbcCoordinator().getBatch2(
batchKey,
batchSize,
() -> ModelMutationHelper.toPreparedStatementGroup(
MutationType.INSERT,
mutationTarget,
batchedMutationsRef,
session
)
);
assert batch != null;
}
this.nonBatchedStatementGroup = ModelMutationHelper.toPreparedStatementGroup(
MutationType.INSERT, MutationType.INSERT,
mutationTarget, mutationTarget,
nonBatchedJdbcMutations, secondaryTableMutations,
session session
); );
} }
@ -174,27 +122,11 @@ public class MutationExecutorPostInsert implements MutationExecutor {
@Override @Override
public PreparedStatementDetails getPreparedStatementDetails(String tableName) { public PreparedStatementDetails getPreparedStatementDetails(String tableName) {
final StatementLocation statementLocation = statementLocationMap.get( tableName ); if ( mutationTarget.getIdentifierTableName().equals( tableName ) ) {
if ( statementLocation == null ) {
return null;
}
if ( statementLocation == StatementLocation.IDENTITY ) {
assert mutationTarget.getIdentifierTableName().equals( tableName );
return identityInsertStatementDetails; return identityInsertStatementDetails;
} }
if ( statementLocation == StatementLocation.BATCHED ) { return secondaryTablesStatementGroup.getPreparedStatementDetails( tableName );
assert batch != null;
return batch.getStatementGroup().getPreparedStatementDetails( tableName );
}
if ( statementLocation == StatementLocation.NON_BATCHED ) {
assert nonBatchedStatementGroup != null;
return nonBatchedStatementGroup.getPreparedStatementDetails( tableName );
}
return null;
} }
@Override @Override
@ -215,19 +147,8 @@ public class MutationExecutorPostInsert implements MutationExecutor {
); );
} }
if ( nonBatchedStatementGroup != null ) { if ( secondaryTablesStatementGroup != null ) {
nonBatchedStatementGroup.forEachStatement( (tableName, statementDetails) -> executeWithId( secondaryTablesStatementGroup.forEachStatement( (tableName, statementDetails) -> executeWithId(
id,
tableName,
statementDetails,
inclusionChecker,
resultChecker,
session
) );
}
if ( batch != null ) {
batch.getStatementGroup().forEachStatement( (tableName, statementDetails) -> executeWithId(
id, id,
tableName, tableName,
statementDetails, statementDetails,
@ -301,7 +222,7 @@ public class MutationExecutorPostInsert implements MutationExecutor {
@Override @Override
public void release() { public void release() {
nonBatchedStatementGroup.release(); secondaryTablesStatementGroup.release();
} }
@Override @Override

View File

@ -69,6 +69,6 @@ public class MutationExecutorServiceInitiator implements StandardServiceInitiato
} }
private MutationExecutorService createStandardService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) { private MutationExecutorService createStandardService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) {
return new StandardMutationExecutorService( configurationValues, registry ); return new StandardMutationExecutorService( configurationValues );
} }
} }

View File

@ -188,7 +188,6 @@ public class MutationExecutorStandard extends AbstractMutationExecutor {
@Override @Override
public void release() { public void release() {
nonBatchedStatementGroup.release(); nonBatchedStatementGroup.release();
// todo (mutation) :implement
} }
@Override @Override

View File

@ -51,12 +51,6 @@ public class PreparedStatementDetailsStandard implements PreparedStatementDetail
Supplier<PreparedStatement> jdbcStatementCreator, Supplier<PreparedStatement> jdbcStatementCreator,
Expectation expectation, Expectation expectation,
JdbcServices jdbcServices) { JdbcServices jdbcServices) {
// todo (mutation) : have `parameterDescriptors` be passed in.
// - these descriptors being only available relative solely
// to a preparable operation, rather than more widely scoped to
// the `MutationOperation`, causes problems for self-executing operations
this.mutatingTableDetails = tableMutation.getTableDetails(); this.mutatingTableDetails = tableMutation.getTableDetails();
this.sql = sql; this.sql = sql;
this.jdbcStatementCreator = jdbcStatementCreator; this.jdbcStatementCreator = jdbcStatementCreator;

View File

@ -17,7 +17,6 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.internal.util.config.ConfigurationHelper;
import org.hibernate.metamodel.mapping.EntityMappingType; import org.hibernate.metamodel.mapping.EntityMappingType;
import org.hibernate.persister.entity.mutation.EntityMutationTarget; import org.hibernate.persister.entity.mutation.EntityMutationTarget;
import org.hibernate.service.spi.ServiceRegistryImplementor;
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.MutationTarget; import org.hibernate.sql.model.MutationTarget;
@ -35,7 +34,7 @@ import org.hibernate.sql.model.SelfExecutingUpdateOperation;
public class StandardMutationExecutorService implements MutationExecutorService { public class StandardMutationExecutorService implements MutationExecutorService {
private final int globalBatchSize; private final int globalBatchSize;
public StandardMutationExecutorService(Map<String, Object> configurationValues, ServiceRegistryImplementor registry) { public StandardMutationExecutorService(Map<String, Object> configurationValues) {
this( ConfigurationHelper.getInt( Environment.STATEMENT_BATCH_SIZE, configurationValues, 1 ) ); this( ConfigurationHelper.getInt( Environment.STATEMENT_BATCH_SIZE, configurationValues, 1 ) );
} }
@ -66,7 +65,7 @@ public class StandardMutationExecutorService implements MutationExecutorService
assert mutationTarget instanceof EntityMappingType; assert mutationTarget instanceof EntityMappingType;
if ( numberOfOperations > 1 ) { if ( numberOfOperations > 1 ) {
return new MutationExecutorPostInsert( operationGroup, batchKeySupplier, batchSizeToUse, session ); return new MutationExecutorPostInsert( operationGroup, session );
} }
return new MutationExecutorPostInsertSingleTable( operationGroup, session ); return new MutationExecutorPostInsertSingleTable( operationGroup, session );

View File

@ -11,4 +11,7 @@
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
@Incubating
package org.hibernate.engine.jdbc.mutation; package org.hibernate.engine.jdbc.mutation;
import org.hibernate.Incubating;

View File

@ -7,12 +7,15 @@
/** /**
* Defines support for performing mutation operations against collections. * Defines support for performing mutation operations against collections.
* <p/> *
* The names used here are logical. E.g. "inserting a row" may actually * @apiNote The names used here are logical. E.g. "inserting a row" may actually
* execute an UPDATE statement instead of an INSERT. This is generally * execute an UPDATE statement instead of an INSERT. This is generally
* delineated based on whether there is a collection table involved or * delineated based on whether there is a collection table involved or
* not. In standard Hibernate terms, this breaks down to the distinction * not. In standard Hibernate terms, this breaks down to the distinction
* between {@link org.hibernate.persister.collection.BasicCollectionPersister} * between {@link org.hibernate.persister.collection.BasicCollectionPersister}
* and {@link org.hibernate.persister.collection.OneToManyPersister}. * and {@link org.hibernate.persister.collection.OneToManyPersister}.
*/ */
@Incubating
package org.hibernate.persister.collection.mutation; package org.hibernate.persister.collection.mutation;
import org.hibernate.Incubating;

View File

@ -0,0 +1,17 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
*/
/**
* Defines support for performing mutation operations originating
* from persistence-context events
*
* @author Steve Ebersole
*/
@Incubating
package org.hibernate.persister.entity.mutation;
import org.hibernate.Incubating;

View File

@ -77,7 +77,11 @@ public interface MutationOperation {
TableMapping getTableDetails(); TableMapping getTableDetails();
/** /**
* Find the JDBC parameter to be used for the specified column * Find the JDBC parameter to be used for the specified column.
*
* @return The descriptor, or null if none match.
*
* @see #getJdbcValueDescriptor
*/ */
JdbcValueDescriptor findValueDescriptor(String columnName, ParameterUsage usage); JdbcValueDescriptor findValueDescriptor(String columnName, ParameterUsage usage);