【问题标题】:Using saveOrUpdate() in Hibernate creates new records instead of updating existing ones在 Hibernate 中使用 saveOrUpdate() 创建新记录而不是更新现有记录
【发布时间】:2010-05-18 13:05:55
【问题描述】:

我有一个类用户

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 行。

【问题讨论】:

  • 我已经通过将模型对象保留在会话中解决了这个问题。@SessionAttributes("user")

标签: java database hibernate orm


【解决方案1】:

此代码总是尝试将账单插入数据库,而不是在数据库中存在有关账单的行时更新...

来自 Hibernate 核心文档的 10.7. Automatic state detection 部分:

saveOrUpdate() 执行以下操作:

  • 如果对象已经是持久的 在此会话中,什么都不做
  • 如果另一个对象与 会话具有相同的标识符,抛出 例外
  • 如果对象没有 标识符属性,save()it
  • 如果对象的标识符有值 分配给新实例化的 对象,save()它
  • 如果对象是 由<version> 或 <timestamp>,以及版本 属性值是相同的值 分配给新实例化的 对象,save()它
  • 否则 update()对象

当你这样做时:

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

这个新创建的实例没有分配任何标识符值,saveOrUpdate() 将save() 它,如文档所述。如果这不是您想要的,请将 name 设为主键。

【讨论】:

  • 感谢您的信赖,我明白为什么我的程序总是尝试插入。我怎样才能做到这一点而不使 name 成为主键,因为我有 id 更快更容易。我可以使用这样的额外选择来做到这一点:User bill = new User(); bill.setName("Bill"); bill.setId(func()); 其中 func() 是一个函数,当带有name='Bill' 的行不存在时,结果为空,否则返回该行的 ID 号;没有 select 怎么办?专业开发者如何解决这个问题?
  • @kunkanwan:我不确定我应该如何理解这个“专业开发者”。目前,这不会促使我进一步帮助您。
  • 恐怕你不明白我的意思。我的英语很差。在“专业开发人员”这句话中,我想知道如何在软件行业(我是没有经验的学生)中解决这个问题,因为我的带有额外选择的简单功能还不够好。如果我伤害了你,我很抱歉。
  • @kunkanwan 好的,那没问题。我明天再来回答这个问题。
  • @PascalThivent 但是如果您将名称命名为 PK,您以后可能需要将其作为另一个表的 FK,那么您将在新表中有很多名称,与使用相比,这会获得大量的数据库作为 PK 的 ID
【解决方案2】:

所以你想要:

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

注意到 u1 和 u2 是同一个 Bill 并且只存储一次? Hibernate 无法知道 Bills 是同一个人。它只查看用户 u2 的 id,发现它没有设置,断定应该插入 u2,然后尝试并报告异常。

SaveOrUpdate 持久保存一个全新的对象和当前附加到会话的加载对象。所以这行得通(假设你在某处有一个 findByName 方法并且还有另一个属性,比如说 favoriteColor):

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

但这不是你想要的,对吧?

您的实体具有代理主键和单独的业务键(通过唯一性约束强制执行)使您的问题变得更糟。如果您没有 id 字段并且只有 name 作为主键,您可以使用 merge() (有关 saveOrUpdate 与合并的讨论,look here):

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

但是你没有那个,你需要对 id 和 name 的唯一性强制执行 PK 约束。所以你需要一些合并的变体来做到这一点。非常粗略地说,你会喜欢这些方面的东西:

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

【讨论】:

    【解决方案3】:

    那么,id 没有保存在数据库中?

    如果在 Hibernate 中将其映射为标识符,为什么 Id 不是 de DB 中的主键?

    您为什么不直接在数据库中对“名称”设置唯一约束,并在 Id 上创建主键约束?

    【讨论】:

    • 感谢您的信赖。 id 是主键,name 具有唯一约束 我们有对象类 User 例如:User bill = new User() 以及我们从键盘或网站获得的有关账单的属性,我的问题是:如果账单,您如何制作 saveOrUpdate() 含义.name 存在于数据库中,我们调用 update() 否则我们保存账单。
    【解决方案4】:

    第一个问题:如果您需要获取名为“Bill”的用户。你会怎么做?那应该给你一个想法。

    要更新,您需要有一个与用户对象关联的身份。只设置名称属性不会有帮助。如果您想在没有标识符的情况下进行更新,请使用 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();
    

    【讨论】:

      【解决方案5】:

      你应该使用 session.update(object) 更新时,以及 session.save(object) 在你保存时

      【讨论】:

      • 是的,你是对的,但我希望 hibernate 中的函数 saveOrUpdate() 可以为我完成工作。我可以编写我的 saveOrUpdate() 来检查数据库中是否存在 name 并调用 save() 或 update()。我认为这不是一个好主意,也许 hibernate 有更好的解决方案,例如覆盖 equals(),我们比较来自 bussines 键的元素。
      【解决方案6】:

      如果名称字段是主键。然后,如果您多次设置相同的名称,它将如何创建新记录。 ?可能是您没有正确理解这个概念。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-01-12
        • 2012-07-25
        • 2021-03-30
        • 2011-12-10
        • 2017-04-03
        • 1970-01-01
        • 2020-06-20
        相关资源
        最近更新 更多