冬眠动物园图片来源:物体的生命状态之旅

欢迎来到冬眠动物园!今天我们有一个令人兴奋的介绍:认识持久性上下文!这个开朗勤奋的小伙伴是您的数据库数据的超级英雄,也是知道如何处理棘手的连接和对象的骗子。为了感觉自己像一个真正的探险家,别忘了戴上你的 Java 虚拟眼镜,我们会在途中将代码交给你。

幕后的秘密:什么是持久性上下文?

**持久性上下文**实际上是 Hibernate 在使用数据库时跟踪的所有对象的容器。一旦您开始事务,持久性上下文就在那里。它监视每个对象,缓存它们,以便重复的请求不会不必要地拉动数据库,并管理所有实体的状态。

把它想象成一只带着篮子的胖浣熊:它会绕着你所有的物品走来走去,高兴地把它们添加到篮子里,记住它们并保留它们直到交易完成。然后(砰!)它一次性将它们保存到数据库中。

Hibernate 持久性上下文中的对象生命阶段

在 Hibernate 世界中,对象可以从一种状态转换到另一种状态,这对于管理其生命周期至关重要。了解这些阶段有助于您有效地管理数据的存储、更新和删除。想象一下你的物体经历四个不同的阶段,就像电脑游戏中的关卡一样。

过境者(门口的客人)

当一个对象刚创建并且尚未绑定到数据库或 Hibernate 会话时,它处于瞬态。 Hibernate 对此一无所知,因此它不会被持久化到数据库中,直到它被持久化上下文管理。

**例如:Bublik the Cat 刚刚出现**

Cat cat = new Cat("Бублик", "серый");  // Transient
System.out.println("Имя кота: " + cat.getName());

执着(接受庇护)

既然您已确定您的猫(或其他物体)值得您关注并且应该将其保存到数据库中,则可以调用 save() 或 persist() 方法。此后它变得持久并且 Hibernate 开始监视它。当交易结束时,您对此对象所做的任何更改都将被自动跟踪并保存到数据库。

Persistent cat

示例:Bublik 在数据库中找到了归宿

Session session = sessionFactory.openSession();
session.beginTransaction();

Cat cat = new Cat("Бублик", "серый");
session.save(cat);  // Теперь кот стал persistent
cat.setColor("белый");  // Изменили цвет

session.getTransaction().commit();
session.close();

现在,猫 Bublik 是持久的:Hibernate“记住”了它,当您关闭事务时,更改(例如,将颜色更改为白色)会自动保存到数据库中。

分离式(Bablik 无人看管)

当 Hibernate 会话关闭时(例如,调用 session.close() 之后),持久对象将变为分离状态。这意味着它们不再被 Hibernate 跟踪,并且您对它们所做的任何更改都不会自动保存。

想象一下,Bublik 猫跑到街上,现在 Hibernate 不再照顾它。

例子:百吉饼离开了,但它不再在上下文中

Session session = sessionFactory.openSession();
session.beginTransaction();

Cat cat = session.get(Cat.class, 1);  // Кот уже есть в базе и стал persistent
session.getTransaction().commit();
session.close();  // Теперь он detached

cat.setColor("черный");  // Изменение цвета больше не отслеживается Hibernate

会话关闭后,Bublik 猫不再受 Hibernate 的控制。它的状态是分离的,如果我们将其颜色改为“黑色”,它将不会保存在数据库中。

让猫重新进入冬眠状态

要将对象重新绑定到持久上下文,可以使用“merge()”方法。

Session newSession = sessionFactory.openSession();
newSession.beginTransaction();

Cat mergedCat = (Cat) newSession.merge(cat);  // Бублик снова под контролем Hibernate
mergedCat.setColor("рыжий");  // Hibernate снова отслеживает изменения

newSession.getTransaction().commit();
newSession.close();

调用merge()之后,Hibernate再次开始监视Donut,现在所有更改都将被保存。

已移除

当您决定数据库中不再需要某个对象时,可以使用 session.delete() 将其标记为删除。这会使对象进入已移除状态。该对象可能仍存在于内存中,但一旦事务完成,它就会从数据库中消失。于是,我们的英雄再次踏上寻找冒险的旅程。

**示例:通往自由之路**

Session session = sessionFactory.openSession();
session.beginTransaction();

Cat cat = session.get(Cat.class, 1);  // Бублик под контролем Hibernate
session.delete(cat);  // Пометили на удаление

session.getTransaction().commit();  // Бублик удалён из базы
session.close();

一旦我们调用 delete(),该对象就会被删除。一旦交易完成,Bublik 将从数据库中永久删除。

Hibernate 中的状态转换图

为了可视化状态之间的转换,可以使用下图:

