当前位置: 首页 > 知识库问答 >
问题:

Oracle12.2数据库中CLOB的JDBC流编码错误

湛鸿
2023-03-14
create table oracle_compatibility_test
(
    id           number(19)    not null primary key,
    name         varchar2(500) not null,
    name_as_clob clob          not null
);    

insert all
    into oracle_compatibility_test (id, name, name_as_clob) values (1, 'test1', 'test1')
    into oracle_compatibility_test (id, name, name_as_clob) values (2, 'test2äößrt', 'test2äößrt')
select *
    from dual;

commit;
package de.pie;

import java.io.IOException;
import java.io.Reader;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

public final class OracleCompatibilityTest {
    //A 12.2 Oracle Database
    private static final String DB1 = "jdbc:oracle:thin:@server1:1521";
    //A 12.1 Oracle Database
    private static final String DB2 = "jdbc:oracle:thin:@server2:1521";

    private static final String USED_DATABASE = DB1;

    public static void main(String[] args) {
        printDatabaseCharset();

        try (Connection connection = DriverManager.getConnection(USED_DATABASE, databaseProperties());
             Statement statement = connection.createStatement();
             ResultSet result = statement.executeQuery(
                     "select name, name_as_clob, convert(name_as_clob, 'AL32UTF8') as utf_8_clob " +
                             "from oracle_compatibility_test " +
                             "order by id asc")) {
            readRow(result);
            readRow(result);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void printDatabaseCharset() {
        try (Connection connection = DriverManager.getConnection(USED_DATABASE, databaseProperties());
             Statement statement = connection.createStatement();
             ResultSet result = statement.executeQuery(
                     "select * from database_properties where property_name = 'NLS_CHARACTERSET'")) {
            result.next();
            System.out.println("read charset from database: " + result.getString("property_value") + "\n");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static void readRow(ResultSet result) throws SQLException, IOException {
        result.next();

        String name = result.getString("name");
        String nameAsClob = result.getString("name_as_clob");
        Clob clobAsClobObject = result.getClob("name_as_clob");
        String clobObjectAsString = clobAsClobObject.getSubString(1, (int) clobAsClobObject.length());
        String clobReadAsCharacterStream = readFromCharacterStream(result);
        String utf8clob = result.getString("utf_8_clob");


        StringBuilder sb = new StringBuilder();
        sb.append("read name: ")
          .append(name)
          .append("\nname read as clob: ")
          .append(nameAsClob)
          .append("\nname read as clob-object: ")
          .append(clobObjectAsString)
          .append("\nclob read as character-stream: ")
          .append(clobReadAsCharacterStream)
          .append("\nclob converted to utf-8: ")
          .append(utf8clob)
          .append("\n\n\n");

        System.out.println(sb.toString());
    }

    private static String readFromCharacterStream(ResultSet result) throws SQLException, IOException {
        try (Reader reader = result.getCharacterStream("name_as_clob")) {
            StringBuilder stringBuilder = new StringBuilder();
            int c;
            while ((c = reader.read()) != -1) {
                stringBuilder.append((char) c);
            }
            return stringBuilder.toString();
        }
    }

    private static Properties databaseProperties() {
        Properties prop = new Properties();
        prop.put("user", "user");
        prop.put("password", "password");
        return prop;
    }
}
read charset from database: WE8ISO8859P15

read name: test1
name read as clob: test1
name read as clob-object: test1
clob read as character-stream: test1
clob converted to utf-8:  t e s t 1



read name: test2äößrt
name read as clob: test2äößrt
name read as clob-object: test2äößrt
clob read as character-stream: test2äößrt
clob converted to utf-8:  t e s t 2 ä ö ß r t

对于Oracle12.2,输出如下:

read charset from database: WE8ISO8859P15

read name: test1
name read as clob: test1
name read as clob-object: test1
clob read as character-stream: test1
clob converted to utf-8:  t e s t 1



read name: test2äößrt
name read as clob: test2���rt
name read as clob-object: test2���rt
clob read as character-stream: test2���rt
clob converted to utf-8:  t e s t 2 � � � r t

JDBC驱动程序错误地自动检测字符集为UTF-8,但流实际上是在ISO8859-15中。在JDBC8中不能显式设置字符集。从数据库返回的流是在Oracle 12.1下用UTF-8编码的

共有1个答案

王楚青
2023-03-14

我们发现了问题并找到了解决方案(这可能更像是一种变通方法)。

在Oracle12.1中,属性Oracle.jdbc.DefaultLobPrefetchSize默认设置为-1。在Oracle12.2下,这个值更改为4000,这意味着数据库将尝试在一个查询中获取所有内容,并将CLOB强制转换为VARCHAR2(如果它的大小在4000以下,请参见此处)。在使用字符集WE8ISO8859P15时,这在Oracle12.2中不起作用。

将此属性设置为-1将再次禁用预取(因此需要另一个数据库访问来获取CLOB列),并且一切正常。

 类似资料:
  • 谢谢你的时间 我得到了一个错误,因为我的项目有2个模块添加驱动程序和添加卡车,我正在为这两个模块执行sql查询,但当我为addDriver模块执行查询时,数据库异常正在抛出声明 driver insert sql语句public void insertData(driver-driver){String sql=“insert-INTO-driver”“(DLNo,DName,Age,Experi

  • 问题内容: 我正在使用JDBC和它的新功能。但我不断收到此运行时异常: 这是给定的代码 如何使此代码起作用?我只是JDBC的初学者。 对于上面的代码;PASS =“ passowrd”,USER =“ root” 我在通过此站点修复的端口上遇到了问题 非常感谢 问题答案: 请在mysql数据库中检查数据库名称“ kholofedb ”是否存在 我想你还没有创造 请检查一次(如果没有创建)及其相关表

  • 我发出一个请求,该请求以json utf-8(带元组的dict)响应 如果没有,我将得到一个UnicodeEncodeError,所以似乎有必要对字符串进行编码 我举一个例子: json中的字符串:'quäloan' 编码后的字符串:'qu\xC3\xA4loan' 在数据库中插入:'qu借出' 我的猜测是,数据库处理编码的utf-8字符串仍然是拉丁语-1,但是当我插入一些未编码的样本utf-8字

  • 主要内容:示例动态网页的主要特点就是能及时更新数据,这些更新的数据来源于数据库。学习本节内容需要您了解 JDBC,可以使用 JDBC 连接 MySQL 数据库。 本节数据库使用 MySQL 5.7,你可以点击 MySQL 官方网站 下载相应的 jar 包。 注意:MySQL 8.0 及之后的版本与之前的数据库连接有所不同: 首先驱动 com.mysql.jdbc.Driver 更换为 com.mysql.cj.

  • 主要内容:1. 导入JDBC包,2. 注册JDBC驱动程序,数据库URL配置,创建连接对象,使用具有用户名和密码的数据库URL,关闭JDBC连接安装相应的驱动程序后,现在是时候来学习使用JDBC建立数据库连接了。 建立JDBC连接所涉及的编程相当简单。 以下是基本的四个步骤 - 导入JDBC包:使用Java语言的语句在Java代码开头位置导入所需的类。 注册JDBC驱动程序:使JVM将所需的驱动程序实现加载到内存中,从而可以满足JDBC请求。 数据库URL配置:创建一个正确格式化的地址,指向要连

  • 问题内容: 我正在使用http://code.google.com/p/sqlite- jdbc/wiki/Introduction中 的SQLite驱动程序。 上述文档中显示的示例显示了如何连接现有数据库。 在我的应用程序中,我需要创建一个SQLite数据库。怎么做?创建具有扩展名的文件是否足够?还有一个叫做的函数。如果可以,如何使用?我用谷歌搜索,没有人给出明确的答案。 问题答案: 如果文件不