2013-02-15 48 views
2

我正試圖抓住與SQL Server 2008 R2數據庫交談的Java應用程序。應用程序將數據導入數據庫,並具有「測試模式」; DB請求被封裝在一個事務中,並在最後回滾。Java應用程序在調用PreparedStatement之後掛起(針對SQL Server數據庫)

對於特定的數據集,該工具會禁用觸發器,然後在導入後重新啓用它。在測試模式下,在第一遍時,一切都按預期工作 - 「導入」中的數據集沒有問題。但是,如果我嘗試重複練習,則應用會停止在試圖禁用觸發器的位置。

看着SQL事件探查器,我可以看到一個RPC:已完成的跟蹤項目,這表明SQL Server已收到併成功處理了請求。在這一點上,我期望Java應用程序能夠控制和繼續 - 除非它不這樣做,我正在努力思考接下來要看的東西。

Java代碼:

String sql = "ALTER TABLE MyTable DISABLE TRIGGER ALL"; 
PreparedStatement stmt = mDBConnection.prepareStatement (sql); 
stmt.execute(); 

跟蹤的TextData:

declare @p1 int 
set @p1=1 
exec sp_prepare @p1 output,N'',N'ALTER TABLE MyTable DISABLE TRIGGER ALL',1 
select @p1 

Q:任何想法,問題可能是什麼?或者關於我如何進一步調查的任何建議?

UPDATE: 當然,上面的跟蹤只顯示sp_prepare。有一個相應的sp_execute語句 - 並且缺少RPC:已完成的跟蹤項目,表示問題出現在SQL Server端。修改的跟蹤顯示RPC:啓動項('exec sp_execute 1'),但沒有匹配的RPC:已完成。

我可以在SSMS中運行sp_execute & sp_execute(提供我刪除set語句),如預期的那樣 - 它在第一遍中執行OK。

解決方案: 使用sp_who2(見下文),我可以看到,有第一連接/ SPID被阻斷的第二;在提交時,數據庫連接已關閉,但在回滾時不是。由於我在測試和回滾模式下運行,這是我的問題的關鍵 - 關閉連接解決了問題。

sp_who2:

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 
+0

當您使用SQL Server Management Studio執行它時會發生什麼? – 2013-02-15 17:17:01

+0

@MarkRotteveel - 不確定;有幾次它已經工作(@ sp = 2),但通常它不會(''找不到預處理語句1')。 – CJM 2013-02-15 17:27:26

+0

@MarkRotteveel - 如果我刪除了'set @ p1 = 1',語句在SSMS中執行OK。 – CJM 2013-02-18 09:40:51

回答

1

這聽起來很像你有沒有正確釋放的鎖,並阻止你的DDL執行。

當您的語句掛起時,運行存儲過程sp_who2

在該過程的結果中,您將會阻止您的DDL,然後您可以執行相應的操作。

1

不要使用這個PreparedStatement。只使用普通的Statement

Statement stmt = mDBConnection.createStatement(sql); 
+0

你能解釋一下爲什麼嗎?他們是解決這個問題的方法,還是更一般的建議? – CJM 2013-02-15 21:05:46

+0

這只是不好的建議(恕我直言) – 2013-02-15 23:07:36

+1

@CJM你不需要爲所有事情都使用'PreparedStatement'。對於很少運行的SQL語句,特別是對於沒有綁定變量的SQL語句而言,語句是很好的。定期陳述的存在是有原因的。我並不是說所有時間都使用「陳述」,但在這種情況下,這是正確的。 – mightyrick 2013-02-16 02:27:53

1

「ALTER TABLE」語句是DDL(數據定義語言)。 DDL必須等待所有DML(數據操作語言)語句完成。如果您有一個未關閉的ResultSet,Statement或PreparedStatement查詢該表或該表上的視圖或與該表的聯接,或者關閉了自動提交更新 - 那麼這就是DML不完整。

在像這樣更改表格之前,請確保在其上打開的每個可能的結果集都已顯式關閉,並且類似地聲明任何語句。這將確保所有的DML完成並且可以執行DDL。

一般來說,最好使用PreparedStatements而不是語句。 PreparedStatement被編譯一次。每次執行時聲明。這意味着像您這樣的非參數化報表沒有什麼區別,並且對於任何一次參數化都有潛在的好處。

假設一個可信的JDBC實現,當PreparedStatement沒有時,語句可能無法工作。

您還可能會發現this question有幫助。

+0

對於DDL使用PreparedStatement不會對常規Statement產生任何好處。該聲明不會被執行一次以上。對於只執行一次的語句,PreparedStatement實際上比常規語句慢*。 – 2013-02-15 23:27:32

+0

你怎麼知道它不會被執行多次?關閉觸發器可能是定期更新過程的一部分。如果它只執行一次,那麼任何額外的緩慢只是幾分之一毫秒。使用PreparedStatements是一種很好的做法,因爲它可以確保正確的數據類型映射和可重用性。如果你是初學者,PreparedStatements是一個更好的習慣。 – 2013-02-15 23:40:32

+1

@SimonG。告訴某人總是使用PreparedStatement是一個不好的建議。此外,供應商對JDBC驅動程序的實現並不是查詢緩存如何針對特定SQL語句行爲的唯一決定因素。許多DB基於若干特定因素智能地緩存(或不)SQL語句。 OP的查詢顯然不是一次又一次地執行。禁用觸發器不是普通的重複行爲。沒有理由對不常用的DDL使用PreparedStatement。 – mightyrick 2013-02-16 02:32:35