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

JPA原生查询结果转StudentEntity抛出ClassCastException求因

问题:JPA原生查询结果强转实体类抛出ClassCastException

问题场景

服务端接收客户端请求后,尝试将MySQL原生查询返回的结果转换为StudentEntity实体类,核心代码片段如下:

else if(operation == GET_STUDENT_SUBJECTS_DATA) {
    System.out.println("I'm getting students subjects data!");
    StudentSubjectRequestDto studentGradesRequestData = (StudentSubjectRequestDto) inStream.readObject();
    Query queryForGettingStudent = databaseJpaHandler.getEntityManager().createNativeQuery("SELECT * FROM Students S WHERE S.Email = :email ")
            .setParameter("email", studentGradesRequestData.getEmail());
    List<StudentEntity> listOfStudentsMatched = (List<StudentEntity>)(List<?>) queryForGettingStudent.getResultList();
    if(!listOfStudentsMatched.isEmpty()) {
        StudentEntity student = listOfStudentsMatched.get(0);
        // 后续查询逻辑...
    }
}

执行StudentEntity student = listOfStudentsMatched.get(0);时抛出以下异常:

Exception in thread "Thread-2" java.lang.ClassCastException: class [Ljava.lang.Object; cannot be cast to class org.academic.server.entities.StudentEntity ([Ljava.lang.Object; is in module java.base of loader 'bootstrap'; org.academic.server.entities.StudentEntity is in unnamed module of loader 'app')

对应的StudentEntity实体类已正确标注@Entity和@Table注解,映射数据库Students表。

异常原因

核心原因是JPA原生SQL查询的默认返回类型并非实体类实例:

  • 使用EntityManager.createNativeQuery(String sql)重载方法时,JPA不会自动将结果映射为实体类,而是返回List<Object[]>——查询结果的每一行都会被拆分为一个Object数组,数组元素对应SQL查询的每一列值,顺序与SELECT语句中的列顺序一致。
  • 你代码中的(List<StudentEntity>)(List<?>)只是编译期的强制类型转换,并没有改变运行时的实际元素类型,所以当你尝试把数组Object[]强转为StudentEntity时,必然触发ClassCastException。

解决办法

1. 让原生查询直接返回实体类

使用createNativeQuery的重载方法,指定返回的实体类类型,JPA会自动完成结果映射:

// 重载方法第二个参数指定实体类
Query queryForGettingStudent = databaseJpaHandler.getEntityManager()
    .createNativeQuery("SELECT * FROM Students S WHERE S.Email = :email ", StudentEntity.class)
    .setParameter("email", studentGradesRequestData.getEmail());
// 无需强制类型转换,直接获取实体类列表
List<StudentEntity> listOfStudentsMatched = queryForGettingStudent.getResultList();

2. 处理非实体类的DTO映射(针对第二个查询)

第二个查询需要返回StudentSubjectResponseDto(非JPA实体类),无法直接用实体类映射,可通过以下方式处理:

方式一:手动遍历数组映射

List<Object[]> resultRows = queryForGettingStudentSubjects.getResultList();
List<StudentSubjectResponseDto> responseList = new ArrayList<>();
for(Object[] row : resultRows) {
    StudentSubjectResponseDto dto = new StudentSubjectResponseDto();
    dto.setShortcut((String) row[0]);       // 对应UD.Shortcut
    dto.setSubjectName((String) row[1]);    // 对应Subj.Name
    dto.setProfessorName((String) row[2]);  // 对应P.FirstName + ' ' + P.LastName
    dto.setGrade((Integer) row[3]);         // 对应SS.Grade
    dto.setEcts((Integer) row[4]);          // 对应Subj.ECTS
    dto.setSpecification((String) row[5]);  // 对应Subj.Specification
    responseList.add(dto);
}
outStream.writeObject(responseList);

方式二:使用@SqlResultSetMapping定义映射规则

在实体类上添加@SqlResultSetMapping,将查询列映射到DTO的字段,再通过createNativeQuery指定映射名称。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:03:15