Java应用程序在调用PreparedStatement后挂起(针对SQL Server DB)

Java应用程序在调用PreparedStatement后挂起(针对SQL Server DB),java,sql-server-2008-r2,locking,prepared-statement,jtds,Java,Sql Server 2008 R2,Locking,Prepared Statement,Jtds,我正在尝试使用与SQLServer2008R2数据库对话的Java应用程序。应用程序将数据导入数据库,并具有“测试模式”;DB请求被封装在一个事务中,该事务在最后回滚 对于特定的数据集,该工具禁用触发器,然后在导入后重新启用它。在测试模式下,在第一次通过时,一切都按预期工作-数据集处于“导入”状态,没有问题。但是,如果我尝试重复此练习,应用程序将挂起在尝试禁用触发器的位置 查看SQL Profiler,我可以看到一个RPC:Completed跟踪项,这表明SQL Server已经接收并成功处理了

我正在尝试使用与SQLServer2008R2数据库对话的Java应用程序。应用程序将数据导入数据库,并具有“测试模式”;DB请求被封装在一个事务中,该事务在最后回滚

对于特定的数据集,该工具禁用触发器,然后在导入后重新启用它。在测试模式下,在第一次通过时,一切都按预期工作-数据集处于“导入”状态,没有问题。但是,如果我尝试重复此练习,应用程序将挂起在尝试禁用触发器的位置

查看SQL Profiler,我可以看到一个RPC:Completed跟踪项,这表明SQL Server已经接收并成功处理了请求。在这一点上,我希望Java应用程序能够获得控制权并继续运行——但事实并非如此,我正在努力思考下一步该往哪里看

Java代码:

String sql = "ALTER TABLE MyTable DISABLE TRIGGER ALL";
PreparedStatement stmt = mDBConnection.prepareStatement (sql);
stmt.execute();
declare @p1 int
set @p1=1
exec sp_prepare @p1 output,N'',N'ALTER TABLE MyTable DISABLE TRIGGER ALL',1
select @p1
CREATE TABLE #sp_who2 
(
   SPID INT,  
   Status VARCHAR(1000) NULL,  
   Login SYSNAME NULL,  
   HostName SYSNAME NULL,  
   BlkBy SYSNAME NULL,  
   DBName SYSNAME NULL,  
   Command VARCHAR(1000) NULL,  
   CPUTime INT NULL,  
   DiskIO INT NULL,  
   LastBatch VARCHAR(1000) NULL,  
   ProgramName VARCHAR(1000) NULL,  
   SPID2 INT,
   RequestID int
) 
GO

INSERT INTO #sp_who2 EXEC sp_who2
GO

SELECT spid, status, blkby, command, ProgramName FROM #sp_who2 WHERE DBName = 'rio7_bch_test'
GO

DROP TABLE #sp_who2
GO
跟踪文本数据:

String sql = "ALTER TABLE MyTable DISABLE TRIGGER ALL";
PreparedStatement stmt = mDBConnection.prepareStatement (sql);
stmt.execute();
declare @p1 int
set @p1=1
exec sp_prepare @p1 output,N'',N'ALTER TABLE MyTable DISABLE TRIGGER ALL',1
select @p1
CREATE TABLE #sp_who2 
(
   SPID INT,  
   Status VARCHAR(1000) NULL,  
   Login SYSNAME NULL,  
   HostName SYSNAME NULL,  
   BlkBy SYSNAME NULL,  
   DBName SYSNAME NULL,  
   Command VARCHAR(1000) NULL,  
   CPUTime INT NULL,  
   DiskIO INT NULL,  
   LastBatch VARCHAR(1000) NULL,  
   ProgramName VARCHAR(1000) NULL,  
   SPID2 INT,
   RequestID int
) 
GO

INSERT INTO #sp_who2 EXEC sp_who2
GO

SELECT spid, status, blkby, command, ProgramName FROM #sp_who2 WHERE DBName = 'rio7_bch_test'
GO

DROP TABLE #sp_who2
GO
Q:知道问题出在哪里吗?或者对我如何进一步调查有什么建议

更新: 当然,上面的跟踪只显示sp_prepare。有一个对应的sp_execute语句,并且缺少RPC:Completed跟踪项,表明问题出在SQL Server端。修改后的跟踪显示RPC:Starting条目('exec sp_execute 1'),但没有匹配的RPC:Completed

我可以按预期在SSMS中运行sp_prepare&sp_execute(只要我删除set语句),毕竟它在第一次通过时执行OK

解决方案: 使用sp_who2(见下文),我可以看到第一个连接/spid在第二个连接被阻塞;提交时,db连接已关闭,但回滚时未关闭。因为我是在测试和回滚模式下运行的,所以这是我问题的症结所在——关闭连接解决了问题

