У меня есть двунаправленное соединение в Hibernate, когда я добавляю родителя с его детьми и удаляю их, все работает с CascadeType.All
, но я не могу правильно редактировать свои объекты. Я имею отношение ко многим к средней таблице, поэтому мне нужно отредактировать ее с помощью одной операции. Я использую Spring Data CrudRepository
метод save()
. Странно то, что я могу удалить всех детей и добавить только одного, и это тоже работает. Но когда я хочу добавить более одного дочернего элемента, он выдает исключение detached entity passed to persist: com.entity.Recipe
Ниже показано, как выглядит моя структура.
Родитель
@Entity
public class Recipe {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "recipe_generator")
@SequenceGenerator(name="recipe_generator", sequenceName = "recipe_seq")
@Column(name = "id", nullable = false)
private Long id;
@NaturalId
@Column
private String name;
@OneToMany(mappedBy = "recipe", cascade = CascadeType.ALL, orphanRemoval = true)
private List<RecipeIngredients> ingredients;
public void addIngredient(RecipeIngredients recipeIngredients) {
ingredients.add(recipeIngredients);
recipeIngredients.setRecipe(this);
}
public void removeIngredient(RecipeIngredients recipeIngredients) {
ingredients.remove(recipeIngredients);
recipeIngredients.setRecipe(null);
}
}
Дети
@Entity
public class RecipeIngredients implements Serializable {
@EmbeddedId
private RecipeIngredientsId recipeIngredientsId;
@ManyToOne(fetch = FetchType.LAZY)
@MapsId("recipeId")
private Recipe recipe;
@ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
@MapsId("ingredientId")
private Ingredient ingredient;
public RecipeIngredients(Recipe recipe, Ingredient ingredient) {
this.recipe = recipe;
this.ingredient = ingredient;
this.recipeIngredientsId = new RecipeIngredientsId(recipe.getId(), ingredient.getId());
}
}
@Entity
public class Ingredient {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ingredient_generator")
@SequenceGenerator(name = "ingredient_generator", sequenceName = "ingredient_seq")
@Column(name = "id", updatable = false, nullable = true)
private Long id;
@NaturalId
@Column(unique = true)
private String name;
}
И это то, что я хочу сделать: Сервис
@Transactional
public RecipeDto updateRecipe(Long id, RecipeDto recipe) {
recipe.setId(id);
return recipeDtoToEntityMapper
.recipeEntityToDto(recipeRepository
.save(recipeDtoToEntityMapper.recipeDtoToEntity(recipe)));
}
@Mapper(componentModel = "spring", uses = RecipeIngredientsDtoToEntityMapper.class)
public abstract class RecipeDtoToEntityMapper {
public Recipe recipeDtoToEntity(RecipeDto recipeDto) {
if (recipeDto == null) {
return null;
} else {
Recipe recipe = new Recipe();
recipe.setId(recipeDto.getId());
recipe.setName(recipeDto.getName());
//First I need to pull existing ingredients because when I try to simple add new(edited) childrens
//I have conflict on persistance object because Hibernate see that other Recipe has diffrent children
//"A different object with the same identifier value was already associated with the session"
Optional<List<RecipeIngredients>> existedRecipeIngredients = pullExistedIngredients(recipeDto.getId());
if (existedRecipeIngredients.isPresent()) {
recipe.setIngredients(existedRecipeIngredients.get());
updateIngredients(recipe, recipeDto);
}
}
...
//Then I have method to add new ingredients
private void addNewIngredientsToRecipe(Recipe recipe, RecipeDto recipeDto, List<String> existingIngredientsNames) {
for (RecipeIngredientsDto newIngredient : recipeDto.getIngredients()) {
String ingredientName = newIngredient.getIngredient().getName();
if (!existingIngredientsNames.contains(ingredientName)) {
//And here everything is allright for the first time dto is mapped to entity and ingredient is added
recipe.addIngredient(this.recipeIngredientsDtoToEntityMapper.recipeIngredientsToEntity(newIngredient, recipe));`
}
}
}
И все маги c происходит ниже, когда я хочу вытащить ингредиенты, он выдает мне исключение, что рецепт отсоединен. Зачем??? Я хочу только вытащить его детей и даже не манипулировать данными родителей.
public RecipeIngredients recipeIngredientsToEntity(RecipeIngredientsDto recipeIngredientsDto, Recipe recipe) {
if ( recipeIngredientsDto == null || recipe == null ) {
return null;
}
RecipeIngredients recipeIngredients = new RecipeIngredients();
recipeIngredients.setRecipe(recipe);
recipeIngredients.setIngredient( pullIngredient(ingredientsDtoToEntityMapper.ingredientsToEntity( recipeIngredientsDto.getIngredient() )) );
recipeIngredients.setRecipeIngredientsId(new RecipeIngredientsId());
return recipeIngredients;
}
//On this method I've got an error. *detached entity passed to persist: com.entity.Recipe*
private Ingredient pullIngredient(Ingredient ingredient) {
return ingredientRepository.findByName(ingredient.getName()).orElse(ingredient);
}