【问题标题】:Getting pl/sql array of struct from stored procedure using JdbcSimpleCall使用 JdbcSimpleCall 从存储过程中获取 pl/sql 结构数组
【发布时间】:2014-06-14 16:16:57
【问题描述】:

我正在尝试使用 SimpleJDBCCall 执行 oracle 存储过程,所有表和存储过程都在餐厅模式中,表如下所示:

CREATE TABLE STAFF
(
    STAFF_ID             NUMBER(5),
    STAFF_FIRST_NAME     VARCHAR2(10 BYTE)        NOT NULL,
    STAFF_LAST_NAME      VARCHAR2(20 BYTE)        NOT NULL,
    STAFF_ROLE           VARCHAR2(20 BYTE)        NOT NULL,
    STAFF_OTHER_DETAILS  VARCHAR2(50 BYTE)
);

我的类型包:

CREATE OR REPLACE PACKAGE Staff_Types 
AS
    TYPE Staff_Collection IS TABLE OF Staff%ROWTYPE;  
END Staff_Types;

我的访问包:

CREATE OR REPLACE PACKAGE Staff_TAPI 
AS
    FUNCTION getAllStaff RETURN Staff_Types.Staff_Collection;
END Staff_TAPI;

CREATE OR REPLACE PACKAGE BODY Staff_Tapi
AS
    FUNCTION getAllStaff 
        RETURN Staff_Types.Staff_Collection
    IS
        all_staff Staff_Types.Staff_Collection;
    BEGIN
        SELECT * 
        BULK COLLECT INTO all_staff
        FROM Staff;

        RETURN all_staff;
    END;

END Staff_Tapi;

Java 访问:

@Component
@Qualifier("staffJdbcDAO")
public class StaffJDBCDAO implements StaffDAO {
    JdbcTemplate jdbcTemplate;
    SimpleJdbcCall getAllMembersSP;

    @Autowired
    @Qualifier("dataSource")
    DataSource dataSource;

    @Autowired
    @Qualifier("jdbcTemplate")
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        initializeStoredProceduresCalls();
    }

    private void initializeStoredProceduresCalls() {
        getAllMembersSP = new SimpleJdbcCall(jdbcTemplate);
        getAllMembersSP.withCatalogName("Staff_Tapi");
        getAllMembersSP.withFunctionName("getAllStaff");
        getAllMembersSP.declareParameters(
            new SqlOutParameter("return",
                    Types.OTHER,
                    "Staff_Types.Staff_Collection",
                    new SqlReturnStructArray<>( new StaffMapper() )
            )
        );
        getAllMembersSP.compile();

    }

    @Override
    public List<Staff> getAllMembers() {
        Staff[] staff = getAllMembersSP.executeFunction(Staff[].class,new   HashMap<String,Object>() );
        return Arrays.asList(staff);
    }
}

映射类:

public class StaffMapper implements StructMapper<Staff> {

    @Override
    public STRUCT toStruct(Staff staff, Connection connection, String typeName) throws SQLException {
        StructDescriptor descriptor = StructDescriptor.createDescriptor(typeName, connection);

        Object[] attributes = new Object[5];
        attributes[0] = new Integer( staff.getId() );
        attributes[1] = new String("STAFF_FIRST_NAME");
        attributes[2] = new String("STAFF_LAST_NAME");
        attributes[3] = new String("STAFF_ROLE");
        attributes[4] = new String("STAFF_OTHER_DETAILS");
        Struct staffStruct = connection.createStruct(typeName,attributes);
        return new STRUCT(descriptor,connection,attributes);
    }

    @Override
    public Staff fromStruct(STRUCT struct) throws SQLException {
        StructDescriptor descriptor = struct.getDescriptor();
        ResultSetMetaData metaData = descriptor.getMetaData();
        Object[] attributes = struct.getAttributes();
        Map<String,Object> attributeMap = new HashMap<>();
        int idx = 1;
        for ( Object attribute : attributes )
            attributeMap.put( metaData.getColumnName(idx++),attribute );


        int id = ((Integer)attributeMap.get("STAFF_ID")).intValue();
        String firstName = (String) attributeMap.get("STAFF_FIRST_NAME");
        String lastName = (String) attributeMap.get("STAFF_LAST_NAME");
        String staffRole = (String) attributeMap.get("STAFF_ROLE");
        String otherDetails = (String) attributeMap.get("STAFF_OTHER_DETAILS");
        return new Staff(id,firstName,lastName,staffRole,otherDetails);
    }
}

和工作人员:

public class Staff {
    private int id;
    private String firstName;
    private String lastName;
    private String profession;
    private String otherDetails;


    public Staff(int id, String firstName, String lastName, String profession, String otherDetails) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
        this.profession = profession;
        this.otherDetails = otherDetails;
    }

    public int getId() {
        return id;
    }

    public int setId(int id) {
        this.id = id;
    }

    // and others getters and setters
}

