Skip to content

Commit e53ff58

Browse files
committed
HHH-18692 Hibernate attempts to close batched statements multiple times
1 parent b5c4f46 commit e53ff58

File tree

7 files changed

+93
-29
lines changed

7 files changed

+93
-29
lines changed

hibernate-core/src/main/java/org/hibernate/engine/jdbc/batch/internal/BatchImpl.java

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -162,26 +162,11 @@ public void addToBatch(JdbcValueBindings jdbcValueBindings, TableInclusionChecke
162162
if ( batchPosition == batchSizeToUse ) {
163163
notifyObserversImplicitExecution();
164164
performExecution();
165-
batchPosition = 0;
166-
batchExecuted = true;
167165
}
168166
}
169167

170168
protected void releaseStatements() {
171-
statementGroup.forEachStatement( (tableName, statementDetails) -> {
172-
if ( statementDetails.getStatement() == null ) {
173-
BATCH_LOGGER.debugf(
174-
"PreparedStatementDetails did not contain PreparedStatement on #releaseStatements : %s",
175-
statementDetails.getSqlString()
176-
);
177-
}
178-
else {
179-
clearBatch( statementDetails );
180-
}
181-
} );
182-
183169
statementGroup.release();
184-
jdbcCoordinator.afterStatementExecution();
185170
}
186171

187172
protected void clearBatch(PreparedStatementDetails statementDetails) {
@@ -299,8 +284,10 @@ protected void performExecution() {
299284
}
300285
}
301286
} );
287+
batchExecuted = true;
302288
}
303289
finally {
290+
jdbcCoordinator.afterStatementExecution();
304291
batchPosition = 0;
305292
}
306293
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/JdbcCoordinatorImpl.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,12 @@ public Batch getBatch(BatchKey key, Integer batchSize, Supplier<PreparedStatemen
165165
return currentBatch;
166166
}
167167
else {
168-
currentBatch.execute();
169-
currentBatch.release();
168+
try {
169+
currentBatch.execute();
170+
}
171+
finally {
172+
currentBatch.release();
173+
}
170174
}
171175
}
172176

