JPA使用new map()查询后强转Record报错,求排查解决方法
解决JPA查询返回Map导致的类型转换异常问题
嗨,Claus,我一眼就看出问题出在哪了,咱们一步步来解决!
问题根源
你在RecordRepository里用了select new map(...)这个JPQL语法,这个查询实际返回的是List<Map<String, Object>>,但你把方法的返回类型声明成了List<Record>。虽然编译时不会报错,但运行时JVM发现List里装的是HashMap而不是Record实体,调用getField1()自然就会抛出java.util.HashMap cannot be cast to Record的异常。
你打印的{field1=2018-01-15, field2=201801}就是HashMap的toString结果,这也能佐证这一点。
解决方案一:修正返回类型并处理Map
这是最快的修复方式,只需要调整Repository和Controller的代码:
- 修改
RecordRepository.java的返回类型:
@Query("select new map(field1 as field1, field2 as field2) from Record where year = ?1") List<Map<String, Object>> findByYear(String year);
- 在
RecordController.java中调整字段获取逻辑:
@RestController public class RecordController { @Autowired private RecordRepository recordRepository; @RequestMapping(value = "/record/{year}", method = RequestMethod.GET) public List<Map<String, Object>> recordByYear(@PathVariable("year") String year) { List<Map<String, Object>> l = recordRepository.findByYear(year); System.out.println(l.getClass()); System.out.println(l.get(1967)); // 获取field1的值,注意强制类型转换 String tmp_r = (String) l.get(1967).get("field1"); System.out.println(tmp_r); return l; } }
解决方案二:返回DTO对象(更优雅的方式)
如果你不想处理Map,希望像操作实体类一样调用getter方法,可以创建一个DTO类来接收查询结果:
- 创建
RecordDTO.java:
public class RecordDTO { private String field1; private String field2; // 必须提供与查询字段顺序匹配的构造方法 public RecordDTO(String field1, String field2) { this.field1 = field1; this.field2 = field2; } // getter方法 public String getField1() { return field1; } public String getField2() { return field2; } }
- 修改
RecordRepository.java的查询语句和返回类型:
// 注意替换成你的DTO实际包路径 @Query("select new com.yourpackage.RecordDTO(field1, field2) from Record where year = ?1") List<RecordDTO> findByYear(String year);
- 调整
RecordController.java的返回类型和逻辑:
@RestController public class RecordController { @Autowired private RecordRepository recordRepository; @RequestMapping(value = "/record/{year}", method = RequestMethod.GET) public List<RecordDTO> recordByYear(@PathVariable("year") String year) { List<RecordDTO> l = recordRepository.findByYear(year); System.out.println(l.getClass()); System.out.println(l.get(1967).getField1()); return l; } }
为什么你的初始写法行不通?
JPQL里的new map(...)是JPA提供的快捷语法,用来直接返回键值对形式的HashMap,它不会自动映射到你的Record实体类。JPA只有在查询实体本身(比如select r from Record r where ...)或者指定构造DTO时,才会返回对应的对象实例。
内容的提问来源于stack exchange,提问作者kometen
相关产品推荐
相关产品推荐

