Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hibernate many-to-many association not updating join table

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

like image 846
Martin Avatar asked May 30 '18 15:05

Martin


1 Answers

Instead of

entity.setPreferences(user.getPreferences());

Do something like:

for( UserPreference uf : user.getPreferences() ) {
    entity.getPreferences().add( uf );
}

The main difference here is that you aren't changing the list reference, which is managed by Hibernate, and is only adding elements to it.

like image 60
MiguelKVidal Avatar answered Sep 30 '22 03:09

MiguelKVidal