2015-06-09 47 views
4

我的JDBC PreparedStatement不起作用。我正在使用Oracle 11g Express Edition,Tomcat 7,Java 7,ojdbc7.jar位於$ CATALINA_HOME/lib中。我正在開發的應用程序使用spring框架。但是這並不重要,因爲我構建了一個簡單的Java類來測試相同的PreparedStatement,但仍然沒有結果。準備好的語句不會返回Oracle XE數據庫的結果

如果我在sqlplus中運行查詢,我會得到預期的結果。如果我在正常的Statement中使用相同的查詢,我會得到預期的結果。如果我在Spring中使用我的硬編碼值JdbcTemplate,我會得到結果。只是沒有在那個darned PreparedStatement

正如您從下面的日誌中看到的,我的參數正在插入到JDBC中的PreparedStatement中。跟蹤文件顯示該值在數據庫中綁定,查詢正在運行,但是取回不會返回任何內容。

的log4jdbc日誌顯示我:

Jun 09, 2015 1:05:35 PM net.sf.log4jdbc.Slf4jSpyLogDelegator methodReturned 
INFO: 3. Connection.prepareStatement(select distinct staff_id from OE_ROLES where staff_id = ?) returned [email protected] 

Jun 09, 2015 1:05:35 PM org.springframework.jdbc.core.StatementCreatorUtils setParameterValueInternal 
FINEST: Setting SQL statement parameter value: column index 1, parameter value [jibbyj], value class [java.lang.String], SQL type unknown 

Jun 09, 2015 1:05:35 PM net.sf.log4jdbc.Slf4jSpyLogDelegator methodReturned 
INFO: 3. PreparedStatement.setString(1, "jibbyj") returned 

Jun 09, 2015 1:05:35 PM net.sf.log4jdbc.Slf4jSpyLogDelegator sqlOccured 
INFO: select distinct staff_id from OE_ROLES where staff_id = 'jibbyj' 

Jun 09, 2015 1:05:35 PM net.sf.log4jdbc.Slf4jSpyLogDelegator sqlTimingOccured 
INFO: select distinct staff_id from OE_ROLES where staff_id = 'jibbyj' 
{executed in 2 msec} 

Jun 09, 2015 1:05:35 PM net.sf.log4jdbc.Slf4jSpyLogDelegator methodReturned 
INFO: 3. ResultSet.new ResultSet returned 

Jun 09, 2015 1:05:35 PM net.sf.log4jdbc.Slf4jSpyLogDelegator methodReturned 
INFO: 3. PreparedStatement.executeQuery() returned [email protected] 

Jun 09, 2015 1:05:35 PM net.sf.log4jdbc.Slf4jSpyLogDelegator methodReturned 
INFO: 3. ResultSet.next() returned false 

這是從跟蹤文件:

PARSING IN CURSOR #140603768927480 len=59 dep=0 uid=52 oct=3 lid=52 tim=1433880335336621 hv=1464048059 ad='87cfc090' sqlid='6hbrj2tbn76dv' 
select distinct staff_id from OE_ROLES where staff_id = :1 
END OF STMT 
PARSE #140603768927480:c=0,e=124,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=4279656581,tim=1433880335336604 
BINDS #140603768927480: 
Bind#0 
    oacdty=01 mxl=32(24) mxlc=00 mal=00 scl=00 pre=00 
    oacflg=03 fl2=1000010 frm=01 csi=873 siz=32 off=0 
    kxsbbbfp=7fe0ddb35b88 bln=32 avl=06 flg=05 
    value="jibbyj" 
EXEC#140603768927480:c=0,e=87,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=1,plh=4279656581,tim=1433880335336761 
WAIT #140603768927480: nam='SQL*Net message to client' ela= 6 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1433880335336794 
FETCH #140603768927480:c=0,e=37,p=0,cr=1,cu=0,mis=0,r=0,dep=0,og=1,plh=4279656581,tim=1433880335336853 
STAT #140603768927480 id=1 cnt=0 pid=0 pos=1 obj=0 op='SORT UNIQUE NOSORT (cr=1 pr=0 pw=0 time=46 us cost=2 size=9 card=1)' 
STAT #140603768927480 id=2 cnt=0 pid=1 pos=1 obj=24702 op='INDEX RANGE SCAN AI_OE_ROLES_3 (cr=1 pr=0 pw=0 time=34 us cost=1 size=36 card=4)' 
WAIT #140603768927480: nam='SQL*Net message from client' ela= 16956 driver id=1413697536 #bytes=1 p3=0 obj#=-1 tim=1433880335353990 
CLOSE #140603768927480:c=0,e=22,dep=0,type=0,tim=1433880335354080 
XCTEND rlbk=0, rd_only=1, tim=1433880335354131 

這是從輸出通過TKPROF運行跟蹤文件後:

SQL ID: 6hbrj2tbn76dv Plan Hash: 4279656581 

select distinct staff_id 
from 
OE_ROLES where staff_id = :1 


call  count  cpu elapsed  disk  query current  rows 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
Parse  1  0.00  0.00   0   0   0   0 
Execute  1  0.00  0.00   0   0   0   0 
Fetch  1  0.00  0.00   0   1   0   0 
------- ------ -------- ---------- ---------- ---------- ---------- ---------- 
total  3  0.00  0.00   0   1   0   0 

Misses in library cache during parse: 0 
Optimizer mode: ALL_ROWS 
Parsing user id: 52 
Number of plan statistics captured: 1 

我用POJO試過,還沒有結果

