Answers:
从此论坛帖子
persist()
定义明确。它使瞬态实例持久化。但是,不能保证将标识符值立即分配给持久实例,分配可能在刷新时发生。规范没有这么说,这是我的问题persist()
。
persist()
还保证如果在事务边界之外调用它,则不会执行INSERT语句。在具有扩展会话/持久性上下文的长时间对话中,这很有用。
persist()
需要类似的方法。
save()
不能保证相同,它返回一个标识符,并且如果必须执行INSERT来获取标识符(例如“ identity”生成器,而不是“ sequence”),则无论您位于内部还是外部,此INSERT都会立即发生交易。在具有扩展的会话/持久性上下文的长时间对话中,这不好。
我对save()与persist()进行了很好的研究,包括在本地计算机上运行了几次。前面的所有解释都是令人困惑的,而且是不正确的。经过深入研究,我在下面比较了save()和persist()。
Save()
Serializable
返回类型。Persist()
generated id
给您要保留的实体session.persist()
因为分离的对象将被抛出,PersistentObjectException
因为它是不允许的。所有这些都在上尝试过/测试过Hibernate v4.0.1
。
Save()
?
我做了一些模拟测试记录之间的差别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不是生成的值,则将导致异常,说明密钥违反。
以下差异可以帮助您了解persist和save方法的优点:
persist()方法不能保证将标识符值立即分配给持久状态,分配可能在刷新时发生。
如果在事务边界之外调用persist()方法将不会执行插入查询。同时,save()方法返回一个标识符,以便立即执行插入查询以获取该标识符,而不管它是在事务内部还是外部。
persist方法在事务边界之外被调用,它在具有扩展Session上下文的长时间对话中很有用。另一方面,在具有扩展Session上下文的长时间对话中,保存方法不是很好。
Hibernate中save和persist方法之间的第五个区别:JPA支持persist,而Hibernate仅支持save。
您可以从Hibernate中的save和persist方法之间的区别一文中看到完整的工作示例。
save()-顾名思义,hibernate save()可用于将实体保存到数据库。我们可以在事务外部调用此方法。如果我们在不使用事务的情况下使用它,并且在实体之间进行级联,那么只有主实体会被保存,除非刷新会话。
persist()-Hibernate持久性类似于保存(带有事务),它将实体对象添加到持久性上下文中,因此可以跟踪任何进一步的更改。如果在提交事务或刷新会话之前更改了对象属性,则该对象属性还将保存到数据库中。另外,我们只能在事务的边界内使用persist()方法,因此它是安全的,并且可以处理所有级联的对象。最后,persist不返回任何内容,因此我们需要使用persisted对象来获取生成的标识符值。
区别在于:
保存:
坚持:
基本规则说:
对于具有生成的标识符的实体:
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之间的其他区别是:
save()可以在事务外部调用。如果使用分配的标识符,则由于id已经可用,因此不会立即触发插入查询。仅在刷新会话时才触发查询。
如果使用了生成的标识符,则由于需要生成ID,因此会立即触发insert。但这只会保存主要实体。如果该实体具有一些级联实体,那么这些实体此时将不会保存在db中。刷新会话后将保存它们。
如果persist()在事务之外,则仅在刷新会话时才触发插入,无论使用哪种类型的标识符(生成或分配的)。
如果通过持久对象调用save,则使用更新查询保存实体。
实际上,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都会立即发生。