@@ -192,7 +196,12 @@ public void executeBatch() {
192196
public void conditionallyExecuteBatch(BatchKey key) {
193197
if ( currentBatch != null && !currentBatch.getKey().equals( key ) ) {
194198
JdbcBatchLogging.BATCH_LOGGER.debugf( "Conditionally executing batch - %s", currentBatch.getKey() );
195-
currentBatch.execute();
199+
try {
200+
currentBatch.execute();
201+
}
202+
finally {
203+
currentBatch.release();
204+
}
196205
}
197206
}
198207

hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/group/PreparedStatementDetails.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,8 @@ default boolean isCallable() {
5656
}
5757

5858
void releaseStatement(SharedSessionContractImplementor session);
59+
60+
default boolean toRelease(){
61+
return false;
62+
}
5963
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* SPDX-License-Identifier: LGPL-2.1-or-later
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.engine.jdbc.mutation.internal;
6+
7+
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
8+
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup;
9+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
10+
11+
import java.sql.PreparedStatement;
12+
import java.sql.SQLException;
13+
14+
import static org.hibernate.engine.jdbc.batch.JdbcBatchLogging.BATCH_LOGGER;
15+
import static org.hibernate.engine.jdbc.batch.JdbcBatchLogging.BATCH_MESSAGE_LOGGER;
16+
17+
public abstract class AbstractPreparedStatementGroup implements PreparedStatementGroup {
18+
private final SharedSessionContractImplementor session;
19+
20+
public AbstractPreparedStatementGroup(SharedSessionContractImplementor session) {
21+
this.session = session;
22+
}
23+
24+
protected void clearBatch(PreparedStatementDetails statementDetails) {
25+
final PreparedStatement statement = statementDetails.getStatement();
26+
assert statement != null;
27+
28+
try {
29+
// This code can be called after the connection is released
30+
// and the statement is closed. If the statement is closed,
31+
// then SQLException will be thrown when PreparedStatement#clearBatch
32+
// is called.
33+
// Ensure the statement is not closed before
34+
// calling PreparedStatement#clearBatch.
35+
if ( !statement.isClosed() ) {
36+
statement.clearBatch();
37+
}
38+
}
39+
catch ( SQLException e ) {
40+
BATCH_MESSAGE_LOGGER.unableToReleaseBatchStatement();
41+
}
42+
}
43+
44+
protected void release(PreparedStatementDetails statementDetails) {
45+
if ( statementDetails.toRelease() ) {
46+
if ( statementDetails.getStatement() == null ) {
47+
BATCH_LOGGER.debugf(
48+
"PreparedStatementDetails did not contain PreparedStatement on #releaseStatements : %s",
49+
statementDetails.getSqlString()
50+
);
51+
}
52+
else {
53+
clearBatch( statementDetails );
54+
}
55+
statementDetails.releaseStatement( session );
56+
}
57+
}
58+
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementDetailsStandard.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class PreparedStatementDetailsStandard implements PreparedStatementDetail
3030

3131
private PreparedStatement statement;
3232

33+
private boolean toRelease;
34+
3335
public PreparedStatementDetailsStandard(
3436
PreparableMutationOperation tableMutation,
3537
Supplier<PreparedStatement> jdbcStatementCreator,
@@ -66,6 +68,7 @@ public void releaseStatement(SharedSessionContractImplementor session) {
6668
if ( statement != null ) {
6769
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( statement );
6870
statement = null;
71+
toRelease = false;
6972
}
7073
}
7174

@@ -82,6 +85,7 @@ public PreparedStatement getStatement() {
8285
@Override
8386
public PreparedStatement resolveStatement() {
8487
if ( statement == null ) {
88+
toRelease = true;
8589
statement = jdbcStatementCreator.get();
8690
try {
8791
expectation.prepare( statement );
@@ -102,6 +106,11 @@ public Expectation getExpectation() {
102106
return expectation;
103107
}
104108

109+
@Override
110+
public boolean toRelease() {
111+
return toRelease;
112+
}
113+
105114
@Override
106115
public String toString() {
107116
return "PreparedStatementDetails(" + sql + ")";

hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupSingleTable.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import java.util.function.Predicate;
99

1010
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
11-
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup;
1211
import org.hibernate.engine.spi.SharedSessionContractImplementor;
1312
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
1413
import org.hibernate.sql.model.PreparableMutationOperation;
@@ -20,9 +19,8 @@
2019
*
2120
* @author Steve Ebersole
2221
*/
23-
public class PreparedStatementGroupSingleTable implements PreparedStatementGroup {
22+
public class PreparedStatementGroupSingleTable extends AbstractPreparedStatementGroup {
2423
private final PreparableMutationOperation jdbcMutation;
25-
private final SharedSessionContractImplementor session;
2624

2725
private final PreparedStatementDetails statementDetails;
2826

@@ -36,9 +34,9 @@ public PreparedStatementGroupSingleTable(
3634
PreparableMutationOperation jdbcMutation,
3735
GeneratedValuesMutationDelegate delegate,
3836
SharedSessionContractImplementor session) {
37+
super(session);
3938
this.jdbcMutation = jdbcMutation;
4039
this.statementDetails = ModelMutationHelper.standardPreparation( jdbcMutation, delegate, session );
41-
this.session = session;
4240
}
4341

4442
protected TableMapping getMutatingTableDetails() {
@@ -89,7 +87,7 @@ public boolean hasMatching(Predicate<PreparedStatementDetails> filter) {
8987
@Override
9088
public void release() {
9189
if ( statementDetails != null ) {
92-
statementDetails.releaseStatement( session );
90+
release( statementDetails );
9391
}
9492
}
9593
}

hibernate-core/src/main/java/org/hibernate/engine/jdbc/mutation/internal/PreparedStatementGroupStandard.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.util.function.Supplier;
1717

1818
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
19-
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup;
2019
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
2120
import org.hibernate.engine.jdbc.spi.MutationStatementPreparer;
2221
import org.hibernate.engine.spi.SharedSessionContractImplementor;
@@ -32,11 +31,10 @@
3231
*
3332
* @author Steve Ebersole
3433
*/
35-
public class PreparedStatementGroupStandard implements PreparedStatementGroup {
34+
public class PreparedStatementGroupStandard extends AbstractPreparedStatementGroup {
3635
private final MutationType mutationType;
3736
private final MutationTarget<?> mutationTarget;
3837
private final List<PreparableMutationOperation> jdbcMutations;
39-
private final SharedSessionContractImplementor session;
4038

4139
private final SortedMap<String, PreparedStatementDetails> statementMap;
4240

@@ -47,11 +45,11 @@ public PreparedStatementGroupStandard(
4745
GeneratedValuesMutationDelegate generatedValuesDelegate,
4846
List<PreparableMutationOperation> jdbcMutations,
4947
SharedSessionContractImplementor session) {
48+
super( session );
5049
this.mutationType = mutationType;
5150
this.mutationTarget = mutationTarget;
5251
this.jdbcMutations = jdbcMutations;
5352

54-
this.session = session;
5553

5654
this.statementMap = createStatementDetailsMap( jdbcMutations, mutationType, generatedValuesDelegate, session );
5755
}
@@ -143,10 +141,11 @@ private static PreparedStatementDetails createPreparedStatementDetails(
143141

144142
@Override
145143
public void release() {
146-
statementMap.forEach( (tableName, statementDetails) -> statementDetails.releaseStatement( session ) );
144+
statementMap.forEach( (tableName, statementDetails) -> {
145+
release( statementDetails );
146+
} );
147147
}
148148

149-
150149
private static SortedMap<String, PreparedStatementDetails> createStatementDetailsMap(
151150
List<PreparableMutationOperation> jdbcMutations,
152151
MutationType mutationType,

0 commit comments

Comments
 (0)