sp_who2:

String sql = "ALTER TABLE MyTable DISABLE TRIGGER ALL";
PreparedStatement stmt = mDBConnection.prepareStatement (sql);
stmt.execute();
declare @p1 int
set @p1=1
exec sp_prepare @p1 output,N'',N'ALTER TABLE MyTable DISABLE TRIGGER ALL',1
select @p1
CREATE TABLE #sp_who2 
(
   SPID INT,  
   Status VARCHAR(1000) NULL,  
   Login SYSNAME NULL,  
   HostName SYSNAME NULL,  
   BlkBy SYSNAME NULL,  
   DBName SYSNAME NULL,  
   Command VARCHAR(1000) NULL,  
   CPUTime INT NULL,  
   DiskIO INT NULL,  
   LastBatch VARCHAR(1000) NULL,  
   ProgramName VARCHAR(1000) NULL,  
   SPID2 INT,
   RequestID int
) 
GO

INSERT INTO #sp_who2 EXEC sp_who2
GO

SELECT spid, status, blkby, command, ProgramName FROM #sp_who2 WHERE DBName = 'rio7_bch_test'
GO

DROP TABLE #sp_who2
GO

不要为此使用
PreparedStatement
。只需使用一个普通的
语句

Statement stmt = mDBConnection.createStatement(sql);
“ALTERTABLE”语句是DDL(数据定义语言)。DDL必须等待所有DML(数据操作语言)语句完成。如果您有一个未关闭的ResultSet、语句或PreparedStatement正在查询该表或该表上的视图,或与该表的联接,或在关闭自动提交的情况下进行更新,那么这就是未完成的DML

在像这样修改表之前,请确保在表上打开的所有可能的结果集都已显式关闭,类似地,任何语句都已关闭。这将确保所有DML都已完成,并且可以执行DDL

一般来说,最好使用准备好的报表而不是报表。PreparedStatement只编译一次。每次执行一条语句。这意味着像您这样的未参数化语句没有区别,任何参数化语句都有潜在的好处

假设一个受信任的JDBC实现,当PreparedStatement不起作用时,语句就没有时间起作用了


您可能也会发现有帮助。

这听起来很像您的锁没有被正确释放并阻止了DDL的执行

语句挂起时,运行存储过程
sp\u who2


在该过程的结果中,您将知道哪个会话正在阻止DDL,然后您可以采取适当的操作。

使用SQL Server Management Studio执行时会发生什么情况?@markrottevel-不确定;有几次它可以正常工作(@sp=2),但通常不能正常工作('not find prepared statement with handle 1')@markrotVeel-如果我删除
set@p1=1
,该语句在SSMS中执行正常。您能解释一下原因吗?它们是这个问题的解决方案,还是更一般的建议?这只是坏建议(IMHO)@CJM您不需要对所有事情都使用
PreparedStatement
。对于很少运行的SQL语句,特别是对于没有绑定变量的SQL语句,语句是很好的。正则语句的存在是有原因的。我不是说要一直使用
语句
,但在这种情况下,这是正确的做法。@CJM如果在使用语句后没有看到任何更改,请检查是否正在提交。如果您的JDBC连接设置为
autoCommit(false)
,或者默认情况下连接设置为那样,那么您需要显式地调用连接对象上的
commit()
——在对语句调用
execute()
之后。@RickGrashel-是的,我们在事务内部运行,然后执行(或回滚),但这是学术性的-我们没有从SQL得到响应,没有RPC:Completed,因此控制不会返回到java。使用PreparedStatement进行DDL与常规语句相比没有任何优势。该语句不会执行多次。对于只执行一次的语句,PreparedStatement实际上比常规语句慢。您怎么知道它不会执行多次呢?关闭触发器可能是常规更新过程的一部分。如果它只执行一次,那么任何额外的慢度仅为毫秒的几分之一。使用PreparedStatements是一种很好的做法,因为它可以确保正确的数据类型映射和可重用性。如果你是初学者,事先准备好的陈述是一个更好的习惯。@SimonG。告诉某人总是使用事先准备好的声明是一个糟糕的建议。此外,供应商对JDBC驱动程序的实现并不是查询缓存在特定SQL语句中的行为的唯一决定因素。许多数据库基于几个特定因素智能地缓存(或不缓存)SQL语句。OP的查询,就其性质而言,显然不是一次又一次地执行的。禁用触发器不是普通的重复行为。对于不经常使用的DDL,没有理由使用PreparedStatement。对不起,当然没有。我相信你的问题就像我说的那样