简体   繁体   English

Hibernate多对多关联不更新联接表

[英]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: 由于联接表需要一个额外的列,因此我不得不将其分解为2个一对多关联,例如:

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 : UserPreference实体:

@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: 这是我用来进行更新的代码,该类带有@Transactional注释,并且对该方法的每个其他调用(例如,更新用户的角色)都可以正常工作:

@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. 我正在对大约30个其他实体使用相同的更新方法样式,并且一切正常。 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. 这是我唯一的多对多关联,我必须分解为2个一对多关联,因此没有什么可比的。

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". 当我创建具有新HashSet的用户并将其持久化时,该值将正确写入“联接表”中。

*****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 谢谢

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. 这里的主要区别是您没有更改由Hibernate管理的列表引用,而只是向其中添加了元素。

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. 编辑:为清楚起见,它替代了旧的更新方法,因此应像以前一样从客户端使用loggingInUser调用它。

EDIT 2: as noted in the comments merge will update all fields. 编辑2:如注释中所述,合并将更新所有字段。 The old update method also seems to do that? 旧的更新方法似乎也可以做到这一点? Optimistic locks (version numbers) can be used to guard against overwriting other changes by mistake. 乐观锁(版本号)可用于防止错误覆盖其他更改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM