首页 文章

如何将用户定义的类型作为输入传递给存储过程?

提问于
浏览
3

我有两个涉及用户定义类型的相关存储过程 . 第一个接受对象ID并返回用户定义类型的相应实例 . 第二个接受相同用户定义类型的实例并使用它执行某些操作 .

我正在使用Java,JDBC和一些Spring JDBC . 我已经成功完成了第一个存储过程,即 . 我可以从数据库中检索用户定义类型的实例,但是,我无法使第二个存储过程工作 .

这是我到目前为止的基本概要:

架构(PL / SQL)

create or replace type example_obj as object
  (ID     NUMBER,
   NAME   VARCHAR2(100))

create or replace type example_tab as table of example_obj

create or replace package
example as

procedure getExample
(p_id      in number,
 p_example out example_tab);

procedure useExample
(p_example  in example_tab);

end example;

实体(Java) - 表示Java中的用户定义类型

public class Example {
    public BigDecimal ID;
    public String Name;
}

Mapper(Java) - 从SQL类型映射到Java类型并返回

public class ExampleMapper extends Example implements SQLData {
    public static final String SQL_OBJECT_TYPE_NAME = "example_obj";
    public static final String SQL_TABLE_TYPE_NAME  = "example_tab";    

    @Override
    public String getSQLTypeName() throws SQLException {
        return SQL_TABLE_TYPE_NAME;
    }

    @Override
    public void readSQL(SQLInput stream, String typeName) throws SQLException  {
        ID   = stream.readBigDecimal();
        Name = stream.readString();
    }

    @Override
    public void writeSQL(SQLOutput stream) throws SQLException {
        stream.writeBigDecimal(ID);
        stream.writeString(Name);
    }
}

第一个存储过程(Java) - 检索给定其ID的Example对象

import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import org.springframework.jdbc.core.JdbcTemplate;

public Example getExample(BigDecimal ID) throws SQLException {
    String query = "begin example.getExample(?, ?); end;";
    Connection connection = jdbcTemplate.getDataSource().getConnection();
    CallableStatement callableStatement = connection.prepareCall(query);

    callableStatement.setBigDecimal("p_id", ID);

    Map<String, Class<?>> typeMap = connection.getTypeMap();
    typeMap.put(Example.SQL_OBJECT_TYPE_NAME, ExampleMapper.class);
    callableStatement.registerOutParameter("p_example", Types.ARRAY, Example.SQL_TABLE_TYPE_NAME);
    connection.setTypeMap(typeMap);

    callableStatement.execute();

    Array array = (Array)callableStatement.getObject("p_example");
    Object[] data = (Object[])array.getArray();
    Example example = (Example)data[0]; // It's an ExampleMapper, but I only want Example
    return example;
}

正如我前面提到的,第一个存储过程正常工作 . 从数据库检索的对象自动映射到相应的Java对象 . 下一步是能够调用接受此用户定义类型的实例的存储过程 .

第二个存储过程(Java) - 使用Example对象 - 不完整

public void useExample(Example example) throws SQLException {
    String query = "begin example.useExample(?); end;";
    Connection connection = jdbcTemplate.getDataSource().getConnection();
    CallableStatement callableStatement = connection.prepareCall(query);

    // Is this required (as per getExample())?
    Map<String, Class<?>> typeMap = connection.getTypeMap();
    typeMap.put(Example.SQL_OBJECT_TYPE_NAME, ExampleMapper.class);
    connection.setTypeMap(typeMap);

    /***
     *** What goes here to pass the object in as a parameter?
     ***/
    callableStatement.setObject("p_example", ???);

    callableStatement.execute();
}

1 回答

  • 1

    经过一段时间的努力,我能够开发出一种解决方案 . 一些观察:

    • 关于如何在网上执行此操作的文档不多 .

    • 在我看来,使用用户定义的类型作为输入并不是很好 .

    • 我发现我必须使用反直觉的 Struct (因为只有数组用于输出) .

    • 未使用 SQLData 接口,即 . writeSQL() 从未被调用,因为我发现我必须手动构建结构 . 映射输出时会调用 readSQL() .

    • 我必须使用特定于数据库的代码来创建数组,在我的例子中,这意味着Oracle类 .

    我可能会以错误的方式处理事情,所以我欢迎对我的解决方案发表评论 .

    public void useExample(Example example) throws SQLException {
        String query = "begin example.useExample(?); end;";
        Connection connection = jdbcTemplate.getDataSource().getConnection();
        CallableStatement callableStatement = connection.prepareCall(query);
    
        Map<String, Class<?>> typeMap = connection.getTypeMap();
        typeMap.put(Example.SQL_OBJECT_TYPE_NAME, ExampleMapper.class);
        connection.setTypeMap(typeMap);
    
        // Manually convert the example object into an SQL type.
        Object[] exampleAttributes = new Object[]{example.ID, example.Name};
        Struct struct = connection.createStruct(type.getObjectType(), exampleAttributes);
    
        // Build the array using Oracle specific code.
        DelegatingConnection<OracleConnection> delegatingConnection = (DelegatingConnection<OracleConnection>) new DelegatingConnection(connection);
        OracleConnection oracleConnection = (OracleConnection) delegatingConnection.getInnermostDelegate();
        Object[] data = new Object[]{struct};
        Array array oracleConnection.createOracleArray(Example.SQL_TABLE_TYPE_NAME, data);
    
        // Set the input value (finally).
        callableStatement.setObject("p_example", array);
    
        callableStatement.execute();
    }
    

相关问题