如何从 Java 调用具有自定义复杂对象类型作为输入参数的 Oracle PL-SQL 过程

How to call an Oracle PL-SQL procedure which has a custom defined complex Object type as input parameter from Java

在 Oracle 数据库中,我定义了一个对象类型,它有一个嵌套的对象类型。假设我有一个 PL-SQL 过程,它使用父对象类型作为输入参数,我正在寻找一种从 Java 代码调用该过程的高效方法。

CREATE OR REPLACE TYPE STUDENT AS OBJECT ( 
     name         VARCHAR2(35),
     address      HOME_ADDRESS
);

CREATE OR REPLACE TYPE HOME_ADDRESS AS OBJECT ( 
     street    VARCHAR2(35)
);

CREATE OR REPLACE PROCEDURE TEST(
    studentObject IN STUDENT
)
IS
BEGIN
     ... do domething ...
END;

在 Java 这边,我有相关的 POJO 保存数据。

我尝试将 Spring SimpleJdbcCall 与 StructMapper 一起使用。

StructMapper<Demo> MAPPER = BeanPropertyStructMapper.newInstance(Student.class);
SqlParameter studentIn = new SqlParameter("studentObject", Types.STRUCT, "MY_SCHEMA.STUDENT");
SimpleJdbcCall sjc = new SimpleJdbcCall(datasource).withSchemaName("MY_SCHEMA")
      .withProcedureName("TEST")
      .withoutProcedureColumnMetaDataAccess()
      .useInParameterNames("studentObject")
      .declareParameters(studentIn);
final MapSqlParameterSource inputParameters = new MapSqlParameterSource().addValue("studentObject",
            new SqlStructValue<Student>(studentInstance, MAPPER, "MY_SCHEMA.STUDENT"));
final Map<String, Object> execute = sjc.execute(inputParameters);

以上示例在不使用嵌套对象类型时有效。使用嵌套对象类型时,会抛出 java.sql.SQLException: Fail to convert to internal representation 异常。除此之外,spring-data-oracle 和 spring-data-jdbc-ext 项目似乎不再维护,我想避免使用它们。上面的代码不是我的真实代码,而是问题目的的示例。我的真实对象类型和 Java 中相应的 POJO 可能相当复杂,有许多嵌套对象和数组。

最终,我找到了使用 Java 包装器 类 的解决方案,它扩展了我的普通 POJO 并实现了 java.sql.SQLData 接口。 readSQL 方法的实现指示如何使用从数据库读取的数据填充对象,而 writeSQL 方法将此对象写入给定的 SQL 数据流。

import java.sql.SQLData;
import java.sql.SQLException;
import java.sql.SQLInput;
import java.sql.SQLOutput;

public class StudentMapper extends Student implements SQLData {

    public StudentMapper(Student student) {
        setName(student.getName());
        setAddress(student.getAddress());
    }

    @Override
    public String getSQLTypeName() throws SQLException {
        return "MY_SCHEMA.STUDENT";
    }

    @Override
    public void readSQL(SQLInput stream, String typeName) throws SQLException {
        setName(stream.readString());
        setAddress((Address) stream.readObject());
    }

    @Override
    public void writeSQL(SQLOutput stream) throws SQLException {
        stream.writeString(getName());
        final AddressMapper addressMapper = new AddressMapper(getAddress());
        stream.writeObject(addressMapper);
    }
}

final StudentMapper mapper = new StudentMapper(studentInstance);
CallableStatement callableStatement = ...
callableStatement.setObject("studentObject", mapper);
callableStatement.execute();

您可以使用 JPA 2.1 或更高版本调用 Oracle DB 中的存储过程。

例如: https://thoughts-on-java.org/call-stored-procedures-jpa/