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

时间:2011-05-02 21:27:54

标签: java hibernate

有人能告诉我Hibernate中persist() vs save()的优势是什么?

10 个答案:

答案 0 :(得分:144)

来自this forum post

  

persist()定义明确。它成了一个   瞬态实例持久化。   但是,它并不能保证   标识符值将分配给   持久化实例,   分配可能发生在同花顺   时间。规范没有说,哪个   是persist()的问题。

     

persist()也保证会   如果它不执行INSERT语句   在交易之外被称为   边界。这很有用   与...长期对话   扩展的会话/持久化上下文。

     

需要persist()之类的方法。

     

save()不保证相同,它   返回一个标识符,如果是   必须执行INSERT才能获得   标识符(例如“身份”生成器,   不是“序列”),这个INSERT发生了   不管你是不是立刻   交易内部或外部。   这在长期运行中并不好   与扩展的对话   会话/持久化上下文。

答案 1 :(得分:57)

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

Save()

  1. 保存后返回生成的ID。其Serializable返回类型。
  2. 立即将值保存到DB并跟踪实体直到会话结束(我试图在事务之外更改实体值,当会话提交时它没有显示任何效果)
  3. 将更改保存到事务外部的数据库中。
  4. 将生成的ID分配给您要保留的实体
  5. 分离对象的Session.save()将在表中创建一个新行。
  6. Persist()

    1. 保存后不返回生成的ID。它的返回类型。
    2. 立即将值保存到DB并跟踪实体直到会话结束。(我试图在事务之外更改实体值,在会话提交时它没有显示任何效果)
    3. 不保存对事务外部数据库的更改。
    4. generated id分配给您要保留的实体
    5. session.persist()对于分离的对象将抛出PersistentObjectException,因为它是不允许的。
    6. 所有这些都在Hibernate v4.0.1上进行了尝试/测试。

答案 2 :(得分:23)

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

听起来这两种方法在处理Transient Entity时表现相同,但在处理Detached Entity时有所不同。

对于以下示例,将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,本田);

示例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,名称为“丰田”

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不是生成的值,则会导致异常表明密钥被违反。

答案 3 :(得分:12)

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

long newKey = session.save(myObj);

如果需要立即为持久化实例分配标识符,请使用save()。

使用persist(),insert语句在事务中执行,不一定立即执行。在大多数情况下这是优选的。

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

答案 4 :(得分:5)

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

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

答案 5 :(得分:5)

这些差异可以帮助您获得持久化和保存方法的优势:

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

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

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

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

您可以在帖子Difference between save and persist method in Hibernate

中看到完整的工作示例

答案 6 :(得分:4)

区别在于:

  1. 保存:

    1. 将在将对象保存到数据库时返回id / identifier。
    2. 也会在尝试通过在分离后打开新会话来保存对象时保存。
  2. 坚持:

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

答案 7 :(得分:2)

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

答案 8 :(得分:1)

基本规则说:

对于生成标识符的实体:

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

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

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

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

persist():与save相同。它也会在冲洗时触发插入。

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

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

save():

    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.save(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;
    }
}

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

for 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,因此会立即触发插入。但它只保存了主要实体。如果实体具有某些级联实体,则此时不会将这些实体保存在db中。在刷新会话时,它们将被保存。

  3. 如果persist()在事务之外,则只有在刷新会话时才会触发insert,无论使用何种标识符(生成或分配)。

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

答案 9 :(得分:1)

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

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