Skip to content

Commit e57dbd7

Browse files
mbelladesebersole
authored andcommitted
HHH-17922 - Redesign ActionQueue
Fixes Signed-off-by: Steve Ebersole <steve@hibernate.org>
1 parent 4445a62 commit e57dbd7

13 files changed

Lines changed: 129 additions & 128 deletions

File tree

‎hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/entity/EntityInsertMutationPlanner.java‎

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@ private Map<String, TableInsert> generateStaticOperations() {
176176
private TableInsertBuilder createTableInsertBuilder(TableDescriptor tableDescriptor) {
177177
final boolean isIdentifierTable = tableDescriptor instanceof EntityTableDescriptor entityTableDescriptor
178178
&& entityTableDescriptor.isIdentifierTable();
179+
180+
if ( isIdentifierTable ) {
181+
final var delegate = entityPersister.getInsertDelegate();
182+
if ( delegate != null ) {
183+
return (TableInsertBuilder) delegate.createTableMutationBuilder( null, sessionFactory );
184+
}
185+
}
186+
179187
final boolean isInverse = tableDescriptor instanceof EntityTableDescriptor entityTableDescriptor
180188
&& entityTableDescriptor.isInverse();
181189
final TableDescriptorAsTableMapping tableMapping = new TableDescriptorAsTableMapping(
@@ -200,12 +208,8 @@ private void applyTableInsertDetails(
200208
boolean forceIdentifierBinding) {
201209
final Dialect dialect = sessionFactory.getJdbcServices().getDialect();
202210

203-
builders.forEach( (name, builder) -> {
204-
final var tableMapping = (TableDescriptorAsTableMapping) builder.getMutatingTable().getTableMapping();
205-
final var tableDescriptor = (EntityTableDescriptor) tableMapping.descriptor();
206-
if ( tableDescriptor.isInverse() ) {
207-
return;
208-
}
211+
entityPersister.forEachMutableTableDescriptor( (tableDescriptor) -> {
212+
final var builder = builders.get( tableDescriptor.name() );
209213

210214
for ( int i = 0; i < tableDescriptor.attributes().size(); i++ ) {
211215
var attribute = tableDescriptor.attributes().get( i );
@@ -245,45 +249,42 @@ private void addKeysToInsertGroup(
245249
Dialect dialect,
246250
boolean forceIdentifierBinding) {
247251
builders.forEach( (name, builder) -> {
248-
final var tableMapping = (TableDescriptorAsTableMapping) builder.getMutatingTable().getTableMapping();
249-
final var tableDescriptor = (EntityTableDescriptor) tableMapping.descriptor();
250-
251-
if ( tableDescriptor.isIdentifierTable()
252+
final var tableMapping = builder.getMutatingTable().getTableMapping();
253+
if ( tableMapping.isIdentifierTable()
252254
&& entityPersister.isIdentifierAssignedByInsert()
253255
&& !forceIdentifierBinding ) {
254256
assert entityPersister.getInsertDelegate() != null;
255257
final var generator = (OnExecutionGenerator) entityPersister.getGenerator();
256-
if ( generator.referenceColumnsInSql( dialect ) ) {
257-
final String[] columnValues = generator.getReferencedColumnValues( dialect );
258-
if ( columnValues != null ) {
259-
final var keyColumns = tableDescriptor.keyDescriptor().columns();
260-
assert columnValues.length == keyColumns.size()
261-
: "Mismatch between referenced column values and key columns: "
262-
+ columnValues.length + " vs " + keyColumns.size();
263-
264-
for ( int i = 0; i < columnValues.length; i++ ) {
265-
if ( columnValues[i] != null ) {
258+
final boolean[] columnInclusions = generator.getColumnInclusions( dialect, INSERT );
259+
final String[] columnValues = generator.getReferencedColumnValues( dialect, INSERT );
260+
final var keyColumns = tableMapping.getKeyDetails().getKeyColumns();
261+
if ( columnInclusions != null ) {
262+
for ( int i = 0; i < keyColumns.size(); i++ ) {
263+
if ( columnInclusions[i] ) {
264+
if ( columnValues != null ) {
266265
builder.addColumnAssignment( keyColumns.get( i ), columnValues[i] );
267266
}
268267
else {
269268
builder.addColumnAssignment( keyColumns.get( i ) );
270269
}
271270
}
272271
}
272+
}
273+
else if ( generator.referenceColumnsInSql( dialect, INSERT ) ) {
274+
if ( columnValues != null ) {
275+
for ( int i = 0; i < keyColumns.size(); i++ ) {
276+
builder.addColumnAssignment( keyColumns.get( i ), columnValues[i] );
277+
}
278+
}
273279
else {
274-
for ( var keyColumn : tableDescriptor.keyDescriptor().columns() ) {
280+
for ( var keyColumn : keyColumns ) {
275281
builder.addColumnAssignment( keyColumn );
276282
}
277283
}
278284
}
279-
else {
280-
for ( var keyColumn : tableDescriptor.keyDescriptor().columns() ) {
281-
builder.addColumnAssignment( keyColumn );
282-
}
283-
}
284285
}
285286
else {
286-
for ( var keyColumn : tableDescriptor.keyDescriptor().columns() ) {
287+
for ( var keyColumn : tableMapping.getKeyDetails().getKeyColumns() ) {
287288
if ( !builder.hasColumnAssignment( keyColumn ) ) {
288289
builder.addColumnAssignment( keyColumn );
289290
}

‎hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/entity/InsertDecomposer.java‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
import org.hibernate.action.queue.spi.bind.GeneratedValuesCollector;
1414
import org.hibernate.action.queue.spi.bind.PostExecutionCallback;
1515
import org.hibernate.action.queue.spi.decompose.DecompositionContext;
16-
import org.hibernate.action.queue.spi.meta.EntityTableDescriptor;
17-
import org.hibernate.action.queue.spi.meta.TableDescriptorAsTableMapping;
1816
import org.hibernate.engine.internal.ForeignKeys;
1917
import org.hibernate.engine.internal.Nullability;
2018
import org.hibernate.sql.model.ast.TableInsert;
@@ -144,10 +142,12 @@ public void decompose(
144142

145143
int localOrd = 0;
146144
FlushOperation previousOperation = null;
147-
for ( Map.Entry<String, TableInsert> entry : effectiveGroup.entrySet() ) {
148-
var operation = entry.getValue().createMutationOperation(valuesAnalysis, sessionFactory);
149-
var tableMapping = (TableDescriptorAsTableMapping) operation.getTableDetails();
150-
var tableDescriptor = (EntityTableDescriptor) tableMapping.descriptor();
145+
for ( var tableDescriptor : entityPersister.getTableDescriptors() ) {
146+
if ( tableDescriptor.isInverse() ) {
147+
continue;
148+
}
149+
final var tableInsert = effectiveGroup.get( tableDescriptor.name() );
150+
var operation = tableInsert.createMutationOperation( valuesAnalysis, sessionFactory );
151151

152152
if ( !valuesAnalysis.include( tableDescriptor ) ) {
153153
continue;

‎hibernate-core/src/main/java/org/hibernate/action/queue/internal/decompose/entity/UpdateDecomposer.java‎

Lines changed: 37 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,15 @@ public void decompose(
274274
final boolean isBeingDeleted = decompositionContext != null
275275
&& decompositionContext.isBeingDeletedInCurrentFlush( entity );
276276

277-
for ( Map.Entry<String, LogicalTableUpdate<?>> entry : effectiveGroup.entrySet() ) {
278-
var operation = entry.getValue().createMutationOperation(null, sessionFactory);
279-
var tableMapping = (TableDescriptorAsTableMapping) operation.getTableDetails();
280-
var tableDescriptor = (EntityTableDescriptor) tableMapping.descriptor();
277+
for ( var tableDescriptor : entityPersister.getTableDescriptors() ) {
278+
if ( tableDescriptor.isInverse() ) {
279+
continue;
280+
}
281+
final var tableUpdate = effectiveGroup.get( tableDescriptor.name() );
282+
if ( tableUpdate == null ) {
283+
continue;
284+
}
285+
var operation = tableUpdate.createMutationOperation( null, sessionFactory );
281286

282287
// For static updates, only execute secondary tables when one of their
283288
// attributes actually drove the update. Static mutation groups may
@@ -292,9 +297,9 @@ public void decompose(
292297

293298
// If this entity is being deleted in the same flush, skip UPDATEs to optional tables.
294299
// These UPDATEs can re-insert rows that were just deleted, causing constraint violations.
295-
// DELETE → DELETE edges ensure proper FK ordering, so UPDATEs are unnecessary.
300+
// DELETE -> DELETE edges ensure proper FK ordering, so UPDATEs are unnecessary.
296301
//
297-
// Technically we could alo skip updates to the primary table if no foreign keys are being nullified.
302+
// Technically we could also skip updates to the primary table if no foreign keys are being nullified.
298303
// This should be a rare enough occurrence that it is probably not worth even checking for.
299304
if ( isBeingDeleted && tableDescriptor.isOptional() ) {
300305
continue;
@@ -608,6 +613,14 @@ private Map<String, LogicalTableUpdate<?>> generateStaticOperations() {
608613
}
609614

610615
private TableUpdateBuilder<?> createTableUpdateBuilder(TableDescriptor tableDescriptor) {
616+
if ( tableDescriptor instanceof EntityTableDescriptor entityTableDescriptor
617+
&& entityTableDescriptor.isIdentifierTable() ) {
618+
final var delegate = entityPersister.getUpdateDelegate();
619+
if ( delegate != null ) {
620+
return (TableUpdateBuilder<?>) delegate.createTableMutationBuilder( null, sessionFactory );
621+
}
622+
}
623+
611624
return new TableUpdateBuilderStandard<>(
612625
entityPersister,
613626
new MutatingTableReference( getTableMappingAdapter( tableDescriptor ) ),
@@ -683,14 +696,8 @@ protected Map<String, LogicalTableUpdate<?>> generateDynamicUpdateOperations(
683696
private void applyStaticUpdateDetails(Map<String, TableUpdateBuilder<?>> builders) {
684697
final boolean[] propertyUpdateability = entityPersister.getPropertyUpdateability();
685698

686-
builders.forEach( (name, builder) -> {
687-
// Get the TableDescriptor from the adapter
688-
final var tableMapping = (TableDescriptorAsTableMapping) builder.getMutatingTable().getTableMapping();
689-
final var tableDescriptor = (EntityTableDescriptor) tableMapping.descriptor();
690-
// Skip inverse tables
691-
if (tableDescriptor.isInverse()) {
692-
return;
693-
}
699+
entityPersister.forEachMutableTableDescriptor( (tableDescriptor) -> {
700+
final var builder = builders.get( tableDescriptor.name() );
694701

695702
// Apply SET clause columns for attributes
696703
for ( int i = 0; i < tableDescriptor.attributes().size(); i++ ) {
@@ -710,10 +717,10 @@ private void applyStaticUpdateDetails(Map<String, TableUpdateBuilder<?>> builder
710717
}
711718

712719
// Apply WHERE clause - key restrictions
713-
applyKeyRestriction( builder );
720+
applyKeyRestriction( tableDescriptor, builder );
714721

715722
// Apply optimistic locking
716-
applyOptimisticLocking( builder, null, null, null );
723+
applyOptimisticLocking( tableDescriptor, builder, null, null, null );
717724
} );
718725

719726
// Apply partitioned selection restrictions if needed
@@ -781,12 +788,9 @@ private void applyDynamicUpdateDetails(
781788
UpdateValuesAnalysis valuesAnalysis,
782789
boolean[] updateable,
783790
SharedSessionContractImplementor session) {
784-
builders.forEach( (name, builder) -> {
785-
// Get the TableDescriptor from the adapter
786-
final var tableMapping = (TableDescriptorAsTableMapping) builder.getMutatingTable().getTableMapping();
787-
final var tableDescriptor = (EntityTableDescriptor) tableMapping.descriptor();
788-
// Skip inverse tables
789-
if (tableDescriptor.isInverse()) {
791+
entityPersister.forEachMutableTableDescriptor( (tableDescriptor) -> {
792+
final var builder = builders.get( tableDescriptor.name() );
793+
if ( builder == null ) {
790794
return;
791795
}
792796

@@ -803,10 +807,10 @@ private void applyDynamicUpdateDetails(
803807
}
804808

805809
// Apply WHERE clause - key restrictions
806-
applyKeyRestriction( builder, rowId );
810+
applyKeyRestriction( tableDescriptor, builder, rowId );
807811

808812
// Apply optimistic locking
809-
applyOptimisticLocking( builder, previousState, valuesAnalysis, session );
813+
applyOptimisticLocking( tableDescriptor, builder, previousState, valuesAnalysis, session );
810814
} );
811815

812816
// Apply partitioned selection restrictions if needed
@@ -841,17 +845,13 @@ private boolean shouldIncludeInDynamicUpdate(
841845
return true;
842846
}
843847

844-
private void applyKeyRestriction(TableUpdateBuilder<?> tableUpdateBuilder) {
845-
applyKeyRestriction( tableUpdateBuilder, null );
848+
private void applyKeyRestriction(EntityTableDescriptor tableDescriptor, TableUpdateBuilder<?> tableUpdateBuilder) {
849+
applyKeyRestriction( tableDescriptor, tableUpdateBuilder, null );
846850
}
847851

848-
private void applyKeyRestriction(TableUpdateBuilder<?> tableUpdateBuilder, Object rowId) {
849-
// Get the TableDescriptor from the adapter
850-
final var tableMapping = (TableDescriptorAsTableMapping) tableUpdateBuilder.getMutatingTable().getTableMapping();
851-
final var tableDescriptor = tableMapping.descriptor();
852+
private void applyKeyRestriction(EntityTableDescriptor tableDescriptor, TableUpdateBuilder<?> tableUpdateBuilder, Object rowId) {
852853
if ( rowId != null
853-
&& tableDescriptor instanceof EntityTableDescriptor entityTableDescriptor
854-
&& entityTableDescriptor.isIdentifierTable()
854+
&& tableDescriptor.isIdentifierTable()
855855
&& entityPersister.getRowIdMapping() != null ) {
856856
tableUpdateBuilder.addKeyRestrictionLeniently( entityPersister.getRowIdMapping() );
857857
}
@@ -861,25 +861,23 @@ private void applyKeyRestriction(TableUpdateBuilder<?> tableUpdateBuilder, Objec
861861
}
862862

863863
private void applyOptimisticLocking(
864+
EntityTableDescriptor tableDescriptor,
864865
TableUpdateBuilder<?> tableUpdateBuilder,
865866
Object[] previousState,
866867
UpdateValuesAnalysis valuesAnalysis,
867868
SharedSessionContractImplementor session) {
868869
final var optimisticLockStyle = entityPersister.optimisticLockStyle();
869870

870871
if ( optimisticLockStyle.isVersion() && entityPersister.getVersionMapping() != null ) {
871-
applyVersionBasedOptLocking( tableUpdateBuilder );
872+
applyVersionBasedOptLocking( tableDescriptor, tableUpdateBuilder );
872873
}
873874
else if ( optimisticLockStyle.isAllOrDirty() ) {
874-
applyNonVersionOptLocking( tableUpdateBuilder, previousState, optimisticLockStyle, valuesAnalysis, session );
875+
applyNonVersionOptLocking( tableDescriptor, tableUpdateBuilder, previousState, optimisticLockStyle, valuesAnalysis, session );
875876
}
876877
}
877878

878-
private void applyVersionBasedOptLocking(TableUpdateBuilder<?> tableUpdateBuilder) {
879+
private void applyVersionBasedOptLocking(EntityTableDescriptor tableDescriptor, TableUpdateBuilder<?> tableUpdateBuilder) {
879880
final var versionMapping = entityPersister.getVersionMapping();
880-
// Get the TableDescriptor from the adapter
881-
final var tableMapping = (TableDescriptorAsTableMapping) tableUpdateBuilder.getMutatingTable().getTableMapping();
882-
final var tableDescriptor = tableMapping.descriptor();
883881
if ( versionMapping != null
884882
&& tableDescriptor.name().equals(
885883
versionMapping.getContainingTableExpression() ) ) {
@@ -888,6 +886,7 @@ private void applyVersionBasedOptLocking(TableUpdateBuilder<?> tableUpdateBuilde
888886
}
889887

890888
private void applyNonVersionOptLocking(
889+
EntityTableDescriptor tableDescriptor,
891890
TableUpdateBuilder<?> tableUpdateBuilder,
892891
Object[] previousState,
893892
OptimisticLockStyle optimisticLockStyle,
@@ -902,9 +901,6 @@ private void applyNonVersionOptLocking(
902901
}
903902

904903
final boolean[] versionability = entityPersister.getPropertyVersionability();
905-
// Get the TableDescriptor from the adapter
906-
final var tableMapping = (TableDescriptorAsTableMapping) tableUpdateBuilder.getMutatingTable().getTableMapping();
907-
final var tableDescriptor = (EntityTableDescriptor) tableMapping.descriptor();
908904

909905
for ( int i = 0; i < tableDescriptor.attributes().size(); i++ ) {
910906
var attribute = tableDescriptor.attributes().get( i );

‎hibernate-core/src/main/java/org/hibernate/action/queue/internal/exec/AbstractStepExecutor.java‎

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import org.hibernate.action.queue.spi.MutationKind;
1010
import org.hibernate.action.queue.spi.bind.JdbcValueBindings;
1111
import org.hibernate.action.queue.internal.cyclebreak.FixupSynthesizer;
12-
import org.hibernate.action.queue.spi.plan.FlushOperation;
1312
import org.hibernate.engine.jdbc.mutation.internal.JdbcValueBindingsImpl;
1413
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1514
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
@@ -19,6 +18,7 @@
1918
import org.hibernate.sql.model.SelfExecutingUpdateOperation;
2019

2120
import java.sql.Connection;
21+
import java.sql.SQLException;
2222
import java.util.List;
2323
import java.util.function.Consumer;
2424

@@ -177,15 +177,55 @@ protected void executeWithGeneratedValues(FlushOperation flushOperation) {
177177
generatedValuesDelegate = null;
178178
}
179179

180-
var generatedValues = generatedValuesDelegate.performGraphMutation(
181-
flushOperation,
182-
bindPlan.getEntityInstance(),
183-
session
184-
);
180+
if ( generatedValuesDelegate != null ) {
181+
final var generatedValues = generatedValuesDelegate.performGraphMutation(
182+
flushOperation,
183+
bindPlan.getEntityInstance(),
184+
session
185+
);
186+
generatedValuesCollector.apply( generatedValues );
187+
}
188+
else {
189+
// fallback to direct statement execution (no batching)
190+
final var jdbcOperation = flushOperation.getJdbcOperation();
191+
if ( jdbcOperation instanceof PreparableMutationOperation preparable ) {
192+
executePreparableDirectly( preparable, flushOperation );
193+
}
194+
else if ( jdbcOperation instanceof SelfExecutingUpdateOperation selfExecuting ) {
195+
executeSelfExecuting( selfExecuting, flushOperation );
196+
}
197+
}
198+
}
199+
200+
protected void executePreparableDirectly(
201+
PreparableMutationOperation preparable,
202+
FlushOperation flushOperation) {
203+
try (var stmnt = session.getJdbcCoordinator()
204+
.getStatementPreparer()
205+
.prepareStatement( preparable.getSqlString() )) {
206+
var valueBindings = new JdbcValueBindings( flushOperation.getMutatingTableDescriptor(), preparable );
207+
flushOperation.getBindPlan().bindValues( valueBindings, flushOperation, session );
208+
valueBindings.beforeStatement( stmnt, session );
209+
210+
final int affectedRowCount = session.getJdbcCoordinator()
211+
.getResultSetReturn()
212+
.executeUpdate( stmnt, preparable.getSqlString() );
213+
214+
final var resultChecker = flushOperation.getBindPlan().getOperationResultChecker();
215+
if ( resultChecker != null ) {
216+
resultChecker.checkResult( affectedRowCount, -1, preparable.getSqlString(), session.getFactory() );
217+
}
185218

186-
generatedValuesCollector.apply( generatedValues );
219+
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( stmnt );
220+
}
221+
catch (SQLException sqle) {
222+
throw session.getJdbcServices()
223+
.getSqlExceptionHelper()
224+
.convert( sqle, "Unable to execute non-batched mutation - " + preparable.getSqlString() );
225+
}
187226
}
188227

228+
189229
protected void executeSelfExecuting(SelfExecutingUpdateOperation selfExecuting, FlushOperation flushOperation) {
190230
final var graphBindings = new JdbcValueBindings(
191231
flushOperation.getMutatingTableDescriptor(),

‎hibernate-core/src/main/java/org/hibernate/action/queue/internal/exec/BatchingPlanStepExecutor.java‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
import org.hibernate.action.queue.spi.StatementShapeKey;
1212
import org.hibernate.action.queue.spi.bind.JdbcValueBindings;
1313
import org.hibernate.action.queue.spi.bind.OperationResultChecker;
14-
import org.hibernate.action.queue.spi.plan.FlushOperation;
1514
import org.hibernate.engine.jdbc.batch.spi.Batch;
1615
import org.hibernate.engine.jdbc.batch.spi.StaleStateMapper;
1716
import org.hibernate.engine.jdbc.mutation.internal.JdbcValueBindingsImpl;

0 commit comments

Comments
 (0)