准备好的语句不返回oracle XE数据库

时间:2015-06-09 21:30:37

标签: java oracle jdbc prepared-statement

我的JDBC PreparedStatement无效。我正在使用Oracle 11g Express Edition,Tomcat 7,Java 7,ojdbc7.jar在$ CATALINA_HOME / lib中。我正在开发的应用程序使用spring框架。但这是无关紧要的,因为我构建了一个简单的Java类来测试相同的PreparedStatement,但仍然没有结果。

如果我在sqlplus中运行查询,我会得到预期的结果。如果我在常规Statement中使用相同的查询,我会得到预期的结果。如果我在Spring中使用JdbcTemplate来使用我的硬编码值,我会得到结果。只是不在那个愚蠢的PreparedStatement

正如您将从下面的日志中看到的那样,我的参数将被插入JDBC中的PreparedStatement。跟踪文件显示该值在数据库中是绑定的,查询正在运行,但fetch不会返回任何内容。

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 net.sf.log4jdbc.PreparedStatementSpy@71449b35

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 net.sf.log4jdbc.ResultSetSpy@34460b79

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很想知道。谢谢。

1 个答案:

答案 0 :(得分: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' 

Oracle假定您的文字是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方法中,您在varchar列上强制char比较语义。您可以通过在调用setString之前将Java String对象填充为8个字符或将查询修改为trim char(8)列或rpad来解决此问题。参数传递给8个字符。但是这些选项都不是很令人满意 - 你要么需要编写一堆代码来确定列的长度,以及填充字符串的数量,或者你最终会得到多少存储在代码中的一堆列长度如果有人将来修改数据库以增加列的长度,则可能会过时。将数据库列更改为varchar2(8 char)并删除对char数据类型的任何引用都会更好。