ChatGPT解决这个技术问题 Extra ChatGPT

Hibernate 中的 session.persist() 和 session.save() 有什么区别?

谁能告诉我在 Hibernate 中 persist()save() 的优势是什么?

另请参阅:Hibernate 中 load() 与 get() 的优势是什么?stackoverflow.com/questions/5370482/…
这是作者本人 Vlad Mihalcea 迄今为止的 latest answers 之一。在挖掘了几个旧的文档线程、官方文档以及 Stack Overflow 上的许多变体之后,这是与片段一起最好的答案之一。此 link 还包含实体生命周期状态,以备不时之需。

A
Andrew Tobilko

来自this forum post

persist() 定义明确。它使瞬态实例持久化。但是,它不能保证标识符值将立即分配给持久实例,分配可能发生在刷新时间。规范没有这么说,这是我对persist() 的问题。 persist() 还保证如果在事务边界之外调用它,它不会执行 INSERT 语句。这在具有扩展会话/持久性上下文的长时间运行的对话中很有用。需要像 persist() 这样的方法。 save() 不保证相同,它返回一个标识符,如果必须执行 INSERT 以获取标识符(例如“身份”生成器,而不是“序列”),则无论您是否在里面,此 INSERT 都会立即发生或交易之外。这在具有扩展会话/持久性上下文的长期对话中不好。


从同一篇文章中添加更多内容,抱怨:“可悲的是,5 年后,这个线程仍然是关于这个主题的唯一明确的信息来源。Hibernate 文档虽然冗长,但除了最琐碎的使用信息外,没有任何内容。为什么 christian 的最后一篇文章不在 Session javadoc 中,这只是另一个 Hibernate 文档之谜。”
您的意思是persist() 方法将使实体处于分离状态,而save() 处于附加状态?
我最近在一对多的双向映射中同时使用了保存和持久化。我发现 save 不会级联到孩子,即只有 Parent 被保存/插入到表中。但是,persist 一次调用就完成了保存 Parent 和 Child 的任务。我使用的是复合 ID,而不是生成的 ID。
G
Giorgi Tsiklauri

我对 save() 与 persist() 做了很好的研究,包括在我的本地机器上运行它几次。前面的所有解释都是令人困惑和不正确的。经过深入研究,我比较了下面的 save() 和 persist() 方法。

Save()

保存后返回生成的 Id。它的返回类型是可序列化的;在事务之外保存对数据库的更改;将生成的 id 分配给您要持久化的实体;分离对象的 session.save() 将在表中创建一个新行。

Persist()

保存后不返回生成的 ID。它的返回类型是 void;不将更改保存到事务之外的数据库;将生成的 Id 分配给您要持久化的实体;分离对象的 session.persist() 将抛出 PersistentObjectException,因为这是不允许的。

所有这些都在 Hibernate v4.0.1 上进行了尝试/测试。


提到了 Save() 和 Persist() 的第 3 点,但它们实际上并不相同。 Persist() 方法也将更改保存到事务之外的 db。
当我使用持久方法提交事务后进行测试时,值未保存到数据库
那么#1和#5是两者之间的真正区别吗?如果您需要返回 id 或创建新行,请使用 Save()
Save() #3 如何在事务之外实现
Z
Zeus

我做了一些模拟测试来记录 save()persist() 之间的区别。

听起来这两种方法在处理瞬态实体时表现相同,但在处理分离实体时不同。

对于以下示例,将 EmployeeVehicle 作为实体,PK 为 vehicleId,这是一个生成的值,vehicleName 作为其属性之一。

示例 1:处理瞬态对象

Session session = factory.openSession();
session.beginTransaction();
EmployeeVehicle entity = new EmployeeVehicle();
entity.setVehicleName("Honda");
session.save(entity);
// session.persist(entity);
session.getTransaction().commit();
session.close();

结果:

select nextval ('hibernate_sequence') // This is for vehicle Id generated : 36
insert into Employee_Vehicle ( Vehicle_Name, Vehicle_Id) values ( Honda, 36)

请注意,当您获得一个已经持久化的对象并保存它时,结果是相同的

EmployeeVehicle entity =  (EmployeeVehicle)session.get(EmployeeVehicle.class, 36);
entity.setVehicleName("Toyota");
session.save(entity);    -------> **instead of session.update(entity);**
// session.persist(entity);

使用 persist(entity) 重复相同的操作,结果与新 ID 相同(例如 37,honda);

示例 2:处理分离的对象

// Session 1 
// Get the previously saved Vehicle Entity 
Session session = factory.openSession();
session.beginTransaction();
EmployeeVehicle entity = (EmployeeVehicle)session.get(EmployeeVehicle.class, 36);
session.close();

// Session 2
// Here in Session 2 , vehicle entity obtained in previous session is a detached object and now we will try to save / persist it 
// (i) Using Save() to persist a detached object 
Session session2 = factory.openSession();
session2.beginTransaction();
entity.setVehicleName("Toyota");
session2.save(entity);
session2.getTransaction().commit();
session2.close();

