在 Hibernate 中,session.keep ()和 session.save ()的区别是什么?

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

234358 次浏览

来自 这个论坛帖子

persist()定义明确 暂时实例持久化。 但是,它并不能保证 标识符值将分配给 立即执行持久的实例, 任务可能在同花顺时完成 时间,说明书上没说 就是 persist()的问题。

persist()也保证它会 不执行 INSERT 语句 在交易之外被称为 这在 长时间的对话 扩展的会话/持久化上下文。

需要类似 persist()的方法。

save()不保证相同,它 返回一个标识符,如果 必须执行 INSERT 才能获取 标识符(例如「标识」生成器、, 而不是“序列”) ,则执行此插入操作 马上,不管你是不是 交易内部或外部。 这不是一个好的长期运行 与扩展的 会话/持久化上下文。

关于 Hibernate 中不同的持久化方法,这个问题 有一些很好的答案。为了直接回答您的问题,不管事务状态如何,使用 save ()都会立即执行插入语句。它返回插入的键,所以你可以这样做:

long newKey = session.save(myObj);

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

对于 keep () ,插入语句是在事务中执行的,不一定是立即执行的。

如果您不需要插入操作与事务发生顺序不一致,并且不需要返回插入的键,那么可以使用 keep ()。

实际上,hibernate save ()和 keep ()方法之间的区别取决于我们使用的生成器类。
如果我们的生成器类已经分配,那么 save ()和 keep ()方法之间没有区别。因为生成器“分配”意味着,作为一个程序员,我们需要将主键值保存在数据库中[希望您知道生成器的概念] 如果不是指定的生成器类,假设我们的生成器类名为 Increment 意味着 hibernate 本身将把主键 id 值分配给数据库权限[除了指定的生成器,hibernate 只用于处理主键 id 值 remember ] ,因此在这种情况下,如果我们调用 save ()或者 keep ()方法,那么它将正常地将记录插入数据库。
但是在这里,save ()方法可以返回由 hibernate 生成的主键 id 值,我们可以通过
Long s = session. save (k) ;
在同样的情况下,keep ()不会向客户端返回任何值,返回 void 类型。
还保证,如果在事务边界之外调用它,它将不执行 INSERT 语句。
而在 save ()中,INSERT 立即发生,无论您是在事务内还是在事务外。

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

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

对于下面的示例,将 EmployeeVehicle 作为一个实体,PK 作为 vehicleIdvehicleId是一个生成的值,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)重复同样的操作,使用 new 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获得上一届更新的名称为“丰田”。但是会发生的情况是,一个新的实体保存在 DB 中,新 ID 生成为“ Toyota”,名称为“ Toyota”

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

使用持久化来持久化分离的实体

// (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

因此,最好使用 Perist ()而不是 Save () ,因为在处理瞬态对象时必须谨慎使用保存。

重要提示: 在上面的示例中,vehicle 实体的 pk 是一个生成的值,因此当使用 save ()来持久化一个分离的实体时,hibernate 生成一个新的 id 来持久化。但是,如果这个 pk 不是一个生成的值,那么它将导致异常声明键违反。

基本规则是:

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

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

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

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

Save () : 它立即返回实体的标识符。因为标识符在调用 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;
}
}

保存() :

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

当从事务内部调用保存或持久化时,上述情况是正确的。

保存和持久化之间的其他不同点是:

  1. 可以在事务外部调用 save ()。如果使用指定的标识符,那么因为 id 已经可用,所以不会立即触发任何插入查询。只有在刷新会话时才触发查询。

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

  3. 如果 keep ()位于事务之外,那么只有在刷新会话时才会触发插入,而不管使用什么类型的标识符(生成或分配)。

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

我已经对 save ()和 keep ()做了很好的研究,包括在我的本地机器上多次运行它。以前所有的解释都是混乱和不正确的。在深入研究之后,我比较了下面的 save ()和 keep ()方法。

Save()

  1. 保存后返回生成的 Id。其返回类型为 Serializable;
  2. 在事务之外保存对数据库的更改;
  3. 将生成的 id 分配给正在持久化的实体;
  4. 分离对象的 session.save()将在表中创建一个新行。

Persist()

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

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

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

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

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

  • 保存和持久化之间的第一个区别是它们的返回类型 保存方法的返回类型为空,而保存方法的返回类型为
    方法是可序列化的对象。
  • 方法并不保证标识符值将 被立即分配到持久状态,分配可能 发生在冲水时间

  • 如果方法被调用,将不执行插入查询 而 save ()方法返回 一个标识符,以便立即执行插入查询来获取 标识符,不管它是在 交易

  • 持久化方法在事务边界之外调用,它是 在具有扩展会话的长时间运行会话中非常有用 另一方面,保存方法在长时间运行时不好 使用扩展会话上下文进行对话

  • Hibernate 中保存和持久化方法的第五个不同之处: 持久化是支持的 JPA,而保存只支持 冬眠

您可以从后面的 Hibernate 中保存和持久化方法的区别中看到完整的工作示例

区别在于:

  1. 保存:

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

    1. 将在对象保存到数据库时返回 void。
    2. 将在尝试通过新会话保存已分离对象时引发 PersisentObjectException。

在存储任何实体时,它完全根据 ID 中的“生成器”类型进行响应。 如果为生成器分配了值,则表示您提供了 ID。然后在休眠状态下进行保存或持久化。你想用什么方法都行。如果值没有“分配”,并且您正在使用 save () ,那么您将从 save ()操作中获得 ID 作为返回值。

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