spring boot data @query to DTO

淺唱寂寞╮ 提交于 2020-07-18 07:23:58

问题


I want to assign the result of a query to a DTO object. The DTO looks like this:

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

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

And the query looks like the following:

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

However, this didn't work, as I got the following exception:

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:


In my project I've used projections to this like shown below:

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

The interface to which the result is projected:

public interface PeopleDTO {    
    String getName();
    Long getCount();    
}

The fields from the projected interface must match the fields in this entity. Otherwise field mapping might break.

Also if you use SELECT table.column notation always define aliases matching names from entity as shown in example.

In your case change @Query like shown below:

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

And create interface MetricDTO like shown below:

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

Also make sure the return type of getSum() and getCount() is correct this may vary based not database.




回答2:


First, you can have a look at the Spring Data JPA documentation, you can find some help at this section : Class-based Projections (DTOs).

There is also a paragraph titled Avoid boilerplate code for projection DTOs, where they advise you to use Lombok's @Value annotation, to produce an immutable DTO. This is similar to Lombok's @Data annotation, but immutable.

If you apply it to your example, the source will look like :

@Value
public class MetricDto {

    private int share;
    private int shareholder;

}

Then, as your query is a NativeQuery, specifiy it in your Spring Data Repository. You can find help in the documentation : Native Queries. You will need something like :

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


来源:https://stackoverflow.com/questions/53322203/spring-boot-data-query-to-dto

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!