9

我使用 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 中编写了 check:

// ... 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 有空字符。我可以删除这个单字节,但这将是一个相当奇怪的解决方法。

我在那里不使用 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();
}

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

我将使用我从你的答案中学到的一切。创建最简单的表:

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 中,而不是在第二个中:

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 Developer(在 Windows 上)或 Java(在 Red Hat EE 和 Tomcat7 上)中使用以下 SQL 时,我在所有返回的 CLOB 中的位置 4000 处得到空字符:

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

BR,JM

4

4 回答 4

7

这不是 Oracle 错误(它可以很好地存储和检索 \0。这是一个客户端/Windows 错误(不同的客户端在“NUL”方面的行为与 Windows 一样)

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>

然而蟾蜍在这方面惨败: 蟾蜍

如您所见,sql developer 的表现更好:

SQL 开发人员

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

replace(T1.PROJECT.getClobVal(), chr(0), null)在使用 sql developer/windows 剪贴板时,您应该解决这个问题。

于 2012-11-23T23:52:19.600 回答
3

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

如果读取为 blob,则另一种解决方案,则不会出现错误

T1.PROJECT.getBlobVal(nls_charset_id('UTF8'))
于 2014-02-18T15:32:47.190 回答
3

I also was experiencing this same issue exactly as described by Mikosz (seeing an extra 'NUL' character around the 4000th character when outputting my XMLType value as a Clob). While playing around in SQLDeveloper I noticed an interesting workaround. I was trying to see the output of my XMLType, but was tired of scrolling to the 4000th character, so I started wrapping the Clob output in a substr(...). Much to my surprise, the issue actually disappeared. I incorporated this into my Java app and confirmed that the issue was no longer present and my Clob could be retrieved without the extra character. I know that this isn't an ideal workaround, and I'm still not sure why it works (would love if someone could explain it to me), but here's an abbreviated example of what I've currently got working:

// 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"));
  ...
}
于 2013-02-21T17:31:07.307 回答
2

很容易验证它是否是.getClobVal()调用 - 在生成的 CLOB 上使用 PL/SQL(不是 Java)执行INSTR测试,以查看是否CHR(0)存在。

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

于 2012-11-27T00:59:18.177 回答