结果:您可能期望在先前会话中获得的具有 id : 36 的车辆已更新为名称为 "Toyota" 。但是发生的情况是,一个新实体被保存在数据库中,新的 ID 为生成,名称为“Toyota”

select nextval ('hibernate_sequence')
insert into Employee_Vehicle ( Vehicle_Name, Vehicle_Id) values ( Toyota, 39)

使用 persist 持久化分离的实体

// (ii) Using Persist()  to persist a detached
// Session 1 
Session session = factory.openSession();
session.beginTransaction();
EmployeeVehicle entity = (EmployeeVehicle)session.get(EmployeeVehicle.class, 36);
session.close();

// Session 2
// Here in Session 2 , vehicle entity obtained in previous session is a detached object and now we will try to save / persist it 
// (i) Using Save() to persist a detached
Session session2 = factory.openSession();
session2.beginTransaction();
entity.setVehicleName("Toyota");
session2.persist(entity);
session2.getTransaction().commit();
session2.close();

结果:

Exception being thrown : detached entity passed to persist

因此,使用 Persist() 而不是 Save() 总是更好,因为在处理 Transient object 时必须小心使用 save。

重要提示:在上面的例子中,车辆实体的pk是一个生成的值,所以当使用save()来持久化一个分离的实体时,hibernate会生成一个新的id来持久化。但是,如果此 pk 不是生成的值,则会导致异常声明违反密钥。


C
Community

This question 对 Hibernate 中的不同持久性方法有一些很好的答案。要直接回答您的问题,无论事务状态如何,使用 save() 插入语句都会立即执行。它返回插入的密钥,因此您可以执行以下操作:

long newKey = session.save(myObj);

因此,如果您需要立即分配给持久实例的标识符,请使用 save()。

使用persist(),插入语句在事务中执行,不一定立即执行。在大多数情况下,这是更可取的。

如果您不需要插入与事务无序发生并且您不需要返回插入的密钥,请使用 persist()。


y
yeppe

以下是可以帮助您了解持久和保存方法的优点的差异:

save 和 persist 之间的第一个区别是它们的返回类型。 persist 方法的返回类型是 void,而 save 方法的返回类型是 Serializable 对象。

persist() 方法不保证标识符值将立即分配给持久状态,分配可能发生在刷新时间。

如果在事务边界之外调用persist() 方法,则不会执行插入查询。同时, save() 方法返回一个标识符,以便立即执行插入查询以获取标识符,无论它是在事务内部还是外部。

persist 方法在事务边界之外调用,它在具有扩展 Session 上下文的长时间运行的对话中很有用。另一方面,保存方法在具有扩展会话上下文的长时间运行的对话中并不好。

Hibernate中save和persist方法的第五个区别:JPA支持persist,而只有Hibernate支持save。

您可以从帖子 Difference between save and persist method in Hibernate 中查看完整的工作示例


首先,您说“如果在事务边界之外调用persist() 方法将不会执行插入查询”。然后你说“persist 方法是在事务边界之外调用的,它在具有扩展 Session 上下文的长时间运行的对话中很有用。”他们不矛盾吗?我不明白。
@KumarManish 在持久化方法的情况下,插入查询会在刷新时发生。所以这是长期对话的最佳实践
R
Rohit Goyal

save() - 正如方法名称所暗示的,hibernate save() 可用于将实体保存到数据库。我们可以在事务之外调用这个方法。如果我们在没有事务的情况下使用它并且我们在实体之间有级联,那么除非我们刷新会话,否则只有主实体会被保存。

persist()-Hibernate 持久化类似于保存(使用事务),它将实体对象添加到持久化上下文中,因此可以跟踪任何进一步的更改。如果在提交事务或刷新会话之前更改了对象属性,它也会保存到数据库中。此外,我们只能在事务的边界内使用 persist() 方法,因此它是安全的并且可以处理任何级联对象。最后,persist 不会返回任何内容,因此我们需要使用持久化对象来获取生成的标识符值。


G
Gaurav Kumar

基本规则说:

对于具有生成标识符的实体:

save() :除了使对象持久化之外,它还立即返回实体的标识符。因此立即触发插入查询。

persist() :它返回持久对象。它没有立即返回标识符的任何强制要求,因此它不保证立即触发插入。它可能会立即触发插入,但不能保证。在某些情况下,查询可能会立即触发,而在其他情况下,它可能会在会话刷新时触发。

对于具有指定标识符的实体:

save():它立即返回一个实体的标识符。由于在调用 save 之前标识符已经分配给实体,因此不会立即触发 insert。它在会话刷新时触发。

persist() :与保存相同。它还在冲洗时触发插入。

假设我们有一个使用生成标识符的实体,如下所示:

