Spring Boot Data @Query 转换为 DTO

12

我想把查询结果分配给一个DTO对象。该DTO的格式如下:

@Getter
@Setter
@NoArgsConstructor
public class Metric {
    private int share;
    private int shareholder;

    public Metric(int share, int shareholder) {
        this.share = share;
        this.shareholder = shareholder;
    }
            
}

查询看起来像是这样:

@RepositoryRestResource(collectionResourceRel = "shareholders", path = "shareholders")
public interface ShareholderRepository extends PagingAndSortingRepository<Shareholder, Integer> {
    @Query(value = "SELECT new com.company.shareholders.sh.Metric(SUM(s.no_of_shares),COUNT(*)) FROM shareholders s WHERE s.attend=true")
    Metric getMetrics();
}

然而,这种方法不起作用,因为我得到了以下异常:

Caused by:org.hibernate.QueryException: could not resolve property: no_of_shares of:com.company.shareholders.sh.Shareholder[SELECT new com.company.shareholders.sh.Metric(SUM(s.no_of_shares),COUNT(*)) FROM com.company.shareholders.sh.Shareholder s WHERE s.attend=true]

1
什么是异常? - Maciej Kowalski
@MaciejKowalski 这是引发的异常。Caused by:org.hibernate.QueryException: could not resolve property: no_of_shares of:com.company.shareholders.sh.Shareholder[SELECT new com.company.shareholders.sh.Metric(SUM(s.no_of_shares),COUNT(*)) FROM com.company.shareholders.sh.Shareholder s WHERE s.attend=true] - btinsae
2
看起来你的查询是一个本地查询(不是JPQL或HQL)。在这种情况下,需要在注释中指定它,例如:@Query(value = "sql string ", nativeQuery = true) - Guillaume Husta
这个解决方案可以有效地适用于这种情况。 - Robson Oliveira
4个回答

11
在我的项目中,我使用了像下面展示的投影
@Repository
public interface PeopleRepository extends JpaRepository<People, Long> {
    
    @Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
                   "FROM people p INNER JOIN dream_people dp " +
                   "ON p.id = dp.people_id " +
                   "WHERE p.user_id = :userId " +
                   "GROUP BY dp.people_id " +
                   "ORDER BY p.name", nativeQuery = true)
    List<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId);
    
    @Query(value = "SELECT p.name AS name, COUNT(dp.people_id) AS count " +
                   "FROM people p INNER JOIN dream_people dp " +
                   "ON p.id = dp.people_id " +
                   "WHERE p.user_id = :userId " +
                   "GROUP BY dp.people_id " +
                   "ORDER BY p.name", nativeQuery = true)
    Page<PeopleDTO> findByPeopleAndCountByUserId(@Param("userId") Long userId, Pageable pageable);
    
    }

结果投射到的接口:
public interface PeopleDTO {    
    String getName();
    Long getCount();    
}

投影接口中的字段必须与此实体中的字段匹配。否则,字段映射可能会出现问题。

如果您使用 SELECT table.column 的表示方法,请始终定义与实体名称相匹配的别名,如示例所示。

在您的情况下,请像以下所示更改 @Query

@Query(value = "SELECT new " + 
               "SUM(s.no_of_shares) AS sum,COUNT(*) AS count FROM " +
               "shareholders s WHERE s.attend=true", nativeQuery = true)
MetricDTO getMetrics();

创建以下所示的接口 MetricDTO:

public interface MetricDTO {
    Integer getSum();    
    Long getCount();    
}

同时确保 getSum()getCount() 的返回类型是正确的,这可能因数据库而异。

3

首先,您可以查看Spring Data JPA文档,在此部分中找到一些帮助:基于类的投影(DTO)

还有一个标题为“避免为投影DTO编写样板代码”的段落,在其中他们建议您使用Lombok的@Value注释,以生成不可变DTO。 这类似于Lombok的@Data注释,但具有不可变性。

如果将其应用于您的示例,则源代码将如下所示:

@Value
public class MetricDto {

    private int share;
    private int shareholder;

}

接下来,由于您的查询是NativeQuery,请在Spring Data Repository中进行指定。 您可以在文档中找到帮助:原生查询。 您需要类似以下内容:

@Query(value = "SELECT new 
   com.company.shareholders.sh.MetricDto(SUM(s.no_of_shares),COUNT(*)) FROM 
   shareholders s WHERE s.attend=true", nativeQuery = true)
   MetricDto getMetrics();

我尝试使用本地查询,但不起作用,只有使用HQL才行,但是你的建议帮了我。 - Diego Macario
1
此查询不能是本地的。当使用nativeQuery时,我们不能使用new com.company.shareholders.sh.MetricDto(...)这一部分。 - whitefang

0
Query query = sessionFactory.getCurrentSession()
              .createNativeQuery(stringQuery).unwrap(org.hibernate.query.Query.class);

((NativeQueryImpl) query).setResultTransformer(new AliasToBeanResultTransformer(DtoClass.class));

3
目前您的回答写得不清楚。请[编辑]以添加更多细节,帮助他人理解它如何回答所提出的问题。您可以在帮助中心中找到有关编写好答案的更多信息。 - Community
欢迎来到Stack Overflow,并感谢您为问题提供答案。您是否可以编辑您的答案,包括代码解释?这将有助于未来的读者更好地理解正在发生的事情,特别是那些对该语言不熟悉且难以理解概念的社区成员。当已经有一个被社区验证的答案时,这尤其重要。在什么条件下可能会优先考虑您的方法?您是否利用了新的功能? - Jeremy Caney

0
你正在编写一个混合查询,其中包含本地语法和JPQL;no_of_shares是你在数据库中的列名,但JPA希望你提供非本地语法,所以尝试用实体类中对应的字段替换no_of_shares。或者只需添加nativeQuery = true,让JPA理解它是一个本地查询。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接