首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >使用JDBC调用带有自定义类型输入参数的PL/SQL存储过程,所有字段都为空

使用JDBC调用带有自定义类型输入参数的PL/SQL存储过程,所有字段都为空
EN

Stack Overflow用户
提问于 2016-09-14 17:26:20
回答 1查看 2.6K关注 0票数 1

我使用JDBC与createStruct()一起调用Oracle数据库上的存储过程,该存储过程接受自定义类型作为参数。存储过程将自定义类型字段插入到表中,当我稍后从表中插入SELECT时,我会看到我试图插入的所有字段都是NULL

自定义类型如下所示:

代码语言:javascript
复制
type record_rec as object (owner_id varchar2 (7),
                                        target_id VARCHAR2 (8),
                                        IP VARCHAR2 (15),
                                        PREFIX varchar2 (7),
                                        port varchar2 (4),
                                        description VARCHAR2 (35),
                                        cost_id varchar2(10))

存储过程如下所示:

代码语言:javascript
复制
package body            "PKG_RECORDS"
IS
procedure P_ADD_RECORD (p_target_id in out VARCHAR2,
                      p_record_rec in record_rec)
is
  l_target_id   targets.target_id%TYPE;
BEGIN
  Insert into targets (target_id,
                      owner_id,
                      IP,
                      description,
                      prefix,
                      start_date,
                      end_date,
                      cost_id,
                      port,
                      server_name,
                      server_code)
       values (f_sequence ('TARGETS'),
               p_record_rec.owner_id,
               p_record_rec.ip,
               p_record_rec.description,
               p_record_rec.prefix,
               sysdate,
               to_date ('01-JAN-2050'),
               p_record_rec.cost_id,
               p_record_rec.port,
               'test-server',
               '51')
    returning target_id
         into p_target_id;
 END;
END PKG_RECORDS;

我的Java代码如下所示:

代码语言:javascript
复制
try (Connection con = m_dataSource.getConnection()) {
    ArrayList<String> ids = new ArrayList<>();
    CallableStatement call = con.prepareCall("{call PKG_RECORDS.P_ADD_RECORD(?,?)}");
    for (Record r : records) {
        call.registerOutParameter("p_target_id", Types.VARCHAR);
        call.setObject("p_record_rec", 
                con.createStruct("SCHEME_ADM.RECORD_REC", new Object[] {
                        r.getTarget_id(),
                        null, // will be populated by SP
                        t.getIp(),
                        t.getPrefix(),
                        t.getPort(),
                        t.getDescription(),
                        t.getCost_id()
                }), Types.STRUCT);
        call.execute();
        ids.add(call.getString("p_target_id"));
    }

    return new QueryRunner().query(con, 
            "SELECT * from TARGETS_V WHERE TARGET_ID IN ("+
                    ids.stream().map(s -> "?").collect(Collectors.joining(",")) +
                    ")",
            new BeanListHandler<Record>(Record.class),
            ids.toArray(new Object[] {})
            ).stream()
            .collect(Collectors.toList());
} catch (SQLException e) {
    throw new DataAccessException(e.getMessage());
}

注意:*最后一部分是使用Apache utils-我喜欢它们的bean流操作。*连接使用的是C3P0连接池--这可能是相关的吗?*只是为了说明--并不是bean处理器将空值填充到Record bean字段--如果我使用see直接加载表(或视图),则可以看到数据库中的字段确实设置为NULL

进程运行时不存在SQLException,或者任何其他注意事项都是错误的。

有什么好查的吗?

更新在阅读了Objects和SQLData映射之后,重写了代码以使用SQLData

Record类现在实现了SQLData,它的writeSQL()方法如下所示:

代码语言:javascript
复制
@Override
public void writeSQL(SQLOutput stream) throws SQLException {
    stream.writeString(owner_id);
    stream.writeString(target_id);
    stream.writeString(Objects.isNull(ip) ? "0" : ip); // weird, but as specified
    stream.writeString(prefix);
    stream.writeString(String.valueOf(port));
    stream.writeString(description);
    stream.writeString(cost_id);
}

然后,在调用代码的开头,我添加了:

代码语言:javascript
复制
con.getTypeMap().put("SCHEME_ADM.RECORD_REC", Record.class);

而不是使用createStruct()setObject()调用现在看起来很简单:

