Использование saveOrUpdate () в Hibernate создает новые записи вместо обновления существующих - PullRequest
15 голосов
/ 18 мая 2010

У меня есть класс User

class User { 
  int id;
  String name;
}

, где id - собственный генератор в User.hbm.xml, а name - первичный ключ в БД.

В своей базе данных я сохранил некоторую информацию о пользователях.

Чем я хочу связать с этой информацией о Пользователе.

Например, в моей БД у меня есть строка INSERT INTO User VALUES ('Bill');

Main.java

User bill = new User();
bill.setName("Bill");
session.saveOrUpdate(bill);

Этот код всегда пытается вставить новую строку Bill в таблицу, а не обновлять существующую строку Bill.

Ответы [ 6 ]

29 голосов
/ 18 мая 2010

Этот код всегда пытается вставить счет в базу данных, а не обновлять, когда в БД существует строка о счете ...

Из раздела 10.7. Автоматическое определение состояния документации ядра Hibernate:

saveOrUpdate() выполняет следующие действия:

  • если объект уже является постоянным в этом сеансе ничего не делать
  • если другой объект связан с сессия имеет тот же идентификатор, бросить исключение
  • если объект не имеет Идентификатор свойства, save() it
  • если идентификатор объекта имеет значение назначен на новый экземпляр объект, save() это
  • если объект версия <version> или <timestamp> и версия значение свойства такое же значение назначен на новый экземпляр объект, save() это
  • в противном случае update() объект

Когда вы делаете:

User bill = new User();
bill.setName("Bill");
session.saveOrUpdate(bill);

Для этого вновь созданного экземпляра не назначено значение идентификатора, и saveOrUpdate() будет save(), как описано в документации. Если это не то, что вам нужно, сделайте name первичным ключом.

15 голосов
/ 18 мая 2010

Итак, вы хотите:

User u1 = new User("Bill");
session.save(u1);
User u2 = new User("Bill");
session.saveOrUpdate(u2);

чтобы заметить, что u1 и u2 - это один и тот же Билл, и хранить его только один раз? Hibernate не может знать, что счета являются одним и тем же человеком. Он только смотрит на идентификатор пользователя u2, видит, что он не установлен, приходит к выводу, что u2 должен быть вставлен, пробует это и сообщает об исключении.

SaveOrUpdate сохраняет как совершенно новый объект, так и загруженный объект, который в данный момент присоединен к сеансу. Так что это работает (при условии, что у вас есть метод findByName где-то и есть другой атрибут, скажем, favouriteColor):

User u1 = new User("Bill");
u1.setFavoriteColor("blue");
session.saveOrUpdate(u1);
User u2 = findByName("Joe");
u2.setFavoriteColor("red");
session.saveOrUpdate(u2);

Но это не то, что вы хотите, верно?

Ваша проблема усугубляется тем, что ваша сущность имеет суррогатный первичный ключ и, отдельно, бизнес-ключ (применяется через ограничение уникальности). Если у вас нет поля id и только имя в качестве первичного ключа, вы можете использовать merge () (для обсуждения saveOrUpdate против слияния, посмотрите здесь ):

User u1 = new User("Bill");
u1.setFavoriteColor("blue");
session.save(u1);
User u2 = new User("Bill");
u2.setFavoriteColor("red");
u2 = session.merge(u2);

Но у вас этого нет, вам нужно применить ограничение PK для id и уникальность для имени. Так что вам понадобится несколько вариантов слияния, которые это делают. Очень грубо, вы хотели бы что-то вроде этого:

public User mergeByName(User user) {
    User merged;
    User candidate = findByName(user.getName());
    if (candidate != null) {
        user.setId(candidate.getId());
        merged = session.merge(user);
    } else {
        session.save(user);
        merged = user;
    }
    return merged;
}
1 голос
/ 18 мая 2010

Первый вопрос: если вам нужно выбрать пользователя с именем «Билл».Как бы Вы это сделали?Это должно дать вам представление.

Для обновления вам необходимо иметь идентификатор, связанный с объектом пользователя.Наличие только атрибута name не поможет.Если вы хотите обновить независимо от идентификатора, используйте HQL в качестве запроса.

Query query = session.createQuery("update User u set u.name = :newName where u.name=:name");
query.setString("name", "Bill");
query.setString("newName", "John");
query.executeUpdate();
1 голос
/ 18 мая 2010

Итак, идентификатор не сохраняется в базе данных?

Почему Id не является первичным ключом в DB, ​​если вы отображаете его в Hibernate как идентификатор?

Почему вы просто не наложили уникальное ограничение на имя в базе данных и не создали ограничение первичного ключа для идентификатора?

0 голосов
/ 17 октября 2014

Если поле имени является первичным ключом. Затем, если вы установите одно и то же имя более одного раза, как это создаст новую запись. Может быть, вы не понимаете концепции правильно.

0 голосов
/ 18 мая 2010

Вы должны использовать session.update (объект) во время обновления, и session.save (объект) во время сохранения

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...