You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用含Oracle自定义类型的存储过程失败求助

问题:无法调用含Oracle自定义类型参数的存储过程

我无法调用带有Oracle自定义类型作为输入参数的存储过程,请求协助解决,同时若有相关Git示例也请分享,谢谢。


Oracle类型定义

create or replace TYPE STUDENT_ID_ARRAY AS TABLE OF NUMBER;

Oracle存储过程包

create or replace NONEDITIONABLE PACKAGE student_pkg AS
    -- 定义用于返回游标的RefCursor类型
    TYPE RefCursor IS REF CURSOR;

    PROCEDURE GetStudentInfo(p_department_id IN department.id%TYPE, p_student_ids IN STUDENT_ID_ARRAY, p_student_info OUT RefCursor);
END student_pkg;

create or replace NONEDITIONABLE PACKAGE BODY student_pkg AS

    -- GetStudentInfo存储过程实现
    PROCEDURE GetStudentInfo(p_department_id IN department.id%TYPE, p_student_ids IN STUDENT_ID_ARRAY, p_student_info OUT RefCursor)
    IS
    BEGIN
        -- 通过部门ID获取学生详情
        OPEN p_student_info FOR
            SELECT s.name AS student_name, d.name as department_name
            FROM student s
            INNER JOIN department d ON s.department_id = d.id
            WHERE d.id = p_department_id
            AND s.id in (SELECT COLUMN_VALUE FROM TABLE(p_student_ids));
    END GetStudentInfo;
END student_pkg;

Spring Data JPA Repository实现

@Repository
@RequiredArgsConstructor
public class TypeRepositoryImpl implements TypeRepository {

    private final EntityManager entityManager;

    @Override
    public List<Object[]> getStudentInfo(int departmentId, List<Integer> studentIds) {
        StoredProcedureQuery query = entityManager.createStoredProcedureQuery("student_pkg.GetStudentInfo");

        // 注册参数
        query.registerStoredProcedureParameter("p_department_id", Integer.class, ParameterMode.IN);
        query.registerStoredProcedureParameter("p_student_ids", Object.class, ParameterMode.IN);
        query.registerStoredProcedureParameter("p_student_info", void.class, ParameterMode.REF_CURSOR);

        query.setParameter("p_department_id", departmentId);
        query.setParameter("p_student_ids", studentIds);
        
        query.execute();
        return query.getResultList();
    }
}

错误信息

2024-05-30T06:32:42.166+05:30  INFO 24604 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : 初始化Servlet 'dispatcherServlet'
2024-05-30T06:32:42.167+05:30  INFO 24604 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : 初始化完成,耗时1ms
2024-05-30T06:32:42.249+05:30 DEBUG 24604 --- [nio-8080-exec-1] org.hibernate.SQL                        : 
    {call student_pkg.GetStudentInfo(?, ?, ?)}
Hibernate: 
    {call student_pkg.GetStudentInfo(?, ?, ?)}
2024-05-30T06:32:42.385+05:30  WARN 24604 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL错误: 6550, SQL状态: 65000
2024-05-30T06:32:42.385+05:30 ERROR 24604 --- [nio-8080-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper   : ORA-06550: 第1行, 第7列:
PLS-00306: 调用'GETSTUDENTINFO'时参数数量或类型错误
ORA-06550: 第1行, 第7列:
PL/SQL: 语句被忽略

2024-05-30T06:32:42.396+05:30 ERROR 24604 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : 路径为[]的上下文里,Servlet [dispatcherServlet]的service()方法抛出异常 [请求处理失败: org.springframework.dao.InvalidDataAccessResourceUsageException: 调用CallableStatement.getMoreResults出错 [ORA-06550: 第1行, 第7列:
PLS-00306: 调用'GETSTUDENTINFO'时参数数量或类型错误
ORA-06550: 第1行, 第7列:
PL/SQL: 语句被忽略

问题原因

直接传入List<Integer>无法被Oracle识别为自定义的STUDENT_ID_ARRAY类型,需要将Java集合转换为Oracle对应的ARRAY对象;同时参数注册时使用Object.class未明确指定类型,也会导致类型不匹配。

解决方案

步骤1:修改Repository实现,转换Java集合为Oracle ARRAY

需要获取底层的OracleConnection,通过它创建对应自定义类型的ARRAY对象:

@Repository
@RequiredArgsConstructor
public class TypeRepositoryImpl implements TypeRepository {

    private final EntityManager entityManager;

    @Override
    public List<Object[]> getStudentInfo(int departmentId, List<Integer> studentIds) {
        StoredProcedureQuery query = entityManager.createStoredProcedureQuery("student_pkg.GetStudentInfo");

        // 注册参数,REF_CURSOR使用Oracle内置游标类型
        query.registerStoredProcedureParameter("p_department_id", Integer.class, ParameterMode.IN);
        query.registerStoredProcedureParameter("p_student_ids", ARRAY.class, ParameterMode.IN);
        query.registerStoredProcedureParameter("p_student_info", void.class, ParameterMode.REF_CURSOR);

        query.setParameter("p_department_id", departmentId);

        // 将List转换为Oracle ARRAY
        try {
            Session session = entityManager.unwrap(Session.class);
            Connection connection = session.doReturningWork(Connection::getConnection);
            OracleConnection oracleConnection = connection.unwrap(OracleConnection.class);
            
            // 自定义类型名称要和Oracle中定义的完全一致(Oracle默认大写)
            ARRAY studentIdArray = oracleConnection.createARRAY("STUDENT_ID_ARRAY", studentIds.toArray());
            query.setParameter("p_student_ids", studentIdArray);
        } catch (SQLException e) {
            throw new RuntimeException("转换学生ID数组失败", e);
        }
        
        query.execute();
        return query.getResultList();
    }
}

步骤2:确保依赖正确

确保项目中引入了对应版本的Oracle JDBC驱动:

<!-- Maven依赖示例 -->
<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc8</artifactId>
    <version>21.13.0.0</version>
</dependency>

相关Git示例思路

核心逻辑是通过EntityManager获取OracleConnection,创建对应自定义类型的ARRAY对象:

  • 重点注意自定义类型名称与Oracle中的定义完全一致(区分大小写)
  • 处理JDBC连接时需正确unwrap为OracleConnection
  • 异常捕获要覆盖数组转换的可能报错场景

内容的提问来源于stack exchange,提问作者Jericho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 01:50:57