public static void main(String[] args) { 
     String url = "jdbc:oracle:thin:@oracle-test.company.com:1521:XE"; 
     String user = "schema-owner"; 
     String passwd = "password"; 
     System.out.println("Go!"); 
     try(Connection conn = DriverManager.getConnection(url, user, passwd)){ 
      String pQuery = "select distinct staff_id from OE_ROLES where staff_id = ?"; 
      PreparedStatement pstmt = conn.prepareStatement(pQuery); 
      pstmt.setString(1, "jibbyj"); 
      ResultSet rs = pstmt.executeQuery(); 
      System.out.println("Execute!"); 
      while (rs.next()){ 
       System.out.println("Work!"); 
       System.out.println(rs.getString(1)); 
      } 
     } catch (Exception E) {System.out.println(E.getMessage());} 

     System.out.println("No!"); 

    } 

輸出結果爲:Go! Execute! No!,並且跟蹤文件再次顯示查詢已運行,​​但未返回任何結果。常規聲明返回

Go! Execute! Work! jibbyj No! 

這是正確的。

如果有人知道爲什麼JDBC PreparedStatement不能在我們的oracle數據庫上工作,我和我的dba很想知道。謝謝。

+0

'staff_id'的數據類型是什麼?什麼是有效的查詢?你有被'char'和'varchar2'語義咬住的機會嗎?什麼是'AI_OE_ROLES_3'索引?工作查詢是否使用不同的查詢計劃? –

+0

'ojdbc7.jar'用於Oracle 12c;你可以試試11g的官方驅動程序,['ojdbc6.jar'](http://www.oracle.com/technetwork/apps-tech/jdbc-112010-090769.html)?我會假設新的驅動程序是向後兼容的,但仍然可能至少值得一試。 –

+0

@Justin Cave:staff_id是CHAR(8),這個查詢的作用是「從OE_ROLES中選擇不同的staff_id,其中staff_id ='jibbyj'」,我不知道其他三個問題的答案,但會問我的dba 。 – Jibbyj

回答

6

這是理由#47 char數據類型是邪惡的,應該被放逐。創建char(8)列的任何人將存儲不總是正好8個字符的字符串,應該被判處調試各種令人生厭的問題,直到他們看到他們的方式錯誤。

在數據庫中,char(8 char)將總是消耗8個字符的空間。如果您要存儲的數據實際上不是8個字符,則數據庫必須使用空格填充8個字符。因此,如果您的實際數據長度爲6個字符 - 「jibbyj」 - 數據庫必須在最後添加兩個額外的空格。大約有0個實例會對您有任何好處 - 您將無緣無故地額外存儲兩個字節的數據。如果您使用的是varchar2(8 char),則您的6字符字符串實際上將按照您的預期存儲,而沒有額外的空格。

當您去查詢char列中的數據時,必須非常小心您是使用char或varchar比較語義。如果您的查詢包含一個硬編碼的文字

SELECT * 
    FROM your_table 
WHERE char_column = 'jibbyj' 

甲骨文假定您的文字是一個char和空間墊它在進行比較之前。所以它實際上搜索char_column的值爲「jibbyj」,最後有兩個空格。當它找到該值時,它將返回數據並且一切正常。

另一方面,如果您嘗試使用varchar2(或varchar),則Oracle使用varchar比較語義。發生這種情況時,存儲在表格中的兩個額外空間將被視爲數據的一部分,並且您要搜索的字符串必須完全匹配。

DECLARE 
    l_str_wo_spaces VARCHAR2(8) := 'jibbyj'; 
    l_str_w_spaces VARCHAR2(8) := 'jibbyj '; 
    l_cnt   INTEGER; 
BEGIN 
    -- This will find no rows 
    SELECT COUNT(*) 
    INTO l_cnt 
    FROM your_table 
    WHERE char_column = l_str_wo_spaces; 
    dbms_output.put_line(l_cnt); 

    -- This will find a row because it has the extra spaces 
    SELECT COUNT(*) 
    INTO l_cnt 
    FROM your_table 
    WHERE char_column = l_str_w_spaces; 
    dbms_output.put_line(l_cnt); 
END; 

在你PreparedStatement的方法,你這是在強迫在charvarchar比較語義。在調用setString或將查詢修改爲trimchar(8)列或rpad參數被傳遞到8個字符之前,可以通過填充Java字符串對象至8個字符來解決此問題。但是,這些選項都不是非常令人滿意的 - 你需要編寫一堆代碼來確定列的長度,因此,填充字符串的次數或者最終得到一堆列如果有人在將來修改數據庫以增加列的長度,那麼存儲在代碼中的長度可能會過時。將數據庫列更改爲varchar2(8 char)並刪除任何對char數據類型的引用會更好。

+0

根據SQL標準,當數據庫比較字符串(也是CHAR <> VARCHAR)時,最短的字符串應該用最長的長度填充空格。因此,Oracle的行爲似乎是錯誤的。 –

+0

@MarkRotteveel - 你有參考嗎?當比較兩個'varchar'值時,標準似乎不太可能需要空白填充語義 - 「foo」不同於具有三個尾隨空白的「foo」(如果兩者都在「varchar(6)」中)。當您比較兩個「char」值時,空白填充語義是有意義的。我可以看到,也許SQL標準規定,當比較'charchar'到'varchar'空白填充語義時,Oracle已經以其他方式實現了隱式轉換。但我沒有看標準。 –

+0

我需要再看一遍。它在SQL:2011中的比較規則中。 varchar - varchar比較可能會有所不同,但您的答案基於char - varchar比較。 –