我使用Oracle 11g(在Red Hat上)。我有XMLType列的简单常规表:
CREATE TABLE PROJECTS
(
PROJECT_ID NUMBER(*, 0) NOT NULL,
PROJECT SYS.XMLTYPE,
);
使用Oracle SQL Developer(在Windows上)我这样做:
select T1.PROJECT P1 from PROJECTS T1 where PROJECT_ID = '161';
有效。我得到一个牢房。我可以双击并下载整个XML文件。
然后我尝试将结果作为CLOB:
select T1.PROJECT.getClobVal() P1 from PROJECTS T1 where PROJECT_ID = '161';
有效。我得到一个牢房。我可以双击并查看全文并复制它。但有一个问题。当我将它复制到剪贴板时,我只得到前4000个字符。似乎位置4000处有0x00字符,而CLOB的其余部分未被复制。
为了证实这一点,我在java中写了支票:
// ... create projectsStatement
Reader reader = projectsStatement.getResultSet().getCharacterStream( "P1" );
BufferedReader bf = new BufferedReader( reader );
char buffer[] = new char[ 1024 ];
int count = 0;
int globalPos = 0;
while ( ( count = bf.read( buffer, 0, buffer.length ) ) > 0 )
for ( int i = 0; i < count; i++, globalPos++ )
if ( buffer[ i ] == 0 )
throw new Exception( "ZERO at " + Integer.toString(globalPos) );
Reader返回完整的XML,但我的异常被抛出,因为位置4000处有空字符。我可以删除这个单字节,但这将是一个相当奇怪的解决方法。
我不在那里使用VARCHAR2但是这个问题可能与VARCHAR2限制(4000字节)有关吗?还有其他想法吗?这是Oracle的错误还是我错过了什么?
--------------------编辑--------------------
使用以下存储过程插入值:
create or replace
procedure addProject( projectId number, projectXml clob ) is
sqlstr varchar2(2000);
begin
sqlstr := 'insert into projects ( PROJECT_ID, PROJECT ) VALUES ( :projectId, :projectData )';
execute immediate sqlstr using projectId, XMLTYPE(projectXml);
end;
用于调用它的Java代码:
try ( CallableStatement cs = connection.prepareCall("{call addProject(?,?)}") )
{
cs.setInt( "projectId", projectId );
cs.setCharacterStream( "projectXml", new StringReader(xmlStr) , xmlStr.length() );
cs.execute();
}
--------------------编辑。 SIMPLE TEST --------------------
我会用你从答案中学到的一切。创建最简单的表:
create table T1 ( P XMLTYPE );
使用XML准备两个CLOB。首先是空字符,第二个没有。
declare
P1 clob;
P2 clob;
P3 clob;
begin
P1 := '<a>';
P2 := '<a>';
FOR i IN 1..1000 LOOP
P1 := P1 || '0123456789' || chr(0);
P2 := P2 || '0123456789';
END LOOP;
P1 := P1 || '</a>';
P2 := P2 || '</a>';
检查null是否在第一个CLOB中而不在第二个CLOB中:
DBMS_OUTPUT.put_line( DBMS_LOB.INSTR( P1, chr(0) ) );
DBMS_OUTPUT.put_line( DBMS_LOB.INSTR( P2, chr(0) ) );
我们会按预期得到:
14
0
尝试将第一个CLOB插入XMLTYPE。不起作用。无法插入此值:
insert into T1 ( P ) values ( XMLTYPE( P1 ) );
尝试将第二个CLOB插入XMLTYPE。它会起作用:
insert into T1 ( P ) values ( XMLTYPE( P2 ) );
尝试将插入的XML读入第三个CLOB。它会起作用:
select T.P.getClobVal() into P3 from T1 T where rownum = 1;
检查是否有空。没有空:
DBMS_OUTPUT.put_line( DBMS_LOB.INSTR( P3, chr(0) ) );
它认为数据库中没有null,只要我们在PL / SQL上下文中,就没有null。但是当我尝试在SQL Developer(在Windows上)或在Java中(在Red Hat EE和Tomcat7上)使用以下SQL时,我在所有返回的CLOB中的位置4000处获得空字符:
select T.P.getClobVal() from T1 T;
BR, JM
答案 0 :(得分:6)
它不是一个Oracle错误(它存储和检索\ 0就好了。它是一个客户端/窗口错误(不同的客户端在&#34; NUL和#34;以及窗口方面的行为方式不同)
chr(0)实际上不是非blob中的有效字符(我很好奇你是如何让XMLType首先接受它,因为它通常不会解析)。
\ 0在C中用于表示字符串的结尾(NUL终止符),并且某些GUI将在该点停止处理字符串。例如:
![SQL> select 'IM VISIBLE'||chr(0)||'BUT IM INVISIBLE'
2 from dual
3 /
'IMVISIBLE'||CHR(0)||'BUTIM
---------------------------
IM VISIBLE BUT IM INVISIBLE
SQL>
然而toad在这方面失败了:
sql developer更好,你可以看到它:
但是如果你复制它,剪贴板只会将其复制到零字符。这个复制粘贴错误并不是SQL开发人员的错,但是它的问题是Windows剪贴板不允许NUL正确粘贴。
使用sql developer / windows clipboard时,你应该replace(T1.PROJECT.getClobVal(), chr(0), null)
来解决这个问题。
答案 1 :(得分:3)
我也遇到了与Mikosz完全相同的问题(在输出我的XMLType值作为Clob时,在第4000个字符周围看到一个额外的'NUL'字符)。在SQLDeveloper中玩游戏时,我注意到了一个有趣的解决方法。我试图看到我的XMLType的输出,但厌倦了滚动到第4000个字符,所以我开始在一个substr(...)中包装Clob输出。令我惊讶的是,这个问题实际上已经消失了。我把它合并到我的Java应用程序中并确认问题不再存在,并且可以在没有额外字符的情况下检索我的Clob。我知道这不是一个理想的解决方法,而且我仍然不确定它为什么会起作用(如果有人可以向我解释的话会很乐意),但这里是我目前工作的缩写示例:
// Gets the xml contents
String sql = "select substr(x.xml_content.getClobVal(), 0) as xml_content from my_table x";
ps = con.prepareStatement(sql);
if(rs.next()) {
Reader reader = new BufferedReader(rs.getCharacterStream("xml_content"));
...
}
答案 2 :(得分:3)
错误:14781609 XDB:当XML存储在CLOB中时,XMLType.getclobval()返回临时LOB。 修复补丁集11.2.0.4
和另一种解决方案 如果读为blob,则没有错误,如
T1.PROJECT.getBlobVal(nls_charset_id('UTF8'))
答案 3 :(得分:1)
很容易验证是否是.getClobVal()
调用 - 在结果CLOB上执行PL / SQL(非Java)中的INSTR
测试,以查看CHR(0)
是否存在或不。
如果没有,那么我会指责你的Oracle客户端安装。