Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to map query result to DTO

I want to make a complex query and map the result into a DTO. The DTO is below:

@Value(staticConstructor = "of")
public class TotalsDto {
    LocalDate date;
    long totals;
    long totalPerCategory;
    int categoryId;
    String categoryName;
}

My repository interface is extending from JpaRepository. This is throwing an IllegalArgumentException: Not a managed type, because TotalsDto is not an Entity itself.

The repository is:

@Repository
public interface TotalsRepository extends JpaRepository<TotalsDto, Integer> { 

    @Query(value = "SELECT ...", nativeQuery = true)
    List<TotalsDto> getTotals(params...);
}

The query is getting data from other entities to build the DTO. Any way to map every column to DTO? I tried to map it with the query below but it still getting Not a managed class.

SELECT my.package.TotalsDto.of(column1, subqueryResult1, subqueryResult2...)
like image 521
Leonardo Avatar asked Feb 21 '19 10:02

Leonardo


People also ask

How do I return DTO from native queries?

The easiest way to use this projection is to define your query as a @NamedNativeQuery and assign an @SqlResultSetMapping that defines a constructor result mapping. The instantiation of the DTO objects is then handled by the underlying persistence provider when Spring Data JPA executes the @NamedNativeQuery.

How do you map native query results to entities?

The easiest way to map a query result to an entity is to provide the entity class as a parameter to the createNativeQuery(String sqlString, Class resultClass) method of the EntityManager and use the default mapping. The following snippet shows how this is done with a very simple query.

What is DTO projection?

You define a DTO projection in a CriteriaQuery in a pretty similar way as you do in JPQL. But instead of using the new keyword to specify the constructor call in a query String, you call the construct method on the CriteriaBuilder with a reference to the DTO class and a List of constructor parameters.


Video Answer


2 Answers

1) Make TotalsDto an interface

2) Create getters:

public interface TotalsDto{

    long getTotals();
    int getCategoryId();
    ...

}

Spring Data Jpa will then automatically create / fill you result object.

More on the subject here

like image 137
Maciej Kowalski Avatar answered Oct 26 '22 21:10

Maciej Kowalski


The first type argument to Repository (or JpaRepository) must be a managed entity. Therefore, JpaRepository<TotalsDto, Integer> is invalid, as TotalsDto is not a managed entity. Change TotalsDto to a projection interface like @MaciejKowalski has suggested, and then pass a JPA entity to Repository as the first type argument, say, Repository<Order, Integer>.

like image 1
manish Avatar answered Oct 26 '22 20:10

manish