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

Posted

技术标签:

【中文标题】Hibernate 中的 session.persist() 和 session.save() 有啥区别?【英文标题】:What's the difference between session.persist() and session.save() in Hibernate?Hibernate 中的 session.persist() 和 session.save() 有什么区别? 【发布时间】:2011-08-17 06:54:36 【问题描述】:

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

【问题讨论】:

参见:Hibernate 中 load() 与 get() 的优势是什么?***.com/questions/5370482/… 这是作者本人 Vlad Mihalcea 迄今为止的latest answers 之一。在挖掘了几个旧文档线程、官方文档以及 Stack Overflow 上的许多变体之后,这是与 sn-ps 一起最好的答案之一。这个link 还包含实体生命周期状态,以防万一。 【参考方案1】:

来自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。【参考方案2】:

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

Save()

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

Persist()

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

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

【讨论】:

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

我做了一些模拟测试来记录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 对象时必须小心使用 save。

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

【讨论】:

【参考方案4】:

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

long newKey = session.save(myObj);

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

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

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

【讨论】:

【参考方案5】:

以下是可以帮助您了解持久化和保存方法的优势的区别:

save 和 persist 之间的第一个区别是它们的返回类型。这 persist 方法的返回类型为 void,而 save 的返回类型为 void 方法是可序列化的对象。

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

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

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

Hibernate 中 save 和 persist 方法的第五个区别: persist 受 JPA 支持,而 save 仅受支持 休眠。

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

【讨论】:

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

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

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

【讨论】:

【参考方案7】:

基本规则是这样说的:

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

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() 在事务之外,则只有在刷新会话时才会触发插入,无论使用哪种标识符(生成或分配)。

    如果对持久对象调用 save,则使用更新查询保存实体。

【讨论】:

【参考方案8】:

这里有区别:

    保存:

      将对象保存到数据库时返回 id/标识符。 在对象被分离后通过打开新会话尝试执行相同操作时也会保存。

    坚持:

      将对象保存到数据库时将返回 void。 在尝试通过新会话保存分离的对象时会抛出 PersistentObjectException。

【讨论】:

你能举个例子吗?那会很有用。【参考方案9】:

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

【讨论】:

这里的生成器类是什么?【参考方案10】:

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

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

【讨论】:

以上是关于Hibernate 中的 session.persist() 和 session.save() 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

HIbernate——hibernate中的各种查询

hibernate中的查询方式都有哪些?

Hibernate-ORM:07.Hibernate中的参数绑定

(N)Hibernate 中的一级和二级缓存是啥?

Hibernate中的五大核心类和接口

Hibernate-ORM:11.Hibernate中的关联查询