Search code examples
spring-bootmany-to-many

Spring boot @ManyToMany update -> delete joined record


The code below is perfectly working when I'm adding new user or new AccountLo. The problem is, if I update a record in User all joined record from AccountLo are deleted.

I don't understand what's the problem.

The models:

public class User {
    ...
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
    @JoinTable (
        name = "lo_users",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "account_lo_id")
    )
    @Getter @Setter private List<AccountLo> accountLos; 
    ...
}

public class AccountLo {
    ....
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
    @JoinTable (
        name = "lo_users",
        joinColumns = @JoinColumn(name = "account_lo_id"),
        inverseJoinColumns = @JoinColumn(name = "user_id")
    )
    @Getter @Setter private List<User> users;  
    ....
}

The save method in the controller:

@PostMapping("/manage_users/save")
    public String saveUser(@ModelAttribute("user") User user, @RequestParam("roleRadio") String roleString, Model theModel) {
        
            List<UserType> userTypes = new ArrayList<>();
            
            // Add the user type for all users
            userTypes.add(userTypeService.findById(1));
            
            // Set the user types
            user.setUserTypes(userTypes);

            // Save the user
            userService.save(user);
            return "redirect:/admin/manage_users";
    }

I tried different cascading setup. I have this result always.


Solution

  • You are mapping bi-directional many-to-many association. You don't have to specify the join table in both classes. When you create a bi-directional association like this, you should set the join table in only one class (the host of the association), then use the mappedBy attribute to other class.

    public class User {
    ...
    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
    @JoinTable (
        name = "lo_users",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "account_lo_id")
    )
    @Getter @Setter private List<AccountLo> accountLos; 
    ...}
    
    
    public class AccountLo {
    ....
    @ManyToMany(mappedBy = "accountLos")
    @Getter @Setter private List<User> users;  
    
    ....}
    

    I also advise you to avoid using fetch = FetchType.EAGER in both classes. You would get a StackOverflow exception when loading entities from database.

    You can follow that tutorial for more explanations: https://www.baeldung.com/hibernate-many-to-many