2012-11-23 254 views
8

我使用Oracle 11g(在Red Hat上)。我有XMLType列简单的常规表:奇怪的Oracle XMLType.getClobVal()结果

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的其余部分未被复制。

// ... 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)); 

阅读器返回完整的XML,但我会抛出异常,因为在4000位置空字符我可以删除此单个字节,但这将是相当:

为了证实这一点,我在java中写检查奇怪的解决方法。

我没有在那里使用VARCHAR2,但也许这个问题与VARCHAR2限制(4000字节)有关?任何其他想法?这是一个Oracle错误还是我错过了什么?

--------------------编辑--------------------

Value使用下面的存储过程中插入:习惯称它为

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(); 
} 

--------------------编辑。简单测试--------------------

我将使用我从你的答案中学到的所有东西。创建最简单的表格:

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 || '' || chr(0); 
    P2 := P2 || ''; 
    END LOOP; 
    P1 := P1 || '</a>'; 
    P2 := P2 || '</a>'; 

检查null是第一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))); 

它接缝,有内部数据库中没有空,只要我们在PL/SQL背景下,有没有空。但是,当我尝试使用SQL开发人员(在Windows上)以下SQL或Java中(在Red Hat EE和Tomcat7)我所有返回的CLOB得到空字符在4000位置:

select T.P.getClobVal() from T1 T; 

BR, JM

+2

没有错误,你可以把它写使用'utl_file',看看内容如何看起来像一个文件?你还可以尝试从PROJECTS中选择XMLType.getClobVal(PROJECT);'? (虽然没有功能上的差异) – Annjawn

+1

柱状体是如何填充的?你确定问题在于检索 - 如果不同的客户看到相同的事情听起来不太可能。您也可以选择值的子字符串,并查看是否仍然存在空字符。 –

+0

我运行utlfile.sql和prvtfile.plb,但我仍然无法使用utl_file(ORA-06521:PL/SQL:错误映射函数),抱歉。 – Mikosz

回答

6

它不是一个Oracle的bug(它存储和检索的\ 0就好了。它的客户端/ Windows漏洞(不同的客户行为不同的问候“NUL”一样窗)

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 Developer

,但如果你把它复制的剪贴板将只复制到nul字符。这个复制粘贴错误虽然不是SQL开发者的错误,但它与Windows剪贴板的问题不允许NUL正确粘贴。

你应该只是replace(T1.PROJECT.getClobVal(), chr(0), null)来解决这个问题,当使用SQL开发/ Windows剪贴板。

+0

谢谢你的回应。当然剪贴板是Windows的问题,但这不是主要问题,它只是测试它的一种方式。我想知道为什么null有:)。我认为这不是客户的问题。当我从此表下载单个单元格时(使用SQL Developer或Java中的Blob),不存在空字符。它只出现在我使用PROJECT.getClobVal()时(都在SQL Developer和Java中)。因此,对于我来说,getClobVal()在其结果的位置4000插入空字符。 – Mikosz

+2

不,它不会这样做(或不应该),如果是这样,在每一个LOB上,我建议你向Oracle提交一份SR。我在1MB以上的文件上广泛使用XMLType,并且从不丢弃chr(0)(10.2.0.4 + 11.2.0.2)。 – DazzaL

+0

谢谢。我也使用11.2.02(Express Edition)。我用最简单的测试编辑了我的帖子。似乎每个getClobVal()结果在PL/SQL之外传输时都会出现问题。 – Mikosz

1

很容易验证,如果它是.getClobVal()通话与否 - 在你得到CLOB执行PL/SQL(不是Java)的INSTR测试,看是否CHR(0)存在与否。

如果没有,那么我会指着你的Oracle客户端安装。

+0

谢谢。你是对的,在PL/SQL中没有null。当结果转移到外部时,它就会出现。这很奇怪,因为我使用两个不同的客户端(Windows上的SQL Developer和Red Hat上的Java)。 – Mikosz

+2

您可能患有JDBC错误;看到你的两个客户都是基于Java的。 – Ben

3

我也正在经历与Mikosz所描述的相同的问题(当输出我的XMLType值作为Clob时,在第4000个字符附近看到额外的'NUL'字符)。在SQLDeveloper中玩耍时,我注意到一个有趣的解决方法。我试图看到我的XMLType的输出,但厌倦了滚动到第4000个字符,所以我开始将Clob输出封装在substr(...)中。令我惊讶的是,这个问题实际上已经消失了。我将它加入到我的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")); 
    ... 
} 
+0

另外,值得一提的是,我们在几个地方使用了XMLType列。在我们注册模式(结构化XML)的地方,我没有看到这个问题。对于不使用注册模式的XMLType列,我们确实看到了这个问题。 – Mark

+0

非常感谢您的建议。它也解决了我的问题。这是我能找到合适解决方案的唯一地方。 –

3

错误:14781609 XDB:当XML存储在CLOB中时,XMLType.getclobval()返回临时LOB。 修复了补丁集11.2.0中的错误。4

和另一种解决方案 如果读成团块,然后像

T1.PROJECT.getBlobVal(nls_charset_id('UTF8'))