javajdbcprepared-statementdeadlockmssql-jdbc

PreparedStatement parameterization triggers deadlocks (JDBC, SQL Server 2019)


Background

I am working on a piece of enterprise software for which we've recently decided to move from building SQL queries via string concatenation to doing so by using prepared statements with parameters, so that this first example:

String query = "SELECT * FROM CARS WHERE MAKE = '" + getMake() 
             + "' AND MODEL = '" + getModel() + "'";
PreparedStatement preparedStatement = connection.prepareStatement(query);

becomes the second:

String query = "SELECT * FROM CARS WHERE MAKE = ? AND MODEL = ?";
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, getMake());
preparedStatement.setString(2, getModel());

For context, I am using SQL Server 2019 and JDBC.

Issue

With this change being the ONLY difference in the code, I now receive deadlocks that interrupt processing and I don't understand why. The environment is highly concurrent in that large batches of data are processed in multiple threads, with many of each of insert, delete, and select. What I don't understand is why this concurrency only becomes a problem when using templated statements, as opposed to concatenated ones.

So far I've tried:

What else can I try? Why in the world would the behavior between these approaches be so dramatic as to cause deadlocks? How can I fix them?

Thanks!


Solution

  • if you ask for reason of the dramatic difference in performance I can give at least this one: the type conversion.

    Java type of SQL parameters is Unicode String but on the database you will have something else like varchar with a specific collation. The conversion can cause that even if an index is defined for the table columns it will not be used.

    The concatenated String used before the code change didn't need conversion of types.

    Solution

    You can tell the driver to not send parameters as Unicode by adding to the connection url:

    sendStringParametersAsUnicode=false
    

    Hope it helps.