后台
我正在开发一个企业软件,我们最近决定从通过字符串连接构建SQL查询转向使用带参数的预处理语句,因此第一个示例:
String query = "SELECT * FROM CARS WHERE MAKE = '" + getMake()
+ "' AND MODEL = '" + getModel() + "'";
PreparedStatement preparedStatement = connection.prepareStatement(query);
成为第二个:
String query = "SELECT * FROM CARS WHERE MAKE = ? AND MODEL = ?";
PreparedStatement preparedStatement = connection.prepareStatement(query);
preparedStatement.setString(1, getMake());
preparedStatement.setString(2, getModel());
对于上下文,我使用SQL Server 2019和JDBC。
问题
由于这个变化是代码中唯一的区别,我现在收到了中断处理的死锁,我不明白为什么。该环境是高度并发的,因为大批量的数据在多个线程中处理,每个线程都有许多insert
、delete
和select
。我不明白的是,为什么这种并发性只在使用模板语句时才成为问题,而不是使用连接语句。
到目前为止,我已经尝试过:
- 将隔离级别降低到READ_UNCOMMITTED-只会导致delete语句锁定
- 创建唯一的PreparedStatement对象而不是重用一个对象-不会导致任何更改
- 询问ChatGPT,这表明它可能与SQL Server重用模板语句有关,因为模板字符串是相同的,而串联版本是唯一的,因为它们包括字符串中的变量。
- 使用
WITH (ROWLOCK)
,这似乎导致了本质上相同的情况,只有键锁而不是页面锁导致死锁。
我还能尝试什么?为什么这些方法之间的行为会如此戏剧性,以至于导致死锁?我该怎么解决?
谢谢!
1条答案
按热度按时间l7wslrjt1#
如果你问我为什么会有如此巨大的差异,我至少可以给予这样一个答案:类型转换。
Java类型的SQL参数是Unicode字符串,但在数据库上,您将使用其他东西,如具有特定排序规则的varchar。这种转换可能导致即使为表列定义了索引,也不会使用该索引。
在代码更改之前使用的串联String不需要类型转换。
解决方案
您可以通过在连接url中添加以下内容来告诉驱动程序不要将参数作为Unicode发送:
希望能帮上忙。