Search code examples
javaspringspring-bootspring-data-jpaprojection

How to use Spring data JPA projections when a Collection of another DTO inside needs to be fetched


I have a DTO that has a Collection of another DTO. I want to use Spring data JPA projections to fetch only the data needed for this particular DTO. But I don't know how.

Upd: added Entity classes. The question is about Spring Data Projections, not Spring data jpa in general.

@Getter
@Setter
@NoArgsConstructor
public class RestaurantDto {
    private int id;
    private String name;
private String address;
private int votes;
private List<DishDtoShort> dishes;


public RestaurantDto(String name, String address) {
    this.name = name;
    this.address = address;
}

public RestaurantDto(int id, String name, String address,int votes, List<DishDtoShort> dishes) {
    this.votes = votes;
    this.id = id;
    this.name = name;
    this.address = address;
    this.dishes = dishes;
}

public void addDish(DishDtoShort dishDtoShort) {
    dishes.add(dishDtoShort);
}
public List getDishes() {
    return Collections.unmodifiableList(dishes);
}

}

DTO inside a Collection

@Getter
@Setter
@NoArgsConstructor
public class DishDtoShort {
    private int id;
    private String name;
    private double price;

    public DishDtoShort(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
}

Restaurant Entity

@Entity
@Getter
@Setter
@NoArgsConstructor
@ToString(exclude = {"votes", "dishes"})
public class Restaurant extends AbstractNamedEntity {

    String address;

    @OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonManagedReference
    private List<Dish> dishes;

    @OneToMany(mappedBy = "restaurant", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonManagedReference
    private List<Vote> votes;

    public void addDish(Dish dish) {
        this.dishes.add(dish);
        dish.setRestaurant(this);
    }

    public void removeDish(Dish dish) {
        this.dishes.remove(dish);
        dish.setRestaurant(null);
    }

    public void addVote(Vote vote) {
        this.votes.add(vote);
        vote.setRestaurant(this);
    }

    public void removeVote(Vote vote) {
        this.votes.remove(vote);
        vote.setRestaurant(null);
    }

    public Restaurant(String name, String address) {
        super(name);
        this.address = address;
    }



    public List<Dish> getDishes() {
        return Collections.unmodifiableList(dishes);
    }

    public List<Vote> getVotes() {
        return Collections.unmodifiableList(votes);
    }
}

Dish Entity

@Entity
 @Getter
 @Setter
 @ToString
 @NoArgsConstructor
public class Dish extends AbstractNamedEntity {
    private double price;
    private LocalDate dateAdded;
    @ManyToOne(fetch = FetchType.LAZY)
    @JsonBackReference
    private Restaurant restaurant;

    public void setRestaurant (Restaurant restaurant) {
        this.restaurant = restaurant;
    }

    public Dish(String name, double price, Restaurant restaurant) {
        super(name);
        this.price = price;
        this.dateAdded = LocalDate.now();
        this.restaurant = restaurant;
    }
}

P.S. I know about wrong class naming.


Solution

  • I think, you just have to define your DTO in your repository and don't forget to have same name on your entity and DTO for the mapping :

    @Repository
    public interface RestaurantRepository extends JpaRepository<Restaurant, Integer> {
        List<RestaurantDto> findByName(String name);
    }
    

    you can have more informations here : https://www.baeldung.com/spring-data-jpa-projections