In my Spring Boot app, I use Hibernate and applied the necessary relations to the following entities properly.
@Entity
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable=false, length=50)
private String title;
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL)
private List<RecipeIngredient> recipeIngredients = new ArrayList<>();
}
@Entity
public class RecipeIngredient {
@EmbeddedId
private RecipeIngredientId recipeIngredientId = new RecipeIngredientId();
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@MapsId("recipeId")
@JoinColumn(name = "recipe_id", referencedColumnName = "id")
private Recipe recipe;
@ManyToOne(optional = true, fetch = FetchType.LAZY)
@MapsId("ingredientId")
@JoinColumn(name = "ingredient_id", referencedColumnName = "id")
private Ingredient ingredient;
}
@Entity
public class Ingredient
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(unique=true, nullable=false, length=50)
@EqualsAndHashCode.Include
private String name;
@OneToMany(mappedBy = "ingredient", cascade = CascadeType.ALL)
private Set<RecipeIngredient> recipeIngredients = new HashSet<>();
}
Now I am trying to retrieve data by merging related entities. For example, when retrieving a Recipe, I also need to retrieve all Ingredients belonging to this Recipe.
As far as I know, I can use Projection and maybe it is better to only use Hibernate features and retrieve related table data via Java Stream
. I have no idea how should I retrieve data via Hibernate.
Suppose that I just need an Optional<Recipe>
that has List<Ingredient>
. Then, I probably need a DTO class something like that:
@Data
public class ResponseDTO {
private Long id;
private String title;
List<RecipeIngredient> ingredients;
// getter, setter, constructor
}
So, how should I populate this DTO with the requested Recipe and corresponding Ingredient data (getting Ingredient names besides id values) using Java Stream
?
Or if you suggest Projection way, I tried it but the data is multiplied by the ingredient count belonging to the searched recipe.
Update:
@Getter
@Setter
@NoArgsConstructor
public class ResponseDTO {
private Long id;
private String title;
List<IngredientDTO> ingredientDTOList;
public ResponseDTO(Recipe recipe) {
this.id = recipe.getId();
this.title = recipe.getTitle();
this.ingredientDTOList = recipe.getRecipeIngredients().stream()
.map(ri -> new IngredientDTO(ri.getIngredient().getName()))
.toList();
}
}
@Getter
@Setter
public class IngredientDTO {
private Long id;
private String name;
public IngredientDTO(String name) {
this.name = name;
}
}
CodePudding user response:
First, in the ResponseDTO
you will need you change the type of ingredients from List<RecipeIngredient>
to List<Ingredient>
.
To manually perform the mapping, you should use (to map from a suppose Recipe recipe to a RespondeDTO
response):
ResponseDTO recipeToResponseDTO(Recipe recipe) {
ResponseDTO response = new ResponseDTO();
response.setId(recipe.getId());
response.setTitle(recipe.getTitle());
response.setIngredients(recipe.recipeIngredients.stream()
.map(RecipeIngredient::getIngredient()
.collect(Collectors.toList());
return response;
}
On the other hand, to model a n-n relation, I encourage you to use the approach proposed by E-Riz in the comment.