javadatabasejpaone-to-manycascading-deletes

How to let to know parent that their child was deleted?


Hi i'm making application for recipe. One user could have many recipes and then he can delete his recipe. I store date in data base. User have a list of recipes in one of the field in db.I dont know what to do to delete a recipe on that list. I delete a recipe in db but is still on list in user field so we still can reach that recipe. I use JpaResporitory .There i give a part of my code. Could you help me please ?

public ResponseEntity<String> deleteRecipeById(long id, @AuthenticationPrincipal UserDetails details) {//toDo
   Recipe currentRecipe = getRecipeById(id);
   User currentUser = userRepository.findByEmail(details.getUsername())
            .orElseThrow(() -> new UsernameNotFoundException("User not found"));
    if(currentRecipe.getUser().equals(currentUser) ){
        currentUser.deleteFromUserList(currentRecipe);
        recipeRepository.delete(currentRecipe);
        // AND WHAT HERE ?
        return ResponseEntity.status(HttpStatus.OK).build();
    }
    return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
@Entity
@Getter
@Setter
@RequiredArgsConstructor
public class Recipe {
...
  @JsonIgnore
    @ManyToOne
    @JoinColumn(name = "user_id",  nullable = false)
    private User user;
}
@Entity
@Table(name = "users")
@Getter
@Setter
@RequiredArgsConstructor
class User {
...
   @JsonIgnore
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL,orphanRemoval = true ,  fetch = FetchType.EAGER)
    @Fetch(value = FetchMode.SUBSELECT)
    @ToString.Exclude
    @Column(name = "recipes")
    private List<Recipe> recipes = new ArrayList<>();
} THERE IS THAT LIST THAT I CAN'T CHANGE AND  NEED THIS TO DELETE RECIPE

there is how i save recipe

  public Long create(Recipe recipe, UserDetails details) {
        User currentUser = userRepository.findByEmail(details.getUsername())
                .orElseThrow(() -> new UsernameNotFoundException("User not found"));
        recipe.setUser(currentUser);
        currentUser.getRecipes()
                .add(recipe);
        userRepository.save(currentUser);
        return userRepository.findByEmail(details.getUsername())
                .orElseThrow(() -> new UsernameNotFoundException("User not found"))
                .getRecipes()
                .get(userRepository.findByEmail(details.getUsername())
                        .orElseThrow(() -> new UsernameNotFoundException("User not found"))
                        .getRecipes()
                        .size() - 1)
                .getId();
    }

I tried googling that i read article about jpa but i still dont know what i have to do


Solution

  • Ok the real issue given info you posted, is that you remove recipe from user but you don't save user after

    You either do:

    1- Call userRepository.save(currentUser), after removing recipe from user

    OR better >

    2- Annotate deleteRecipeById with @Transactional and it will do the job

    If you wonder how no.2 will do the job, it is because when you load entity from DB talking user here, the entity becomes in a Managed state meaning that if you do any updates to that entity like recipes.remove(recipe); then this managed entity user becomes eligible for updates, if you are in an opened transaction and that transaction gets committed [meaning function annotated with @Transactional comes to end] that managed entity user updates will be written to DB since transaction is committed, which will issue update SQL query to remove that receipe from user

    *TIP, BTW, you can not use write operation against DB without a transaction scope, the fact you deleted recipe when you called recipeRepository.delete(currentRecipe); it is because Spring Repositories are annotated with @Transactional by default and that is why solution no 2 is better since now you would use only one transaction for both operations instead of the solution no 1 that would issue 2 transactions