Hibernate ассоциация «многие ко многим» не обновляет таблицу соединений - PullRequest
0 голосов
/ 30 мая 2018

В моем приложении у меня есть связь «многие ко многим» между сущностями «Пользователь» и «Предпочтения».Поскольку для таблицы объединения требуется дополнительный столбец, мне пришлось разбить его на 2 однозначных ассоциации:

Сущность пользователя:

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

Сущность предпочтения:

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

Сущность 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;
}

Чтобы обновить одно из предпочтений, я перебираю набор предпочтений пользователя и обновляю значение следующим образом:

@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подтвердили, что переменная пользователя, которую я пытаюсь обновить, действительно содержит новое значение после выполнения этого кода.Еще более странно, что значение обновляется на веб-странице, когда происходит перенаправление, но база данных НЕ обновляется!Это код, который я использую для обновления, этот класс помечен @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());
    }
}

Это действуеткак и кеш сессии hibernate имеет обновленное значение, но фактически не сохраняет его.Я использую этот же метод обновления стиля примерно для 30 других объектов, и все работает отлично.Это моя единственная ассоциация «многие ко многим», которую мне пришлось разбить на 2 ассоциации «один ко многим», поэтому мне не с чем сравнивать.

Я что-то не так делаю?Когда я создаю пользователя с новым HashSet и сохраняю его, значение правильно записывается в «таблицу соединений».

***** РЕДАКТИРОВАТЬ *****

Для сравненияэто код, который я использую для создания нового пользователя с настройками по умолчанию.Предпочтения уже существуют, но таблица соединений полностью пуста, и этот код правильно сохраняет сущности:

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);

Спасибо

Ответы [ 2 ]

0 голосов
/ 09 июня 2018

Как насчет использования слияния?В конце концов это то, что вы каскадируете, и вы изменили отдельный объект, и вам нужно объединить изменения:

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

РЕДАКТИРОВАТЬ: для ясности это заменяет старый метод обновления, поэтому его следует вызывать изна стороне клиента с loggedInUser, как и прежде.

РЕДАКТИРОВАТЬ 2: как отмечено в слиянии комментариев, обновит все поля.Старый метод обновления также, кажется, делает это?Оптимистические блокировки (номера версий) могут использоваться для защиты от перезаписи других изменений по ошибке.

0 голосов
/ 06 июня 2018

Вместо

entity.setPreferences(user.getPreferences());

Сделайте что-то вроде:

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

Основное отличие состоит в том, что вы не изменяете ссылку на список, которая управляется Hibernate, и являетсятолько добавляя элементы к нему.

...