`Transient` → `Persistent`:使用 `session.save()` 或 `session.persist()` 将对象保存到数据库。

`Persistent` → `Detached`:会话已关闭,并且对象不再受 Hibernate 的控制。

`Detached` → `Persistent`:使用 `session.merge()` 或 `session.update()` 将对象重新绑定到 Hibernate。

`Persistent` → `Removed`:使用 `session.delete()` 标记对象以删除。

perscontext

Hibernate 中管理对象生命周期的方法

Hibernate 提供了许多管理对象状态的方法。从创建到删除,每个方法都会通过持久性上下文影响对象及其与数据库的关系。让我们看看每种方法、它们的特点、典型错误和有用的例子。

让我们列出生命周期管理的主要 Hibernate 方法

**节省()**

目的:

将对象保存到数据库,将其从瞬态转换为持久态。

主要特点:

生成一个 SQL INSERT 查询以将对象添加到数据库。

返回为对象生成的标识符(id)。

不需要对象已经存在于数据库中

Cat murzik = new Cat("Мурзик", "серый"); // Transient
session.save(murzik); // Теперь murzik — Persistent

典型错误:

尝试对已设置 id 的对象调用 save() 可能会导致 id 冲突错误。

**坚持()**

目的:

还将对象设置为持久状态。

与save()的区别:

persist() 不返回任何内容。

仅用于没有 ID 的对象(新对象)。

例子:

Cat barsik = new Cat("Барсик", "рыжий");
session.persist(barsik); // Добавлен в Persistence Context

典型错误:

persist() 不能用于处于分离状态的对象。

**更新()**

目的:

将处于分离状态的对象绑定到当前会话,并使其持久化。

例子:

session.close(); // Объект стал Detached
session = factory.openSession();
session.update(detachedCat); // Привязываем обратно

**合并()**

目的:

将对处于分离状态的对象的更改合并到持久性上下文中的当前状态中。

例子:

Cat detachedCat = session.get(Cat.class, 1);
session.close(); // Detached
detachedCat.setColor("белый");

session = factory.openSession();
session.merge(detachedCat); // Объединяет изменения с базой

与update()的区别:

如果持久上下文中已经存在分离对象,则 merge() 会创建一个新对象。

这使得merge()更安全。

**删除()**

目的:

从数据库中删除一个对象并将其设置为已删除状态。

例子:

session.delete(murzik); // Удаляет объект из базы

特点:

删除后,对象仍保留在内存中,但不再附加到持久性上下文。

**刷新()**

目的:

强制持久性上下文状态与数据库同步。

例子:

session.save(cat);
session.flush(); // Все изменения записаны в базу

典型错误:

如果在 flush() 和 commit() 之间发生错误,数据库将保持不同步。

**保存或更新()**

目的:

保存新对象或更新现有对象。

例子:

Cat cat = new Cat("Барсик", "рыжий");
cat.setId(1); // Если id существует, объект будет обновлён
session.saveOrUpdate(cat);

特点:

如果不确定该对象是新的还是现有的,请使用此方法。

但这可能会产生比必要更多的查询。

**清除()**

目的:

清除持久性上下文,从中删除所有对象。

例子:

session.clear(); // Все объекты становятся Detached

典型错误:

在 clear() 之后,对象将无法被访问并且不能保持持久性。

**分离()**

目的:

将对象置于分离状态,并将其从持久性上下文中移除。

例子:

session.detach(cat); // Теперь cat — Detached

**关闭()**

目的:

关闭 Hibernate 会话,使所有对象处于分离状态。

例如:`session.close();`

困难和常见错误

  • 使用 update() 时出错:如果持久性上下文中已经存在具有相同标识符的对象,则 Hibernate 将抛出异常。解决方案:如果您不确定对象的状态,请使用merge()。
  • 由于 `flush()` 导致的数据丢失:

    如果在“commit()”之前调用“flush()”,则更改可能会写入数据库,但不会被提交。

    解决方案:

    在 `flush()` 之后立即使用 `commit()`。

  • 使用 save() 时重复:如果对已设置 id 的对象调用 save(),则可能会导致唯一性违规。解决方案:在调用 save() 之前检查对象是否存在。
  • 延迟加载(LazyInitializationException):如果您在会话关闭后尝试访问相关对象,则会发生错误。解决方案:使用 fetch=FetchType.EAGER。使用JOIN FETCH提前加载数据。
  • Hibernate Zoo 中的对象之旅是一次激动人心的冒险,在每个阶段,Bublik 和他的朋友都会进入不同的状态并通过持久性上下文与数据库进行交互。一开始它们是自由的,就像鸟(或猫!)一样,然后它们受到严格的监督,自由游泳,最后可以永远离开这个动物园。