在Hibernate中,persist()vs save()的优点是什么?


Answers:


154

从此论坛帖子

persist()定义明确。它使瞬态实例持久化。但是,不能保证将标识符值立即分配给持久实例,分配可能在刷新时发生。规范没有这么说,这是我的问题persist()

persist()还保证如果在事务边界之外调用它,则不会执行INSERT语句。在具有扩展会话/持久性上下文的长时间对话中,这很有用。

persist()需要类似的方法。

save()不能保证相同,它返回一个标识符,并且如果必须执行INSERT来获取标识符(例如“ identity”生成器,而不是“ sequence”),则无论您位于内部还是外部,此INSERT都会立即发生交易。在具有扩展的会话/持久性上下文的长时间对话中,这不好。


44
不幸的是,五年后,该线程仍然是有关此主题的唯一清晰信息来源。Hibernate文档虽然冗长,但除了最琐碎的用法信息之外,几乎没有其他内容。为什么克里斯蒂安的最新文章不在Session javadoc中,这只是另一个Hibernate文档的奥秘。”
kommradHomer 2012年

您是说persist()方法将使实体处于分离状态,而save()处于连接状态?
rekinyz 2015年

2
我最近在一对多双向映射中同时使用了保存和持久性。我发现保存不会级联到孩子,即,只有父级被保存/插入到表中。但是,坚持不懈地完成了在一次通话中保存父母和孩子的任务。我使用的是合成ID,而不是生成的ID。
arn-arn

68

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

Save()

  1. 返回保存后生成的ID。它的Serializable返回类型。
  2. 将更改保存到事务外部的数据库中。
  3. 将生成的ID分配给您要保留的实体
  4. 分离对象的Session.save()将在表中创建新行。

Persist()

  1. 保存后不返回生成的ID。它的void返回类型。
  2. 不将更改保存到事务外部的数据库。
  3. 将分配generated id给您要保留的实体
  4. session.persist()因为分离的对象将被抛出,PersistentObjectException因为它是不允许的。

所有这些都在上尝试过/测试过Hibernate v4.0.1


提到了Save()和Persist()的第3点,但它们实际上并不相同。Persist()方法还将更改保存到事务外部的db中。
Ravi.Kumar '16

2
当我在提交使用持久方法值的事务后进行测试时,未保存到数据库
Laxminarayana Challagonda '16

那么#1和#5两者的真正区别是什么?如果您需要返回ID或创建新行,请使用Save()
user2490003

如何在外部交易中实现Save()#3
vikas singh

24

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

听起来这两种方法在处理瞬态实体时行为相同,而在处理分离实体时则不同。

对于下面的示例,将EmployeeVehicle用作具有PK的实体,其中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,本田)得到相同的结果;

示例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。

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


12

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

long newKey = session.save(myObj);

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

使用persist(),insert语句可以在事务中执行,而不必立即执行。在大多数情况下,这是可取的。

如果不需要插入不随事务顺序发生,并且不需要返回插入的键,请使用persist()。


6

以下差异可以帮助您了解persist和save方法的优点:

  • save和persist之间的第一个区别是它们的返回类型。持久方法的返回类型为void,而保存
    方法的返回类型为Serializable对象。
  • persist()方法不能保证将标识符值立即分配给持久状态,分配可能在刷新时发生。

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

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

  • Hibernate中save和persist方法之间的第五个区别:JPA支持persist,而Hibernate仅支持save。

您可以从Hibernate中的save和persist方法之间区别一文中看到完整的工作示例。


首先,您说“如果在事务边界之外调用persist()方法将不会执行插入查询”。然后您说:“ persist方法在事务边界之外被调用,它在具有扩展Session上下文的长时间对话中很有用。” 他们不是矛盾的吗?我不明白
库玛·马尼什

@KumarManish如果使用persist方法,则在刷新时会发生插入查询。因此,这是长时间对话中的最佳做法
David Pham

5

save()-顾名思义,hibernate save()可用于将实体保存到数据库。我们可以在事务外部调用此方法。如果我们在不使用事务的情况下使用它,并且在实体之间进行级联,那么只有主实体会被保存,除非刷新会话。

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


5

区别在于:

  1. 保存:

    1. 将对象保存到数据库时,将返回ID /标识符。
    2. 当对象在分离后尝试通过打开新会话来尝试执行相同操作时,也会保存。
  2. 坚持:

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

能否请您提供一个带有摘要的示例。那会很有用。
Avikool91 '18 -4-8

5

基本规则说:

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

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

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

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

save():立即返回实体的标识符。由于在调用保存之前已将标识符分配给实体,因此不会立即触发插入。在会话刷新时间将其触发。

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();

persist():

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

对于save():

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();

对于persist():

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之间的其他区别是:

  1. save()可以在事务外部调用。如果使用分配的标识符,则由于id已经可用,因此不会立即触发插入查询。仅在刷新会话时才触发查询。

  2. 如果使用了生成的标识符,则由于需要生成ID,因此会立即触发insert。但这只会保存主要实体。如果该实体具有一些级联实体,那么这些实体此时将不会保存在db中。刷新会话后将保存它们。

  3. 如果persist()在事务之外,则仅在刷新会话时才触发插入,无论使用哪种类型的标识符(生成或分配的)。

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


2

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


1

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

另一个检查是您是否正在执行超出事务限制的操作。因为persist()属于JPA,而save()属于休眠模式。因此,在事务边界之外使用persist()将不允许这样做,并引发与持久性有关的异常。而使用save()则没有这种限制,并且可以通过超出事务限制的save()进行数据库事务。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.