准备好的语句不从 oracle XE 数据库返回任何结果
Prepared statement returning no results from oracle XE database
我的 JDBC PreparedStatement
不工作。我使用的是 Oracle 11g 快捷版,Tomcat7,Java7,ojdbc7.jar 在 $CATALINA_HOME/lib 中。我正在开发的应用程序使用 spring 框架。但这无关紧要,因为我构建了一个简单的 Java class 来测试相同的 PreparedStatement
,但仍然没有结果。
如果我 运行 在 sqlplus 中查询,我会得到预期的结果。如果我在常规 Statement
中使用相同的查询,我会得到预期的结果。如果我在 Spring 中破解 JdbcTemplate
以使用我的硬编码值,我会得到结果。只是不是那个该死的 PreparedStatement
.
正如您将从下面的日志中看到的,我的参数被插入到 JDBC 中的 PreparedStatement
中。跟踪文件显示该值在数据库中绑定,查询是 运行ning,但 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!
,跟踪文件再次显示查询 运行,但没有返回任何结果。常规语句 returns
Go! Execute! Work! jibbyj No!
哪个是对的。
如果有人知道为什么 JDBC PreparedStatement 不能在我们的 oracle 数据库上运行,我和我的 dba 很想知道。谢谢。
这就是 char
数据类型是邪恶的并且应该被驱逐的原因 #47。任何人如果创建 char(8)
列来存储不总是恰好 8 个字符的字符串,都应该被判处调试各种令人抓狂的问题,直到他们发现自己的方法有误为止。
在数据库中,一个char(8 char)
总是消耗相当于space的8个字符。如果您存储的数据实际上不是 8 个字符长,则数据库必须用 space 填充它到 8 个字符。因此,如果您的实际数据长度为 6 个字符——"jibbyj"——数据库必须在末尾添加两个额外的 space。大约有 0 次这对您有任何好处——您无缘无故地承担了存储两个额外字节数据的额外费用。如果您使用 varchar2(8 char)
代替,您的 6 个字符的字符串实际上会按照您的预期存储,而无需额外的 spaces.
当你去查询char
列中的数据时,你必须非常小心你使用的是char还是varchar比较语义。如果您的查询包含硬编码文字
SELECT *
FROM your_table
WHERE char_column = 'jibbyj'
Oracle 假定您的文字是 char
并在进行比较之前对它进行 space 填充。所以它实际上在 char_column
中搜索值 "jibbyj ",最后有两个 space。当它找到该值时,它 returns 数据和一切正常。
另一方面,如果您尝试使用 varchar2
(或 varchar
),则 Oracle 使用 varchar
比较语义。发生这种情况时,存储在 table 中的两个额外 space 将被视为数据的一部分,并且您正在搜索的字符串必须完全匹配。
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
方法中,您在 char
列上强制使用 varchar
比较语义。您可以通过在调用 setString
之前将 Java 字符串对象填充到 8 个字符或将查询修改为 trim
char(8)
列或 rpad
传入的参数为8个字符。但是这些选项中的 none 非常令人满意——您要么需要编写一堆代码来确定列的长度,从而确定要填充多少字符串,要么最终得到如果将来有人修改数据库以增加列的长度,则存储在您的代码中的一堆列长度可能会过时。将数据库列更改为 varchar2(8 char)
并删除对 char
数据类型的任何引用会好得多。
我的 JDBC PreparedStatement
不工作。我使用的是 Oracle 11g 快捷版,Tomcat7,Java7,ojdbc7.jar 在 $CATALINA_HOME/lib 中。我正在开发的应用程序使用 spring 框架。但这无关紧要,因为我构建了一个简单的 Java class 来测试相同的 PreparedStatement
,但仍然没有结果。
如果我 运行 在 sqlplus 中查询,我会得到预期的结果。如果我在常规 Statement
中使用相同的查询,我会得到预期的结果。如果我在 Spring 中破解 JdbcTemplate
以使用我的硬编码值,我会得到结果。只是不是那个该死的 PreparedStatement
.
正如您将从下面的日志中看到的,我的参数被插入到 JDBC 中的 PreparedStatement
中。跟踪文件显示该值在数据库中绑定,查询是 运行ning,但 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!
,跟踪文件再次显示查询 运行,但没有返回任何结果。常规语句 returns
Go! Execute! Work! jibbyj No!
哪个是对的。
如果有人知道为什么 JDBC PreparedStatement 不能在我们的 oracle 数据库上运行,我和我的 dba 很想知道。谢谢。
这就是 char
数据类型是邪恶的并且应该被驱逐的原因 #47。任何人如果创建 char(8)
列来存储不总是恰好 8 个字符的字符串,都应该被判处调试各种令人抓狂的问题,直到他们发现自己的方法有误为止。
在数据库中,一个char(8 char)
总是消耗相当于space的8个字符。如果您存储的数据实际上不是 8 个字符长,则数据库必须用 space 填充它到 8 个字符。因此,如果您的实际数据长度为 6 个字符——"jibbyj"——数据库必须在末尾添加两个额外的 space。大约有 0 次这对您有任何好处——您无缘无故地承担了存储两个额外字节数据的额外费用。如果您使用 varchar2(8 char)
代替,您的 6 个字符的字符串实际上会按照您的预期存储,而无需额外的 spaces.
当你去查询char
列中的数据时,你必须非常小心你使用的是char还是varchar比较语义。如果您的查询包含硬编码文字
SELECT *
FROM your_table
WHERE char_column = 'jibbyj'
Oracle 假定您的文字是 char
并在进行比较之前对它进行 space 填充。所以它实际上在 char_column
中搜索值 "jibbyj ",最后有两个 space。当它找到该值时,它 returns 数据和一切正常。
另一方面,如果您尝试使用 varchar2
(或 varchar
),则 Oracle 使用 varchar
比较语义。发生这种情况时,存储在 table 中的两个额外 space 将被视为数据的一部分,并且您正在搜索的字符串必须完全匹配。
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
方法中,您在 char
列上强制使用 varchar
比较语义。您可以通过在调用 setString
之前将 Java 字符串对象填充到 8 个字符或将查询修改为 trim
char(8)
列或 rpad
传入的参数为8个字符。但是这些选项中的 none 非常令人满意——您要么需要编写一堆代码来确定列的长度,从而确定要填充多少字符串,要么最终得到如果将来有人修改数据库以增加列的长度,则存储在您的代码中的一堆列长度可能会过时。将数据库列更改为 varchar2(8 char)
并删除对 char
数据类型的任何引用会好得多。