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

Spring Data JPA调用存储过程时触发ClassCastException异常求助

问题描述

在Spring Data JPA应用中,使用Jakarta EntityManager调用Oracle存储过程P_GET_ACTORS_BY_CATEGORIES时抛出java.lang.ClassCastException异常,异常栈如下:

java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:777) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:758) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1309) ~[spring-boot-3.1.4.jar:3.1.4]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1298) ~[spring-boot-3.1.4.jar:3.1.4]
    at com.example.bootdatajpaproj11callingprocedure.BootDataJpaProj11CallingProcedureApplication.main(BootDataJpaProj11CallingProcedureApplication.java:10) ~[classes/:na]
Caused by: java.lang.ClassCastException: class org.hibernate.metamodel.model.domain.internal.EntityTypeImpl cannot be cast to class org.hibernate.query.OutputableType (org.hibernate.metamodel.model.domain.internal.EntityTypeImpl and org.hibernate.query.OutputableType are in unnamed module of loader 'app')
    at org.hibernate.procedure.internal.ProcedureParameterImpl.toJdbcParameterRegistration(ProcedureParameterImpl.java:118) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final]
    at org.hibernate.procedure.internal.StandardCallableStatementSupport.interpretCall(StandardCallableStatementSupport.java:90) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final]
    at org.hibernate.procedure.internal.ProcedureCallImpl.buildOutputs(ProcedureCallImpl.java:619) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final]
    at org.hibernate.procedure.internal.ProcedureCallImpl.getOutputs(ProcedureCallImpl.java:594) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final]
    at org.hibernate.procedure.internal.ProcedureCallImpl.outputs(ProcedureCallImpl.java:847) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final]
    at org.hibernate.procedure.internal.ProcedureCallImpl.getResultList(ProcedureCallImpl.java:973) ~[hibernate-core-6.2.9.Final.jar:6.2.9.Final]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.orm.jpa.SharedEntityManagerCreator$DeferredQueryInvocationHandler.invoke(SharedEntityManagerCreator.java:405) ~[spring-orm-6.0.12.jar:6.0.12]
    at jdk.proxy2/jdk.proxy2.$Proxy92.getResultList(Unknown Source) ~[na:na]
    at com.example.bootdatajpaproj11callingprocedure.runner.CallingPL_ProcedureTestRunner.run(CallingPL_ProcedureTestRunner.java:40) ~[classes/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:774) ~[spring-boot-3.1.4.jar:3.1.4]
    ... 5 common frames omitted

相关代码与配置

1. Entity类Actor.java

//Actor.java (Entity class)
package com.nt.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
@Entity
@Data
@AllArgsConstructor
public class Actor {
    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO)    
    private Integer aid;
    @Column(length = 20)
    private String  aname;
    @Column(length = 20)
    private  String category;
    @Column(unique = true,nullable = true)
    private  Long mobileNo;

    public  Actor() {
        System.out.println("Actor: 0-param constructor"+this.getClass());
    }
}

2. Repository接口IActorRepository.java

//IActorRepository.java
package com.nt.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.nt.entity.Actor;

public interface IActorRepository extends JpaRepository<Actor, Integer> {

    
}

3. 存储过程调用类CallingPL_ProcedureTestRunner.java

//CallingPL_ProcedureTestRunner.java
package com.nt.runner;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import com.nt.entity.Actor;

import jakarta.persistence.EntityManager;
import jakarta.persistence.ParameterMode;
import jakarta.persistence.StoredProcedureQuery;


@Component
public class CallingPL_ProcedureTestRunner  implements CommandLineRunner {
 
    @Autowired
    private EntityManager  manager;

    @Override
    public void run(String... args) throws Exception {
        StoredProcedureQuery query=manager.createStoredProcedureQuery("P_GET_ACTORS_BY_CATEGORIES", Actor.class);
        // register both IN ,OUT params  of PL/SQL procedure
        query.registerStoredProcedureParameter(1, String.class,ParameterMode.IN);
        query.registerStoredProcedureParameter(2, String.class,ParameterMode.IN);
        //registering out param
        query.registerStoredProcedureParameter(3, Actor.class,ParameterMode.REF_CURSOR); 
        //set values to IN params
        query.setParameter(1, "CLERK");
        query.setParameter(2, "MANAGER");
        //call PL/SQL procedure
        List<Actor> list=query.getResultList();
        //proecess the result
        list.forEach(System.out::println);
        
    }//main

}//class

4. Oracle表结构

CREATE TABLE "SYSTEM"."ACTOR" 
   (    "AID" NUMBER(10,0) NOT NULL ENABLE, 
    "ANAME" VARCHAR2(20 CHAR), 
    "CATEGORY" VARCHAR2(20 CHAR), 
    "MOBILE_NO" NUMBER(19,0), 
     PRIMARY KEY ("AID"))

5. Oracle存储过程定义

create or replace NONEDITIONABLE PROCEDURE P_GET_ACTORS_BY_CATEGORIES 
(
  ROLE1 IN VARCHAR2 
, ROLE2 IN VARCHAR2 
, DETAILS OUT SYS_REFCURSOR 
) AS BEGIN

  open DETAILS for 
  SELECT AID,ANAME,CATEGORY,MOBILE_NO FROM ACTOR WHERE CATEGORY IN(role1, role2) ORDER BY CATEGORY;
  
END P_GET_ACTORS_BY_CATEGORIES;

6. application.properties配置

#DataSource Configuration  (Bydefault  hikaricp )
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:orcl
spring.datasource.username=system
spring.datasource.password=tiger

#JPA- Hibenrate properties
spring.jpa.database-platform=org.hibernate.dialect.OracleDialect
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update

环境信息

  • Spring Boot 3.1.4
  • Hibernate 6.2.9.Final
  • Oracle数据库

解决方案

异常根源是注册REF_CURSOR类型输出参数时,错误指定Actor.class作为参数类型,Hibernate无法将实体类型转换为处理存储过程输出所需的OutputableType。

修改CallingPL_ProcedureTestRunner.java中的参数注册代码:
将注册REF_CURSOR参数的代码:

query.registerStoredProcedureParameter(3, Actor.class,ParameterMode.REF_CURSOR); 

替换为:

query.registerStoredProcedureParameter(3, void.class, ParameterMode.REF_CURSOR);

修改后的完整run方法代码:

@Override
public void run(String... args) throws Exception {
    StoredProcedureQuery query=manager.createStoredProcedureQuery("P_GET_ACTORS_BY_CATEGORIES", Actor.class);
    // register both IN ,OUT params  of PL/SQL procedure
    query.registerStoredProcedureParameter(1, String.class,ParameterMode.IN);
    query.registerStoredProcedureParameter(2, String.class,ParameterMode.IN);
    //registering out param - 使用void.class替代Actor.class
    query.registerStoredProcedureParameter(3, void.class, ParameterMode.REF_CURSOR); 
    //set values to IN params
    query.setParameter(1, "CLERK");
    query.setParameter(2, "MANAGER");
    //call PL/SQL procedure
    List<Actor> list=query.getResultList();
    //proecess the result
    list.forEach(System.out::println);
}

原理说明

  • 调用createStoredProcedureQuery时已指定结果映射的实体类型Actor.class,Hibernate会自动将REF_CURSOR返回的结果集映射到该实体。
  • REF_CURSOR本身只是游标句柄,不需要指定为实体类,指定void.class是告知Hibernate该参数为输出游标,无需额外类型转换。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 17:57:05