春季启动数据@query到DTO

Posted

技术标签:

【中文标题】春季启动数据@query到DTO【英文标题】:spring boot data @query to DTO 【发布时间】:2019-04-18 17:14:03 【问题描述】:

我想将查询结果分配给 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]

【问题讨论】:

有什么异常? @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] 您的查询似乎是本机查询(!= JPQL 或 HQL)。在这种情况下,请在注释中指定它,例如:@Query(value = "sql string ", nativeQuery = true) 这个solution 可以在这种情况下正常工作。 【参考方案1】:

在我的项目中,我使用了 projections,如下所示:

@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();

并创建interface MetricDTO 如下所示:

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

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

【讨论】:

【参考方案2】:

首先,您可以查看 Spring Data JPA 文档,您可以在此部分找到一些帮助:Class-based Projections (DTOs)。

还有一段标题为避免用于投影 DTO 的样板代码,他们建议您使用 Lombok 的 @Value 注释来生成不可变 DTO。这类似于 Lombok 的 @Data 注解,但不可变。

如果将它应用到示例中,源代码将如下所示:

@Value
public class MetricDto 

    private int share;
    private int shareholder;


然后,由于您的查询是 NativeQuery,请在 Spring Data Repository 中指定它。 您可以在文档中找到帮助:Native Queries。 您将需要类似的东西:

@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,但您的建议对我有帮助【参考方案3】:
Query query = sessionFactory.getCurrentSession()
              .createNativeQuery(stringQuery).unwrap(org.hibernate.query.Query.class);

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

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。 欢迎来到 Stack Overflow,感谢您提供答案。您能否编辑您的答案以包括对您的代码的解释?这将帮助未来的读者更好地理解正在发生的事情,尤其是那些不熟悉该语言并努力理解这些概念的社区成员。当已经有社区验证的答案时,这一点尤其重要。在什么条件下您的方法可能更受欢迎?您是否在利用新功能?

以上是关于春季启动数据@query到DTO的主要内容,如果未能解决你的问题,请参考以下文章

春季数据 jpa @query 和可分页

在使用 @Query 注释的 Spring 数据存储库方法中返回 DTO 对象

春季启动指标+数据狗

春季启动和休眠:更新条目为空的所有列的行

春季启动中的休眠统计信息不起作用?

春季启动数据休息中的日期问题