将Oracle存储过程结果Map到自定义Java类型(类)

qnzebej0  于 2023-08-04  发布在  Oracle
关注(0)|答案(3)|浏览(182)

我必须调用Oracle(11 g)中使用单个IN OUT参数的存储过程。此参数是Oracle自定义类型,定义为

CREATE OR REPLACE TYPE "SEPADD"."T_NAPRAVI_NALOG_TEST" IS OBJECT
 (
    I_INICIJALI              varchar2(3)  ,          
    I_STATUS                 number(1)        
 )

字符串
实际的类型更复杂,但为了更好的可读性,我在这里进行了简化。使用此类型的Oracle过程(再次简化)定义为

CREATE OR REPLACE PROCEDURE "SEPADD"."GETNALOGTESTPROC"(nalog in out T_NAPRAVI_NALOG_TEST )
IS   
      BEGIN       
  nalog.I_INICIJALI := 'PC';         
      nalog.I_STATUS := nalog.I_STATUS + 3; 
END;


Oracle自定义类型Map到实现SQLData接口的Java类。(请参见https://docs.oracle.com/javase/tutorial/jdbc/basics/sqlcustommapping.html#implementing_sqldata)

public class TNapraviNalog implements SQLData{

private int I_STATUS;
private String I_INICIJALI;
private String sql_type = "T_NAPRAVI_NALOG_TEST";
public String getSQLTypeName() {
        return sql_type;
    }
    public int getIStatus(){
     return this.I_STATUS;
}
    public String getIInicijali(){
        return this.I_INICIJALI;
    }
    public void setIInicijali(String in){
        I_INICIJALI = in;
    }
    public void setIStatus(int st){
        I_STATUS = st;
    }
    public void readSQL(SQLInput stream, String type)
        throws SQLException {
        sql_type = type;
        I_INICIJALI = stream.readString();
        I_STATUS = stream.readInt();

    }

    public void writeSQL(SQLOutput stream)
        throws SQLException {
        stream.writeString(I_INICIJALI);
        stream.writeInt(I_STATUS);          
    }
}


现在,从我的JDBC代码中,我以下面的方式调用Oracle存储过程

Object obj=null;
    ResultSet rs=null;
    CallableStatement stmt=null;
    TNapraviNalog n = null;
    try{

          sqlQuery = "{call getnalogtestproc(?)}";

          Map m = conn.getTypeMap();
          m.put("sepadd.T_NAPRAVI_NALOG_TEST", Class.forName("ib.easyorm.db.TNapraviNalog"));//this maps the Java class to the Oracle custom type
          conn.setTypeMap(m);

          stmt=conn.prepareCall(sqlQuery);
          stmt.registerOutParameter(1, Types.STRUCT, "T_NAPRAVI_NALOG_TEST");
          stmt.setObject(1, paramValues.get(0) ); //paramValues.get(0) returns an instance of TNapraviNalog class

          stmt.execute();    

          obj = stmt.getObject(1, m);

          //obj = stmt.getObject(1,TNapraviNalog.class); this method is not implemented in the JDBC driver

    }catch(Exception e){
          throw new EasyORMException(e);
    }finally{
          closeResources(rs,stmt);
    }
    return obj;


现在,问题是,虽然我可以获得存储过程返回的结果,但结果没有转换为Java类(TNApraviNalog),因此我必须手动完成该操作。我可以用TNapraviNalog(stmt.setObject(1,paramValues.get(0))的示例成功地调用Oracle过程;)但我无法将结果转换为TNapraviNalog。我真的很希望能有这样的东西

TNapraviNalog nalog = stmt.getObject(1, m);


但是,这一行将导致异常(java.lang.ClassCastException:不能将oracle.sql.STRUCT强制转换为ib.easyorm.db.TNapraviNalog)。我猜JDBC驱动程序不知道stmt.getObject(1,m)返回的实际类型,因此无法进行转换。
有谁知道这是否可以使用普通JDBC或Hibernate来完成?

编辑:来自Oracle页面的相关代码(链接在cihan seven的回答中给出)
从可调用语句OUT参数中检索SQLData对象

假设您有一个OracleCallableStatement示例ocs,它调用PL/SQL函数GETEMPLOYEE。程序将雇员编号传递给函数。该函数返回相应的Employee对象。要检索此对象,请执行以下操作:
1.准备OracleCallableStatement以调用GETEMPLOYEE函数,如下所示:

OracleCallableStatement ocs = (OracleCallableStatement)conn.prepareCall("{ ? = call GETEMPLOYEE(?) }");


2.将empnumber声明为GETEMPLOYEE的输入参数。使用类型代码OracleTypes. STRUCT将SQLData对象注册为OUT参数。然后运行语句。这可以通过以下方式完成:

ocs.setInt(2, empnumber); 
 ocs.registerOutParameter(1, OracleTypes.STRUCT, "EMP_OBJECT"); 
ocs.execute();


3.使用getObject方法检索employee对象。下面的代码假设有一个类型Map条目将Oracle对象Map到Java类型Employee:

Employee emp = (Employee)ocs.getObject(1); //my comment-->this doesn't seem to work


如果没有类型Map条目,那么getObject将返回一个oracle.sql.STRUCT对象。将输出强制转换为STRUCT类型,因为getObject方法返回泛型java.lang.Object类的示例。这一过程如下:

STRUCT emp = (STRUCT)ocs.getObject(1);


谢谢你的好意

yeotifhr

yeotifhr1#

错误似乎在这一行:

m.put("sepadd.T_NAPRAVI_NALOG_TEST", Class.forName("ib.easyorm.db.TNapraviNalog"));//this maps the Java class to the Oracle custom type

字符串
这是唯一一行使用模式所有者限定T_NAPRAVI_NALOG_TEST类型的代码。您可以在另外两个地方引用它而不使用模式名称。
如果您以SEPADD用户的身份连接到数据库(看起来确实如此),则可以从此行中删除模式所有者前缀sepadd.。或者,尝试将上一行中的模式所有者更改为大写。

nzkunb0c

nzkunb0c2#

您尝试将Struct对象强制转换为TNapraviNalog。但是根据Oracle documentation,您应该首先将结构体作为java.sql.STRUCT对象检索,然后将其转换为TNapraviNalog对象。
试试这个:

java.sql.Struct jdbcStruct = (java.sql.Struct)stmt.getObject(1, m);

字符串

zrfyljdw

zrfyljdw3#

我发现,每当遇到“java.lang.ClassCastException:oracle.sql.STRUCT cannot be cast to some.java.Class”问题是类没有被添加到TypeMap(这里不是这种情况)或者TnapraviNalog没有一个no arg构造函数(这是本例中的原因)

相关问题