Spring Data JPA:查询如何返回非实体对象或对象列表?

Posted

技术标签:

【中文标题】Spring Data JPA:查询如何返回非实体对象或对象列表?【英文标题】:Spring Data JPA: How can Query return Non- Entities Objects or List of Objects? 【发布时间】:2013-05-01 12:06:03 【问题描述】:

我在我的项目中使用 Spring Data JPA。我正在玩数百万张唱片。我有一个要求,我必须为各种表获取数据并构建一个对象,然后在 UI 上绘制它。现在如何在我的 Spring 数据存储库中实现这一点。我读过它可以通过命名原生查询来实现。

如果命名本机查询不返回实体或列表 实体,我们可以将查询结果映射到正确的返回类型 使用@SqlResultSetMapping 注解。

但是当我尝试使用@SqlResultSetMapping 时,它正在使用另一个entityResult。我的意思是它只是将一些查询结果转换为实体结果集,但我想要一个非实体对象的结果集。

@SqlResultSetMapping(
    name="studentPercentile",
    entities=
        @EntityResult(
           entityClass=CustomStudent.class,
              fields=
                  @FieldResult(name="id", column="ID"),
                  @FieldResult(name="firstName", column="FIRST_NAME"),
                   @FieldResult(name="lastName", column="LAST_NAME")
                       
        )
   
) 
@NamedNativeQuery(
    name="findStudentPercentile", 
    query="SELECT * FROM STUDENT", 
    resultSetMapping="studentPercentile")

在上面的示例中,我只是想将学生实体的结果放入另一个不是实体的 pojo 'CustomStudent' 中。 (这个例子我只是为了 POC 目的而执行的,实际用例非常复杂,复杂的查询返回不同的结果集)。

如何实现上述用例?除了使用我的存储库方法返回非实体对象的名称查询之外,还有其他方法吗?

【问题讨论】:

我最近遇到了同样的问题,很高兴看到有人发布并得到了解决方案! 我们手头有一个类似的问题,我们应该从三个表中获取记录计数,最终我们使用了 spring JdbcTemplate(尽管通常我们在项目中使用 Spring JPA Hibernate)。仅仅为了获取记录数而编写实体等似乎工作量太大。 答案同this question 【参考方案1】:

我们也可以使用 JSON 帮助进行解析。

类级别声明。

@Autowired
private EntityManager em;
private ObjectMapper mapper = new ObjectMapper(); 

主代码。

Query query = em.createNativeQuery(argQueryString);
NativeQueryImpl nativeQuery = (NativeQueryImpl) query;
nativeQuery.setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE);
List<Map<String,Object>> result = nativeQuery.getResultList();
List<CustomStudent> resultList = result.stream()
   .map(o -> 
         try 
              return 
            mapper.readValue(mapper.writeValueAsString(o),CustomStudent.class);
        catch (Exception e) 
           ApplicationLogger.logger.error(e.getMessage(),e);
       
     return null;
    ).collect(Collectors.toList());

【讨论】:

【参考方案2】:

JPA 2.1 ConstructorResult 怎么样?

@SqlResultSetMapping(
    name="studentPercentile",
    classes=
        @ConstructorResult(
            targetClass=CustomStudent.class,
            columns=
                @ColumnResult(name="ID"),
                @ColumnResult(name="FIRST_NAME"),
                @ColumnResult(name="LAST_NAME")
            
        )
    
)

@NamedNativeQuery(name="findStudentPercentile", query="SELECT * FROM STUDENT", resultSetMapping="studentPercentile")

【讨论】:

您好,这听起来不错,但是 NamedQuery 仍然必须附加到 Entity 才能成为持久性单元的一部分?【参考方案3】:

你可以这样做

@NamedQuery(name="findWhatever", query="SELECT new path.to.dto.MyDto(e.id, e.otherProperty) FROM Student e WHERE e.id = ?1")

那么 MyDto 对象只需要一个使用正确字段定义的构造函数,即

public MyDto(String id, String otherProperty)  this.id = id; this.otherProperty = otherProperty; 

【讨论】:

我可以写SELECT new path.to.dto.MyDto(e.id, new path.to.dto.OtherDto) 之类的东西吗 不,但是在您的 MyDto 中您可以有一个 OtherDto 属性,并且在 MyDto 的构造函数中您可以使用传递给 MyDto 构造函数的数据设置 OtherDto 属性。或者你可以使用@SqlResultSetMapping 注解。 当我写这篇文章时,编译失败,Validation failed for query for method public abstract..【参考方案4】:

当我第一次遇到这个问题时,我感到非常惊讶,但是,是的,您可以使用 @SqlResultSetMapping 仅将查询结果映射到标量和托管实体。

我猜你能做的最好的事情就是跳过自动映射。没有映射的查询将返回List&lt;Object[]&gt;,您可以按照您需要的方式对其进行映射。

另一种方法是使用@MappedSuperclass。表示为@MappedSuperclass 的类(在您的情况下为CustomStudent)可以(但不确定100%)在@SqlResultSetMapping 中使用。但是您需要引入继承层次结构,即您的学生实体必须扩展 CustomStudent。大部分时间这会从正确的 OO 设计中吸走,因为继承会有点人为......

【讨论】:

你的意思是原始类型吗? 可以使用ConstructorResult ***.com/a/42942353/4854931

以上是关于Spring Data JPA:查询如何返回非实体对象或对象列表?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Data JPA 查询结果返回至自定义实体

Spring Boot Jpa框架自定义查询语句返回自定义实体

Spring Data JPA 通过查询从实体获取投影

如何与 Spring Data REST 和 JPA 保持双向关系?

处理 JPA 规范和 spring-data-jpa 时如何使用声明 Stream 作为返回类型

Spring Data JPA Repository:如何有条件地获取子实体