Java SQLData - 如何将列表/数组转换为用户对象?

3

我正在学习如何使用SQLData,并且在将数据类型转换回对象时遇到了问题。

我的Oracle类型大致如下:

CREATE OR REPLACE TYPE activities_t AS OBJECT
(
   list   activity_list_t;
);

CREATE OR REPLACE TYPE activity_list_t AS TABLE OF activity_t;

CREATE OR REPLACE TYPE activity_t AS OBJECT
(
   startDate  DATE;
   endDate    DATE;
);

我的Java代码如下:

public class Activities implements SQLData {
    private String sqlType = "ACTIVITIES_T";
    List<Activity> list;

    // must have default ctor!
    public Activities() {
    }

    public String getSQLTypeName() throws SQLException
    { 
        return sqlType; 
    }

    public List getList() {
        return list;
    }

    public void setList(List list) {
        this.list = list;
    }

    public void readSQL(SQLInput stream, String typeName) throws SQLException 
    {
        Array a = stream.readArray();
        // :(
    }

    public void writeSQL(SQLOutput stream) throws SQLException 
    {
        // stream.writeArray(this.list);
    }
}

我尝试了一些readSQL的方法,但并没有取得太大的成功 - 我错过了什么?

我正在使用JDBC调用一个具有"activities_t" OUT参数的PL/SQL存储过程:

Map map = connection.getTypeMap();
map.put("ACTIVITIES_T", Class.forName("Activities"));
connection.setTypeMap(map);

callableStatement = connection.prepareCall("{call GET_ACTIVITIES(?)}");
callableStatement.execute();

谢谢! Steve

(上述大部分是从记忆中写出来的,因为代码在工作中...)

2个回答

6
您需要添加一个类型映射来处理类型ACTIVITY_TACTIVITIES_T。从您的问题中无法确定您是否已经完成了这个步骤。
假设您已完成上述步骤并创建了一个名为Activity的类,并使其实现了SQLData接口。之后,您只需使用以下代码即可读取Activities中的活动列表:
    public void readSQL(SQLInput stream, String typeName) throws SQLException {
        Array array = stream.readArray();
        this.list = new ArrayList<Activity>();
        for (Object obj : (Object[])array.getArray()) {
            list.add((Activity)obj);
        }
    }

谢谢,一旦我弄清楚我缺少了另一种类型映射,我最终得到了类似的结果 :) - Steve
谢谢你的回答,但是关于writeSQL的实现呢?我找不到一个合适的方法将List转换为java.sql.Array。 - Dmitry Avgustis
@DmitryAvgustis: 对于这个问题你有什么看法? - Luke Woodward

3

提示:

  • JDBC API对类型名称区分大小写;如果您的类型名称不完全匹配,将会看到一个“无法解析类型”的错误。Oracle会将您的类型名称转换为大写,除非在创建语句中使用了双引号。
  • 如果类型不在默认模式中,则可能需要指定SCHEMA.TYPE_NAME
  • 请记得对类型授权执行权限,如果连接的用户不是所有者。
    如果您拥有包的执行权限但没有类型的执行权限,则getArray()在尝试查找类型元数据时会抛出异常。

getArray()

我的解决方案与Luke的基本相同。但是,在获取数组时,我需要提供类型映射:array.getArray(typeMap)。 您还可以在Connection上设置默认类型映射,但这对我不起作用。

调用getArray()时,您将获得对象类型的数组,即表示activity_tSQLData实现。

以下是一个通用函数,您可能会发现它有用:

public static <T> List<T> listFromArray(Array array, Class<T> typeClass) throws SQLException {
    if (array == null) {
        return Collections.emptyList();
    }
    // Java does not allow casting Object[] to T[]
    final Object[] objectArray = (Object[]) array.getArray(getTypeMap());
    List<T> list = new ArrayList<>(objectArray.length);
    for (Object o : objectArray) {
        list.add(typeClass.cast(o));
    }
    return list;
}

writeArray()

学习如何编写数组可能会令人沮丧,因为Oracle APIs需要连接才能创建一个数组,但是在writeSQL(SQLOutput sqlOutput)的上下文中并没有明显的连接。幸运的是,这篇博客提供了一个技巧/黑科技来获取OracleConnection,我在此处使用了它。

当你使用createOracleArray()创建一个数组时,你需要指定列表类型而不是对象类型作为类型名称,例如activity_list_t

以下是一个通用的编写数组函数。在您的情况下,listType将是"activity_list_t",您需要传入一个List<Activity>

public static <T> void writeArrayFromList(SQLOutput sqlOutput, String listType, @Nullable List<T> list) throws SQLException {
    final OracleSQLOutput out = (OracleSQLOutput) sqlOutput;
    OracleConnection conn = (OracleConnection) out.getSTRUCT().getJavaSqlConnection();
    conn.setTypeMap(getTypeMap());  // not needed?
    if (list == null) {
        list = Collections.emptyList();
    }
    final Array array = conn.createOracleArray(listType, list.toArray());
    out.writeArray(array);
}

注意:曾经我认为setTypeMap是必需的,但是现在当我删除了那行代码后,我的代码仍然可以正常工作,所以我不确定它是否必要。

Oracle是区分大小写的,并不会将所有内容转换为大写 - 默认行为是将所有未加引号的标识符转换为大写(因此看起来是不区分大小写的),但您可以使用双引号来抑制该行为,并使其保持标识符的大小写敏感性。 - MT0
如果我将类型名称改为小写,我会得到这个错误:错误代码[17060];无法构造描述符:无法解析类型“MYSCHEMA.mytype”;嵌套异常是java.sql.SQLException: 无法构造描述符:无法解析类型“MYSCHEMA.mytype” - Arlo
如果在Oracle中执行CREATE TYPE MYSCHEMA."mytype" IS ...,那么你将得到一个类型,该类型的标识符是区分大小写的(小写)。如果你执行CREATE TYPE MYSCHEMA.mytype IS ...(不使用引号),那么你将得到一个类型,该类型的标识符是MYTYPE(大写),因为Oracle会在幕后将未加引号的标识符转换为大写。你所说的“尽管Oracle是不区分大小写的”是不正确的 - 当你使用带引号的标识符时,Oracle是区分大小写的;当你不使用带引号的标识符时,它会将所有内容转换为大写。 - MT0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接