Driver version
com.microsoft.sqlserver
mssql-jdbc
12.6.1.jre11
SQL Server version
Microsoft SQL Server 2019 (RTM-CU18) (KB5017593) - 15.0.4261.1 (X64)
Sep 12 2022 15:07:06
Copyright (C) 2019 Microsoft Corporation
Developer Edition (64-bit) on Windows 10 Pro 10.0 <X64> (Build 19045: )
Client Operating System
Windows 10 Pro
JAVA/JVM version
Java 17
Problem description
There are some duplicate inserts and some missing entries when a custom connection pool is being utilized and server side statement pooling is enabled (using the setDisableStatementPooling flag). When the connection is re-used from the connection pool and the flag setDisableStatementPooling is set to false this issue seems to happen.
To test this instance, I was able to simulate a very basic stand alone as below :
Please see the following test program, where I intentionally return the previously created connection object from the _userToConnectionsMap cache map for subsequent runs.
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sql.PooledConnection;
public class SQLConnectionPool {
final private ReentrantLock _connLock = new ReentrantLock();
final private Map _userToConnectionsMap = new ConcurrentHashMap();
static String tableName = "batchTest";
/**
* Crate a new connection
*
* @return the new created connection
* @throws SQLException When fail to create a new connection.
*/
private Connection createNewConnection(final String username, final String password) throws SQLException {
com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource msDs = new com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource();
msDs.setUser(username);
msDs.setApplicationName("SQL Server Duplicate Row");
msDs.setPassword(password);
msDs.setServerName("<>");
msDs.setPortNumber(0);
msDs.setDatabaseName("<>");
msDs.setInstanceName("<>");
msDs.setEncrypt("false");
msDs.setDisableStatementPooling(false);
msDs.setStatementPoolingCacheSize(20);
msDs.setServerPreparedStatementDiscardThreshold(20);
msDs.setSendTemporalDataTypesAsStringForBulkCopy(false);
// ... create the ACTUAL new connection...
PooledConnection actual = msDs.getPooledConnection(username, password);
// ... cache it...
Collection conns = (Collection) _userToConnectionsMap.get(username);
if (conns == null || conns.isEmpty()) {
conns = new CopyOnWriteArrayList();
_userToConnectionsMap.put(username, conns);
}
conns.add(actual);
return actual.getConnection();
}
final public Connection getConnection(final String username, final String password)
throws SQLException, InterruptedException {
if (username == null) {
throw new NullPointerException("The username cannot be null.");
}
if (password == null) {
throw new NullPointerException("The password cannot be null.");
}
// Acquire a lock...
_connLock.lock();
// Declare the connection to be returned...
Connection conn = null;
try {
conn = getAvailableConnection(username);
// If none found...
if (conn == null) {
conn = createNewConnection(username, password);
}
//
} finally {
// Ensure that the lock is released...
_connLock.unlock();
}
// Return the connection...
return conn;
}
private Connection getAvailableConnection(final String username) throws SQLException {
// Get the connections for the specified user...
Collection conns = (Collection) _userToConnectionsMap.get(username);
// If none found, then NO connections are available...
if (conns == null || conns.isEmpty()) {
return null;
}
// ... otherwise, check to see if any of the connections are available...
boolean isAvailable = false;
final Iterator iterator = conns.iterator();
PooledConnection pconn = null;
while (iterator.hasNext()) {
pconn = (PooledConnection) iterator.next();
return pconn.getConnection();
// Is this connection available? ignore this check for this sample test
}
return null;
}
public static void main(String[] args) throws SQLException, ClassNotFoundException, InterruptedException {
Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.Statement");
logger.setLevel(Level.FINER);
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
SQLConnectionPool pool = new SQLConnectionPool();
try {
Connection conn = pool.getConnection("<>", "<>");
try (Statement stmt = conn.createStatement();) {
String dropSql = "if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[" + tableName
+ "]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) DROP TABLE [" + tableName + "]";
stmt.execute(dropSql);
String createSql = "create table " + tableName + " (c1 int, c2 varchar(20))";
stmt.execute(createSql);
}
for (int j = 0; j < 5; j++) {
conn = pool.getConnection("<>", "<>");
conn.setAutoCommit(false);
try (PreparedStatement pstmt = conn.prepareStatement("insert into " + tableName + " values (?, ?)");) {
for (int i = 0; i < 3; i++) {
pstmt.setInt(1, i);
pstmt.setString(2, "test" + i);
pstmt.addBatch();
}
long start = System.currentTimeMillis();
pstmt.executeBatch();
conn.commit();
long end = System.currentTimeMillis();
System.out.println("Finished. Time taken : " + (end - start) + " milliseconds.");
}
conn.close();
}
} finally {
// nothing for this test
}
}
}
This program results in incorrect entries being inserted to the test table with last item of the batch being repeated and the first item of the batch missed out.
Expected behavior
Caching and connection pool shouldn't lead to incorrect insert to the table. Should see no duplicates and no missing rows.
Actual behavior
At the end of the stand alone program run, could see there are (2*j) - 1 = 9 rows instead of 5 rows for column c1 : 2 and (j-1) = 4 entries missing for column c1 : 0

Error message/stack trace
With FINER logging enabled for SQL driver , could see the following exception : SQLException: com.microsoft.sqlserver.jdbc.SQLServerException: Could not find prepared statement with handle

Any other details that can be helpful
This issue does not happen when I comment the call to sp_reset_connection in SQLServerPooledConnection.java

JDBC trace logs
SQL Profiler data

Driver version
com.microsoft.sqlserver mssql-jdbc 12.6.1.jre11SQL Server version
Client Operating System
Windows 10 Pro
JAVA/JVM version
Java 17
Problem description
There are some duplicate inserts and some missing entries when a custom connection pool is being utilized and server side statement pooling is enabled (using the setDisableStatementPooling flag). When the connection is re-used from the connection pool and the flag
setDisableStatementPooling is set to falsethis issue seems to happen.To test this instance, I was able to simulate a very basic stand alone as below :
Please see the following test program, where I intentionally return the previously created connection object from the _userToConnectionsMap cache map for subsequent runs.
This program results in incorrect entries being inserted to the test table with last item of the batch being repeated and the first item of the batch missed out.
Expected behavior
Caching and connection pool shouldn't lead to incorrect insert to the table. Should see no duplicates and no missing rows.
Actual behavior
At the end of the stand alone program run, could see there are (2*j) - 1 = 9 rows instead of 5 rows for column c1 : 2 and (j-1) = 4 entries missing for column c1 : 0
Error message/stack trace
With FINER logging enabled for SQL driver , could see the following exception :
SQLException: com.microsoft.sqlserver.jdbc.SQLServerException: Could not find prepared statement with handleAny other details that can be helpful
This issue does not happen when I comment the call to

sp_reset_connectionin SQLServerPooledConnection.javaJDBC trace logs
SQL Profiler data