diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java index 86b59d2a6f44..15c9ca7a81cc 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java @@ -162,26 +162,11 @@ public void addToBatch(JdbcValueBindings jdbcValueBindings, TableInclusionChecke if ( batchPosition == batchSizeToUse ) { notifyObserversImplicitExecution(); performExecution(); - batchPosition = 0; - batchExecuted = true; } } protected void releaseStatements() { - statementGroup.forEachStatement( (tableName, statementDetails) -> { - if ( statementDetails.getStatement() == null ) { - BATCH_LOGGER.debugf( - "PreparedStatementDetails did not contain PreparedStatement on #releaseStatements : %s", - statementDetails.getSqlString() - ); - } - else { - clearBatch( statementDetails ); - } - } ); - statementGroup.release(); - jdbcCoordinator.afterStatementExecution(); } protected void clearBatch(PreparedStatementDetails statementDetails) { @@ -299,8 +284,10 @@ protected void performExecution() { } } } ); + batchExecuted = true; } finally { + jdbcCoordinator.afterStatementExecution(); batchPosition = 0; } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java index 8581b10f0041..39c6a4bde8a6 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java @@ -167,8 +167,12 @@ public Batch getBatch(BatchKey key, Integer batchSize, Supplier jdbcStatementCreator, @@ -66,6 +68,7 @@ public void releaseStatement(SharedSessionContractImplementor session) { if ( statement != null ) { session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( statement ); statement = null; + toRelease = false; } } @@ -82,6 +85,7 @@ public PreparedStatement getStatement() { @Override public PreparedStatement resolveStatement() { if ( statement == null ) { + toRelease = true; statement = jdbcStatementCreator.get(); try { expectation.prepare( statement ); @@ -102,6 +106,11 @@ public Expectation getExpectation() { return expectation; } + @Override + public boolean toRelease() { + return toRelease; + } + @Override public String toString() { return "PreparedStatementDetails(" + sql + ")"; diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupSingleTable.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupSingleTable.java index 59682d403d3d..50fd8ffc337a 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupSingleTable.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupSingleTable.java @@ -8,7 +8,6 @@ import java.util.function.Predicate; import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; -import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup; import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.generator.values.GeneratedValuesMutationDelegate; import org.hibernate.sql.model.PreparableMutationOperation; @@ -20,9 +19,8 @@ * * @author Steve Ebersole */ -public class PreparedStatementGroupSingleTable implements PreparedStatementGroup { +public class PreparedStatementGroupSingleTable extends AbstractPreparedStatementGroup { private final PreparableMutationOperation jdbcMutation; - private final SharedSessionContractImplementor session; private final PreparedStatementDetails statementDetails; @@ -36,9 +34,9 @@ public PreparedStatementGroupSingleTable( PreparableMutationOperation jdbcMutation, GeneratedValuesMutationDelegate delegate, SharedSessionContractImplementor session) { + super(session); this.jdbcMutation = jdbcMutation; this.statementDetails = ModelMutationHelper.standardPreparation( jdbcMutation, delegate, session ); - this.session = session; } protected TableMapping getMutatingTableDetails() { @@ -89,7 +87,7 @@ public boolean hasMatching(Predicate filter) { @Override public void release() { if ( statementDetails != null ) { - statementDetails.releaseStatement( session ); + release( statementDetails ); } } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupStandard.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupStandard.java index 820b72f98954..5a74624e84a2 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupStandard.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupStandard.java @@ -16,7 +16,6 @@ import java.util.function.Supplier; import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails; -import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup; import org.hibernate.engine.jdbc.spi.JdbcCoordinator; import org.hibernate.engine.jdbc.spi.MutationStatementPreparer; import org.hibernate.engine.spi.SharedSessionContractImplementor; @@ -32,11 +31,10 @@ * * @author Steve Ebersole */ -public class PreparedStatementGroupStandard implements PreparedStatementGroup { +public class PreparedStatementGroupStandard extends AbstractPreparedStatementGroup { private final MutationType mutationType; private final MutationTarget mutationTarget; private final List jdbcMutations; - private final SharedSessionContractImplementor session; private final SortedMap statementMap; @@ -47,11 +45,11 @@ public PreparedStatementGroupStandard( GeneratedValuesMutationDelegate generatedValuesDelegate, List jdbcMutations, SharedSessionContractImplementor session) { + super( session ); this.mutationType = mutationType; this.mutationTarget = mutationTarget; this.jdbcMutations = jdbcMutations; - this.session = session; this.statementMap = createStatementDetailsMap( jdbcMutations, mutationType, generatedValuesDelegate, session ); } @@ -143,10 +141,11 @@ private static PreparedStatementDetails createPreparedStatementDetails( @Override public void release() { - statementMap.forEach( (tableName, statementDetails) -> statementDetails.releaseStatement( session ) ); + statementMap.forEach( (tableName, statementDetails) -> { + release( statementDetails ); + } ); } - private static SortedMap createStatementDetailsMap( List jdbcMutations, MutationType mutationType, diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchReleaseStatementDebugMessageTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchReleaseStatementDebugMessageTest.java new file mode 100644 index 000000000000..f9121da288c5 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/batch/BatchReleaseStatementDebugMessageTest.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.batch; + +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.engine.jdbc.batch.JdbcBatchLogging; +import org.hibernate.testing.logger.LogInspectionHelper; +import org.hibernate.testing.logger.LogListener; +import org.hibernate.testing.logger.Triggerable; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.ServiceRegistry; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.Setting; +import org.jboss.logging.Logger; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CopyOnWriteArrayList; + +import static org.assertj.core.api.Assertions.assertThat; + +@SessionFactory +@DomainModel(annotatedClasses = { + BatchReleaseStatementDebugMessageTest.TestEntity.class +}) +@ServiceRegistry(settings = @Setting(name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "3")) +@JiraKey("HHH-13615") +public class BatchReleaseStatementDebugMessageTest { + private TriggerOnDebugMessageListener trigger; + + @BeforeAll + public void setUp(SessionFactoryScope scope) { + trigger = new TriggerOnDebugMessageListener(); + LogInspectionHelper.registerListener( trigger, JdbcBatchLogging.BATCH_LOGGER ); + } + + @Test + public void testLogIsNotGenerated(SessionFactoryScope scope) { + + scope.inTransaction( + session -> { + session.persist( new TestEntity( 1L ) ); + } + ); + assertThat( trigger.wasTriggered() ).as( "Debug message was triggered" ).isFalse(); + + } + + private static class TriggerOnDebugMessageListener implements LogListener, Triggerable { + private final List triggerMessages = new CopyOnWriteArrayList<>(); + + @Override + public void loggedEvent(Logger.Level level, String renderedMessage, Throwable thrown) { + if ( renderedMessage.toLowerCase( Locale.ROOT ) + .contains( "preparedstatementdetails did not contain preparedstatement" ) ) { + triggerMessages.add( renderedMessage ); + } + } + + @Override + public String triggerMessage() { + return !triggerMessages.isEmpty() ? triggerMessages.get( 0 ) : null; + } + + @Override + public List triggerMessages() { + return triggerMessages; + } + + @Override + public boolean wasTriggered() { + return !triggerMessages.isEmpty(); + } + + @Override + public void reset() { + triggerMessages.clear(); + } + + } + + @Entity(name = "TestEntity") + public static class TestEntity { + @Id + private Long id; + + private String name; + + public TestEntity() { + } + + public TestEntity(Long id) { + this.id = id; + } + } +}