@Entity
@Table(name="USER_DETAILS")
public class UserDetails {
    @Id
    @Column(name = "USER_ID")
    @GeneratedValue(strategy=GenerationType.AUTO)
    private int userId;

    @Column(name = "USER_NAME")
    private String userName;

    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

节省() :

    Session session = sessionFactory.openSession();
    session.beginTransaction();
    UserDetails user = new UserDetails();
    user.setUserName("Gaurav");
    session.save(user); // Query is fired immediately as this statement is executed.
    session.getTransaction().commit();
    session.close();

坚持():

    Session session = sessionFactory.openSession();
    session.beginTransaction();
    UserDetails user = new UserDetails();
    user.setUserName("Gaurav");
    session.persist(user); // Query is not guaranteed to be fired immediately. It may get fired here.
    session.getTransaction().commit(); // If it not executed in last statement then It is fired here.
    session.close();

现在假设我们定义了相同的实体,但没有生成注释的 id 字段,即手动分配 ID。

@Entity
@Table(name="USER_DETAILS")
public class UserDetails {
    @Id
    @Column(name = "USER_ID")
    private int userId;

    @Column(name = "USER_NAME")
    private String userName;

    public int getUserId() {
        return userId;
    }
    public void setUserId(int userId) {
        this.userId = userId;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
}

对于保存():

Session session = sessionFactory.openSession();
session.beginTransaction();
UserDetails user = new UserDetails();
user.setUserId(1);
user.setUserName("Gaurav");
session.save(user); // Query is not fired here since id for object being referred by user is already available. No query need to be fired to find it. Data for user now available in first level cache but not in db.
session.getTransaction().commit();// Query will be fired at this point and data for user will now also be available in DB
session.close();

对于坚持():

Session session = sessionFactory.openSession();
session.beginTransaction();
UserDetails user = new UserDetails();
user.setUserId(1);
user.setUserName("Gaurav");
session.persist(user); // Query is not fired here.Object is made persistent. Data for user now available in first level cache but not in db.
session.getTransaction().commit();// Query will be fired at this point and data for user will now also be available in DB
session.close();

当从事务中调用 save 或 persist 时,上述情况是正确的。

save 和 persist 之间的其他区别是:

save() 可以在事务之外调用。如果使用分配的标识符,那么由于 id 已经可用,因此不会立即触发插入查询。只有在刷新会话时才会触发查询。如果使用生成的标识符,则由于需要生成 id,因此立即触发 insert。但它只保存了主要实体。如果实体有一些级联实体,那么此时这些实体将不会保存在 db 中。当会话刷新时,它们将被保存。如果persist() 在事务之外,则只有在刷新会话时才会触发插入,无论使用哪种标识符(生成或分配)。如果通过持久对象调用保存,则使用更新查询保存实体。


D
Donald Duck

这是区别:

save:将对象保存到数据库时返回 id/标识符。当对象被分离后通过打开一个新会话尝试执行相同操作时,它也会保存。 Persist:将对象保存到数据库时将返回 void。尝试通过新会话保存分离的对象时将抛出 PersistentObjectException。


能否请您展示一个带有片段的示例。那会很有用。
J
Jabongg

实际上,hibernate save() 和 persist() 方法之间的区别取决于我们使用的生成器类。如果我们的生成器类被分配,那么 save() 和 persist() 方法之间没有区别。因为生成器'assigned'的意思是,作为一个程序员,我们需要给主键值以保存在数据库中[希望你知道这个生成器的概念]如果不是分配的生成器类,假设我们的生成器类名称是增量意味着hibernate 自己会把主键 id 值赋给数据库的权利【除了指定的生成器,hibernate 只用来照顾主键 id 值记住】,所以在这种情况下如果我们调用 save() 或 persist() 方法,那么它会正常将记录插入数据库。但在这里,save() 方法可以返回由 hibernate 生成的主键 id 值,我们可以通过 long s = session.save(k); 看到它。在同样的情况下,persist() 永远不会将任何值返回给客户端,返回类型为 void。 persist() 还保证如果在事务边界之外调用它,它不会执行 INSERT 语句。而在 save() 中,INSERT 会立即发生,无论您是在事务内部还是事务外部。


这里的生成器类是什么?
N
Neeraj

在存储任何实体时,它完全根据 ID 中的“生成器”类型来回答。如果生成器的值是“已分配”,这意味着您正在提供 ID。然后它在休眠中没有差异以进行保存或持久化。你可以用任何你想要的方法。如果值未“分配”并且您正在使用 save() ,那么您将获得 ID 作为 save() 操作的返回值。

另一项检查是您是否在交易限制之外执行操作。因为persist() 属于JPA,而save() 属于休眠。因此,在事务边界之外使用 persist() 将不允许这样做并抛出与持久性相关的异常。而使用 save() 则没有此类限制,并且可以在事务限制之外通过 save() 进行 DB 事务。