代码语言:javascript
复制
call.setObject("p_record_rec", t, Types.STRUCT)

但是结果是相同的-没有错误,所有传递的值都被读取为NULL。我已经通过writeSQL()实现进行了跟踪,我可以看到它被调用了,所有的值都被正确地传递到了Oracle代码中。我尝试在Types.JAVA_OBJECT调用中使用setObject(),得到了一个错误:Invalid column type

更新2濒临疯狂无助我已经实现了模式

代码语言:javascript
复制
public class Record implements SQLData, OracleData, OracleDataFactory {
...
@Override
public Object toJDBCObject(Connection conn) throws SQLException {
    return conn.createStruct(getSQLTypeName(), new Object[] {
            Objects.isNull(owner_id) ? "" : owner_id,
            Objects.isNull(record_id) ? "" : record_id,
            Objects.isNull(ip) ? "0" : ip,
            Objects.isNull(prefix) ? "" : prefix,
            String.valueOf(port),
            Objects.isNull(description) ? "" : description,
            Objects.isNull(cost_id) ? "" : cost_id
    });
}

@Override
public OracleData create(Object jdbcValue, int sqltype) throws SQLException {
    if (Objects.isNull(jdbcValue)) return null;
    LinkedList<Object> attr = new LinkedList<>(Arrays.asList(((OracleStruct)jdbcValue).getAttributes()));
    Record r = new Record();
    r.setOwner_id(attr.removeFirst().toString());
    r.setRecord_id(attr.removeFirst().toString());
    r.setIp(attr.removeFirst().toString());
    r.setPrefix(attr.removeFirst().toString());
    r.setPort(Integer.parseInt(attr.removeFirst().toString()));
    r.setDescription(attr.removeFirst().toString());
    r.setCost_id(attr.removeFirst().toString());
    return r;
}

public static OracleDataFactory getOracleDataFactory() {
    return new Record();
}

呼叫代码:

代码语言:javascript
复制
...
// unwrap the Oracle object from C3P0 (standard JDBCv4 API)
OracleCallableStatement ops = call.unwrap(OracleCallableStatement.class);
// I'm not sure why I even need to do this - it looks exactly like
// the standard JDBC code
for (Records r : records) {
    ops.registerOutParameter(1, Types.VARCHAR);
    ops.setObject(2, t);
    ops.execute();
    ids.add(ops.getString(1));
}
...

同样的结果--没有错误,在表中创建了一个记录,所有提供的值都为null。我已经通过代码进行了跟踪,并正确地调用了toJDBCObject()方法,并将值正确地传递给createStruct()

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2016-09-16 14:51:52

发现问题了。令人烦恼的是,它是关于字符编码的。

如果在toJDBCObject()实现中,我在创建的结构上运行getAttributes(),则生成的Object[]数组的所有字段都设置为"???"。这很奇怪,看起来像是字符集转码失败(尽管看起来也很奇怪--所有字段都有三个问号,不管值长如何,包括空字符串值)。

根据Oracle的JDBC开发人员指南“全球化支持”

基本Java Archive (JAR)文件ojdbc7.jar包含所有必要的类,以便为以下方面提供完全的全球化支持:

  • 用于CHAR、VARCHAR、LONGVARCHAR或CLOB数据的Oracle字符集,这些数据没有作为对象或集合类型的数据成员被检索或插入。
  • CHAR或VARCHAR对象的数据成员,以及字符集US7ASCII、WE8DEC、WE8ISO8859P1、WE8MSWIN1252和UTF8的集合。

若要在对象或集合的CHAR或VARCHAR数据成员中使用任何其他字符集,必须在CLASSPATH环境变量中包括orai18n.jar:

ORACLE_HOME/jlib/orai18n.jar

我的设置是使用字符集"WE8ISO8859P9“(我不知道为什么、它意味着什么,或者即使它是由客户机或服务器选择的--我只是转储了由OracleData API实现创建的STRUCT对象,它就在那里)。

因此,当甲骨文说它没有“提供完全的全球化支持”时,就意味着“所有字符字段都将被悄悄地转换为NULL”。嗯哼。

无论如何,将orai18n.jar添加到CLASSPATH确实解决了问题,现在记录被正确地添加到数据库中。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39496367

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档