Hibernate many-to-many association not updating join table

Martin :

In my app, I have a many-to-many association between the User and Preference entities. Since the join table requires an additional column, I had to break it down into 2 one-to-many associations as such:

User entity :

@OneToMany(mappedBy = "user", fetch = FetchType.EAGER, cascade={CascadeType.PERSIST, CascadeType.MERGE}, orphanRemoval = true)
public Set<UserPreference> getPreferences() 
{
    return preferences;
}

Preference entity :

@OneToMany(mappedBy = "preference", fetch = FetchType.EAGER)
public Set<UserPreference> getUserPreferences() 
{
    return userPreferences;
}

UserPreference entity :

@ManyToOne
@JoinColumn(name = "user_id", nullable = false)
public User getUser() 
{
    return user;
}

public void setUser(User user) 
{
    this.user = user;
}

@ManyToOne
@JoinColumn(name = "preference_id", nullable = false)
public Preference getPreference() 
{
    return preference;
}

public void setPreference(Preference preference) 
{
    this.preference = preference;
}

@Column(nullable = false, length = 25)
public String getValue() 
{
    return value;
}

public void setValue(String value) 
{
    this.value = value;
}

To update one of the preferences, I loop through the user's set of preferences and update the value as such:

@RequestMapping(value = {"/edit-{id}-preference"}, method = RequestMethod.POST)
public String updateUserPreference(@ModelAttribute("userPreference") UserPreference preference, BindingResult result, ModelMap model) 
{
    User loggedInUser = (User)session.getAttribute("loggedInUser");

    for (UserPreference pref : loggedInUser.getPreferences())
    {
        if (Objects.equals(pref.getId(), preference.getId()))
        {
            pref.setValue(preference.getValue());
        }
    }

    userService.update(loggedInUser);

    return "redirect:/users/preferences";
}

I have confirmed that the user variable I'm trying to update does indeed contain the new value after this code runs. Even weirder, the value does update on the webpage when the redirect happens but the database does NOT update! This is the code I'm using to do the update, this class is annotated with @Transactional and every other call to this method (to update the user's role for example) works perfectly:

@Override
public void update(User user) 
{
    User entity = dao.findById(user.getId());
    if (entity != null)
    {
        entity.setUserId(user.getUserId());
        entity.setPassword(user.getPassword());
        entity.setFirstName(user.getFirstName());
        entity.setLastName(user.getLastName());
        entity.setRole(user.getRole());
        entity.setPreferences(user.getPreferences());
    }
}

This acts like hibernate's session "cache" has the updated value but does not actually persist it. I am using this very same update method style for about 30 other entities and everything works fine. This is my only many-to-many association that I had to break down into 2 one-to-many associations so I have nothing to compare to.

Am I doing something wrong? When I create a user with a new HashSet and persist it, the value is written correctly in the "join table".

*****EDIT*****

For comparison, this is the code I use to create a new user with default preferences. The preferences exist already but the join table is completely empty and this code correctly persists the entities:

User user = new User();
        user.setUserId("admin");
        user.setPassword(crypter.encrypt("admin"));
        user.setFirstName("admin");
        user.setLastName("admin");
        user.setRole(roleService.findByName("Admin"));

        Set<UserPreference> userPreferences = new HashSet<>();

        Preference preference = preferenceService.findByName("anchorPage");
        UserPreference userPreference = new UserPreference();
        userPreference.setUser(user);
        userPreference.setPreference(preference);
        userPreference.setValue("System Statistics");
        userPreferences.add(userPreference);

        preference = preferenceService.findByName("showOnlyActivePatients");
        userPreference = new UserPreference();
        userPreference.setUser(user);
        userPreference.setPreference(preference);
        userPreference.setValue("true");
        userPreferences.add(userPreference);

        user.setPreferences(userPreferences);

        userService.save(user);

Thanks

ewramner :

How about using merge? That is what you are cascading after all and you have modified a detached object and need to merge back the changes:

public void update(User user)  {
  dao.merge(user);
}

EDIT: for clarity this replaces the old update method, so it should be called from the client side with loggedInUser, just like before.

EDIT 2: as noted in the comments merge will update all fields. The old update method also seems to do that? Optimistic locks (version numbers) can be used to guard against overwriting other changes by mistake.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=457802&siteId=1