如何从 Spring Data JPA GROUP BY 查询中返回自定义对象

新手上路,请多包涵

我正在使用 Spring Data JPA 开发一个 Spring Boot 应用程序。我正在使用自定义 JPQL 查询按某个字段分组并获取计数。以下是我的存储库方法。

 @Query(value = "select count(v) as cnt, v.answer from Survey v group by v.answer")
public List<?> findSurveyCount();

它正在工作,结果如下:

 [
  [1, "a1"],
  [2, "a2"]
]

我想得到这样的东西:

 [
  { "cnt":1, "answer":"a1" },
  { "cnt":2, "answer":"a2" }
]

我怎样才能做到这一点?

原文由 Pranav C Balan 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.5k
2 个回答

JPQL 查询的解决方案

JPA 规范 中的 JPQL 查询支持此功能。

第 1 步:声明一个简单的 bean 类

package com.path.to;

public class SurveyAnswerStatistics {
  private String answer;
  private Long   cnt;

  public SurveyAnswerStatistics(String answer, Long cnt) {
    this.answer = answer;
    this.count  = cnt;
  }
}

第 2 步:从存储库方法返回 bean 实例

public interface SurveyRepository extends CrudRepository<Survey, Long> {
    @Query("SELECT " +
           "    new com.path.to.SurveyAnswerStatistics(v.answer, COUNT(v)) " +
           "FROM " +
           "    Survey v " +
           "GROUP BY " +
           "    v.answer")
    List<SurveyAnswerStatistics> findSurveyCount();
}

重要笔记

  1. 确保提供 bean 类的完全限定路径,包括包名。例如,如果 bean 类被称为 MyBean 并且它在包 com.path.to 中,则 bean 的完全限定路径将是 com.path.to.MyBean 简单地提供 MyBean 将不起作用(除非 bean 类在默认包中)。
  2. 确保使用 new 关键字调用 bean 类构造函数。 SELECT new com.path.to.MyBean(...) 会起作用,而 SELECT com.path.to.MyBean(...) 不会。
  3. 确保以与 bean 构造函数中预期的顺序完全相同的顺序传递属性。尝试以不同的顺序传递属性将导致异常。
  4. 确保查询是有效的 JPA 查询,也就是说,它不是本机查询。 @Query("SELECT ...")@Query(value = "SELECT ...")@Query(value = "SELECT ...", nativeQuery = false) 将工作,而 @Query(value = "SELECT ...", nativeQuery = true) 将不起作用。这是因为本机查询无需修改即可传递给 JPA 提供程序,并且是针对底层 RDBMS 执行的。由于 newcom.path.to.MyBean 不是有效的 SQL 关键字,因此 RDBMS 会引发异常。

原生查询解决方案

如上所述, new ... 语法是 JPA 支持的机制,适用于所有 JPA 提供程序。但是,如果查询本身不是 JPA 查询,即它是本机查询,则 new ... 语法将不起作用,因为查询直接传递给底层 RDBMS,它不理解 new 关键字,因为它不是 SQL 标准的一部分。

在这种情况下,需要用 Spring Data Projection 接口替换 bean 类。

第一步:声明一个投影接口

package com.path.to;

public interface SurveyAnswerStatistics {
  String getAnswer();

  int getCnt();
}

第 2 步:从查询中返回投影属性

public interface SurveyRepository extends CrudRepository<Survey, Long> {
    @Query(nativeQuery = true, value =
           "SELECT " +
           "    v.answer AS answer, COUNT(v) AS cnt " +
           "FROM " +
           "    Survey v " +
           "GROUP BY " +
           "    v.answer")
    List<SurveyAnswerStatistics> findSurveyCount();
}

使用 SQL AS 关键字将结果字段映射到投影属性以进行明确映射。

原文由 manish 发布,翻译遵循 CC BY-SA 4.0 许可协议

此 SQL 查询返回 List< Object[] > 会。

你可以这样做:

  @RestController
 @RequestMapping("/survey")
 public class SurveyController {

   @Autowired
   private SurveyRepository surveyRepository;

     @RequestMapping(value = "/find", method =  RequestMethod.GET)
     public Map<Long,String> findSurvey(){
       List<Object[]> result = surveyRepository.findSurveyCount();
       Map<Long,String> map = null;
       if(result != null && !result.isEmpty()){
          map = new HashMap<Long,String>();
          for (Object[] object : result) {
            map.put(((Long)object[0]),object[1]);
          }
       }
     return map;
     }
 }

原文由 ooozguuur 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题