Почему Hibernate пытается удалить, когда я пытаюсь обновить / вставить? - PullRequest
20 голосов
/ 07 октября 2008

В моем приложении у меня есть эти типы, отображаемые в Hibernate (общий случай):

class RoleRule {
  private Role role;
  private PermissionAwareEntity entity; // hibernate-mapped entity for which permission is granted
  private PermissionType permissionType; // enum

  @ManyToOne
  @JoinColumn(name = "ROLE_ID")
  public Role getRole() {
    return role;
  }
  public void setRole(Role role) {
    this.role = role;
  }

}

class Role {
  private Set<RoleRule> rules = new HashSet<RoleRule>(0);

  @OneToMany(cascade=CascadeType.ALL)
  @JoinColumn(name="ROLE_ID")
  public Set<RoleRule> getRules() {
    return rules;
  }
  public void setRules(Set<RoleRule> rules) {
    this.rules = rules;
  }

}

Все классы имеют equals() & hashCode() переопределения.

Мое приложение позволяет настраивать роли (только для системных администраторов, не волнуйтесь), а также среди других полей позволяет создавать новые правила ролей. Когда создается новое правило, я пытаюсь создать новый объект RoleRule и вставить его в поле роли rules. Я звоню session.update(role), чтобы применить изменения к базе данных.

Теперь наступает ужасная часть ... Hibernate решает сделать следующее при закрытии транзакции и сбросе:

  1. Вставить новое правило в базу данных. Отлично.
  2. Обновите другие поля ролей (не коллекции). Пока все хорошо.
  3. Обновите существующие правила, даже если в них ничего не изменилось. Я могу жить с этим.
  4. Обновите существующие правила , снова . Вот вставка из журнала, включая автоматический комментарий:
/* delete one-to-many row Role.rules */
update ROLE_RULE set ROLE_ID=null where ROLE_ID=? and ROLE_RULE_ID=?

Конечно, все поля не равны NULL, и эта операция завершается неудачно.

Может кто-нибудь попытаться объяснить, почему Hibernate сделал бы это ??? И что еще важнее, как мне это обойти?

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

Мне все еще очень любопытен мой вопрос. Кто-нибудь может подсказать, почему Hibernate сделал бы это?

Ответы [ 4 ]

7 голосов
/ 07 октября 2008

Я обычно использовал два метода обновления коллекции (многие стороны одного ко многим) в Hibernate. Метод грубой силы - очистить коллекцию, вызвать save для родителя, а затем вызвать flush. Затем добавьте обратно всех членов коллекции и снова вызовите save для родителя. Это удалит все и затем вставит все. Сброс в середине - это ключ, потому что он вызывает удаление перед вставкой. Вероятно, лучше всего использовать этот метод только в небольших коллекциях, поскольку он повторно вставляет их все.

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

copy the list of existing records to a list_to_delete
for each record from the form
   remove it from the list_to_delete
   if the record exists (based on equals()?  key?)
     change each field that the user can enter
   else if the record doesn't exist
     add it to the collection
end for
for each list_to_delete
  remove it
end for
save

Я искал форумы Hibernate в течение многих часов, пытаясь найти правильный способ решения этой проблемы. Вы должны иметь возможность просто обновить свою коллекцию, чтобы сделать ее точной, а затем сохранить родительский элемент, но, как вы узнали, Hibernate пытается отсоединить дочерние элементы от родительского, прежде чем удалять их, и если внешний ключ не равен нулю, потерпит неудачу.

5 голосов
/ 03 июля 2009

См. Ответ на вопрос ' Переопределение equals и hashCode в Java '.

В нем объясняется, как переопределить методы equals и hashCode, которые, по-видимому, были вашей проблемой, так как работали после их перезаписи.

Неправильное их переопределение может привести к тому, что Hibernate удалит ваши коллекции и вставит их заново. (поскольку ключ хеша используется в качестве ключей на картах)

3 голосов
/ 07 октября 2008

Я не уверен, что это решение, но вы можете попробовать:

@OneToMany(mappedBy = "role")

А нет аннотации @JoinColumn? Я думаю, что обе сущности пытаются «владеть» ассоциацией, поэтому SQL может быть испорчен?

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

@Entity
@org.hibernate.annotations.Entity(
    dynamicInsert = true, dynamicUpdate = true
)
0 голосов
/ 05 марта 2018

Ответ Брайана Детерлинга помог мне преодолеть фантомное удаление. Я бы хотел, чтобы он поставил настоящий код. Вот что я получил из его предложения 1. Публикация сообщений для того, чтобы кто-то использовал их или комментировал мой код.

// snFile and task share many to many relationship

@PersistenceContext
private EntityManager em;

public SnFile merge(SnFile snFile) {
        log.debug("Request to merge SnFile : {}", snFile);

        Set<Task> tasks = taskService.findBySnFilesId(snFile.getId());
        if(snFile.getTasks() != null) {
            snFile.getTasks().clear();
        }
        em.merge(snFile);
        em.flush();
        if(tasks != null) {
            if(snFile.getTasks() != null)
                snFile.getTasks().addAll(tasks);
            else
                snFile.setTasks(tasks);
        }

        return em.merge(snFile);
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...