当我从 StaffDAO 执行 getAllMembers 时,我不断得到: CallableStatementCallback; SQL 的未分类 SQLException [{? = 调用 STAFF_TAPI.GETALLSTAFF()}]; SQL 状态 [99999];错误代码 [17004];无效的列类型:1111;

当我将返回类型参数更改为 Types.Array 时,我得到: CallableStatementCallback; SQL 的未分类 SQLException [{? = 调用 STAFF_TAPI.GETALLSTAFF()}]; SQL 状态 [99999];错误代码 [17074];
无效的名称模式:restaurant.Staff_Types.Staff_Collection;

我尝试了两种方式,模式“Staff_Types.Staf_collection”得到了相同的结果,我尝试了将近 2 天,但不知道我该怎么做,如果有人有任何建议,我会很感激。

【问题讨论】:

  • 我似乎记得无法通过 jdbc 引用包类型,并且必须显式使用 CREATE TYPE(模式对象)才能引用。我认为数据字典将包类型名称显示为名称的一些错误版本。但是我已经有几年没有使用 Oracle 了,所以我可能会为此吃午饭。
  • 感谢 Glenn 的快速回答。我试过了,但结果和以前完全一样。

标签: java sql spring oracle jdbc


【解决方案1】:

您可能希望在 java 代码中大写您的自定义类型,例如

    getAllMembersSP.declareParameters(
        new SqlOutParameter("return",
                Types.OTHER,
                "STAFF_TYPES.STAFF_COLLECTION",
                new SqlReturnStructArray<>( new StaffMapper() )
        )
    );

【讨论】:

    【解决方案2】:

    您无法通过 JDBC 从存储过程加载 PL/SQL record。事实上,您甚至无法从 Oracle SQL 加载这样的类型。有关详细信息,另请参阅此问题:

    您只能通过 JDBC 加载 SQL 类型(与 PL/SQL 类型相反)。鉴于您的示例,您需要编写:

    -- You cannot really avoid this redundancy
    CREATE TYPE STAFF AS OBJECT
    (
        STAFF_ID             NUMBER(5),
        STAFF_FIRST_NAME     VARCHAR2(10 BYTE)        NOT NULL,
        STAFF_LAST_NAME      VARCHAR2(20 BYTE)        NOT NULL,
        STAFF_ROLE           VARCHAR2(20 BYTE)        NOT NULL,
        STAFF_OTHER_DETAILS  VARCHAR2(50 BYTE)
    );
    CREATE TYPE STAFF_TABLE AS TABLE OF STAFF;
    

    然后:

    CREATE OR REPLACE PACKAGE Staff_TAPI 
    AS
        FUNCTION getAllStaff RETURN STAFF_TABLE;
    END Staff_TAPI;
    

    【讨论】:

      【解决方案3】:

      为了轻松集成您的 PL/SQL 调用,并且由于它已经构建为函数:您是否考虑过这样的事情?

      select * from TABLE(CAST(Staff_Tapi.getAllStaff() as Staff_Types.Staff_Collection))
      

      这样,您可以轻松地将其作为常规 JDBC 查询执行。完成后,只需处理ResultSet 它返回的fromStruct 方法的一些小变体,以便将List&lt;Staff&gt; 列表返回到您在其之上的任何业务逻辑。希望你觉得这很有用!

      【讨论】:

      • 我尝试了您的查询并从表中选择 *(STAFF_TAPI.GETSTAFFFIRSTNAMES),对于每个我得到 [Error] Execution ORA-00902: invalid datatype ,但感谢您的建议
      • 也许我建议您的查询转换为错误的数据类型,我承认我现在没有 Oracle 环境来测试它。但是,我建议您尝试使其工作,因为它对您有用。看看 table(cast ...) 语法,以防我犯了一些错误:docs.oracle.com/cd/B19306_01/server.102/b14200/functions016.htm
      • 语法可能是正确的,根据我的调查,使用您转换为的类型的转换函数执行此操作,必须是正确的 sql 类型,而不是 pl/sql。据我了解,这导致它(类型)必须在公共范围内(而不是在包中)的问题。我可以将对象类型设为 Staff_collection,但随后我必须在此对象类型中创建所有人员列。我希望它是可以理解的,英语不是我的母语;p 除此之外,感谢您的建议。
      • 在最坏的情况下,正如您所说,它会使您在 Oracle 中创建集合类型作为重复表结构的对象类型的公共集合......可能是较小的邪恶?因为它可能也会让你的第一个 Java 实现工作
      • 首先感谢 Jorge 提供的所有 cmets 和时间。其次,我寻求其他解决方案的原因是,当我将存储过程结果作为查询时,它不能执行任何 DML 语句,这会减少 SP 可能提供的用例。
      猜你喜欢
      • 2015-11-15
      • 2016-10-31
      • 1970-01-01
      • 2011-08-31
      • 1970-01-01
      • 1970-01-01
      • 2014-05-11
      • 1970-01-01
      • 2011-10-12
      相关资源
      最近更新 更多