在 Hibernate 中使用 saveOrUpdate() 会创建新记录,而不是更新现有记录。

2022-09-03 03:50:14

我有一个类用户

class User { 
  int id;
  String name;
}

其中 是 数据库中的本机生成器,并且是 DB 中的主键。idUser.hbm.xmlname

在我的数据库中,我保存了一些关于用户的信息。

比我想连接这个关于用户的信息。

例如,在我的数据库中,我有一行INSERT INTO User VALUES ('Bill');

主要.java

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

此代码始终尝试将新行插入到表中,而不是更新现有行。BillBill


答案 1

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

从第10.7节。休眠内核文档的自动状态检测:

saveOrUpdate()执行以下操作:

  • 如果对象已在此会话中持久化,则不执行任何操作
  • 如果与会话关联的另一个对象具有相同的标识符,则引发异常
  • 如果对象没有标识符属性,则 save() it
  • 如果对象的标识符具有分配给新实例化对象的值,则save()
  • 如果对象由 或 进行版本控制,并且版本属性值与分配给新实例化对象的值相同,则<version><timestamp>save()
  • 否则,对象update()

当您执行以下操作时:

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

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


答案 2

所以你想要:

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

注意到u1和u2是相同的账单,只存储一次?Hibernate无法知道Bills是同一个人。它只查看用户 u2 的 id,看到它未设置,得出应该插入 u2 的结论,尝试并报告异常。

SaveOrUpdate 会保留一个全新的对象和一个当前附加到会话的已加载对象。所以这是有效的(假设你在某处有一个findByName方法,并且有另一个属性,让我们说favoryColor):

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

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

由于您的实体具有代理主键和单独的业务键(通过唯一性约束强制执行),您的问题会变得更糟。如果您没有 id 字段,只有名称作为主键,则可以使用 merge() (有关 saveOrUpdate 与 merge 的讨论,请查看此处):

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

但是你没有这个,你需要对id强制实施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;
}

推荐