11. Hibernate 持久化对象的各种状态

1. 前言

本节课和大家聊聊持久化对象的 3 种状态。通过本节课程,你将了解到:

  • 持久化对象的 3 种状态;
  • 什么是对象持久化能力。

2. 持久化对象的状态

程序运行期间的数据都是存储在内存中。内存具有临时性。程序结束、计算机挂机...... 内存中的数据将不复存在。

重要的数据,需要使用持久化技术 将数据保存到永久性设备上。Hibernate 能够通过 PO(持久化对象) 将数据持久化到数据库。

HibernatePO 进行操作期间,PO 本身会发生一系列的状态变化。

2.1 瞬时状态(Transient)

分析一段保存数据的实例:

使用 Hibernate 保存数据之前,须先在程序中创建一个名为 stuPO

复制代码
Student stu=new Student("PO对象的瞬时状态", "男");  

PO 在程序运行的内存中存在,数据库中没有。PO 此时处于瞬时状态 (Transient)

瞬时状态下的 PO 特征如下:

  • 程序中有、数据库中没有此对象的相关信息;
  • 对象的标识性属性(对应表中主键的那个属性) 为空;
  • 如果不使用 Session 对象的相关方法进行数据库请求操作,程序退出时瞬时状态的对象信息会丢失。

2.2 持久化状态( Persistent )

创建 PO 后,使用 Session 的相关方法,如 save() 方法向数据库提交保存请求:

复制代码
Student stu=new Student("PO对象的瞬时状态", "男");
session.save(stu);  

此时 PO 在程序中有,数据库中也有,状态便由瞬时状态 转变成为持久化状态(Persistent)

持久化状态下的 PO 有如下几个特征:

  • 程序、数据库中都有 PO 的信息;
  • 对象的标识属性的值为数据库中对应记录的主键值;
  • 持久化状态最大的特点是 PO 处于 Session 生命周期之内。此状态下的 PO 具有持久化能力。

2.3 游离状态(Detached)

PO持久化状态 一直维持到 Session 对象关闭。如果 Session 对象关闭了,此 PO 的状态将由持久化状态 转变成游离状态 (Detached)

游离状态时 PO 的特点:

  • 程序、数据库都有 PO 的信息;
  • 但是,此状态下的 PO 不具有持久化能力。

PO 不会一直停留在某一个状态上,PO 随时可以在 3 种状态之间进行切换。

从上图可看出,PO3 种状态之间的相互演变都是通过调用 Session 对象的相关方法实现的。

由此看来,Session 对象被称为持久化容器是有道理的。

由上图可知,处于瞬时状态游离状态 的对象才有可能被 JVM 垃圾回收器回收

3. 对象持久化能力

知道了 PO3 种状态。自然会问:不同状态下的对象对实际操作有什么实际指导意义?

3 种状态中,持久化状态 的意义最大,如果 PO 处于持久化状态,此时 PO 就具有持久化能力。

所谓对象持久化能力,通俗理解:

程序中的数据发生变化,会自动同步到数据库中。

演示一段数据更新实例,更新之前先查询数据:

复制代码
try {
    transaction = session.beginTransaction();
    //查询学生
    Student stu=(Student)session.load(Student.class, new Integer(2));
    //修改学生信息
    
    //执行更新操作
    
    transaction.commit();
} catch (Exception e) {
    transaction.rollback();
} finally {
    session.close();
}  

通过 Sessionget() 方法查询出来的 stu 对象,此时就处于持久化状态。

在" 修改学生信息 "的注释下添加一行代码:

复制代码
stu.setStuName("持久化状态就是这么牛"); 

不需要调用 Session 中的任何其它方法,执行代码,程序中修改的数据立即同步到数据库中。

这就是持久化状态的特点:通过 PO 自动同步程序与数据库中的数据

所谓对象持久化能力本质上还是 Session 给的。
Session 记录对象是否处于持久化状态,并充当后台靠山。处于持久状态的对象与数据库之间的数据同步,只是不需要 Session 显示调用。

除了 get()、load()方法。save()、update()、saveOrUpdate()、persis()、megre() 方法都可称为持久化方法。

调用这些方法后,能让对象进入持久化状态,Session 记录并且默默维持 PO 中数据与数据库中数据的同步。

3.1 save**()** 和 persist**()** 方法

saveOrUpdate() 方法很好理解,是 save()update() 方法的综合简化版,内在本质没改变。

save()persist() 方法有细节上的区别。
save() 方法原型:

复制代码
public Serializable save(Object object);

上一段 save () 方法的测试实例:

复制代码
try {
    Student  stu = new Student("save()方法", "男");
    Serializable  stuId = session.save(stu);
    System.out.println("----------输出学生编号Id---------");
    System.out.println(stu.getStuId());
    System.out.println(stuId);
    System.out.println("----------事务在后面-------"); 
    transaction = session.beginTransaction();
    transaction.commit();
} catch (Exception e) {
    transaction.rollback();  
} finally {
    session.close();
}  

输出结果:

复制代码
Hibernate: 
    insert 
    into
        Student
        (stuName, stuPassword, stuPic, stuSex) 
    values
        (?, ?, ?, ?)
----------输出学生编号Id---------
40
40
----------事务在后面------- 

结果即结论:

Save() 方法可以在事务之外执行;
有一个关键点需要引起重视:

无论是在事务之内还是事务之外,save() 方法都会向数据库发送了一条 Sql 语句请求,控制台输出结果是一样的。

但是:

  • 如果程序中 Hibernate 不显示发送事务提交指令,数据会回滚(丢失);
  • 只有当数据库系统接收到程序中发送过来的事务提交指令后,才会真正意义上保存。

很好理解,因为事务是交给 Hibernate 管理的,数据库接收到插入指令后,在没有明确事务提交指令之前,只会把数据缓存在内存中。

也就是说,虽然 save() 方法看起来不依赖事务就可插入数据,但,没有事务组件的指令,最后也是虚行一场。

persist() 方法原型:

复制代码
public void persist(Object object);  

上一段 persist() 测试实例:

复制代码
try {
    Student stu = new Student("persist()方法", "男");
    session.persist(stu);
    System.out.println("----------输出学生编号Id---------");
    System.out.println(stu.getStuId());
    System.out.println("----------事务在后面-------");
    transaction = session.beginTransaction(); 
    System.out.println("-------------事务提交---------------");
    transaction.commit();
    System.out.println(stu.getStuId());
} catch (Exception e) {
    transaction.rollback();
} finally {
    session.close();
}

输出结果:

复制代码
----------输出学生编号Id---------
null
----------事务在后面-------
-------------事务提交---------------
Hibernate: 
  insert 
  into
  Student
  (stuName, stuPassword, stuPic, stuSex) 
  values
  (?, ?, ?, ?)
39 

persist() 方法只有当事务提交后,才会发送 Sql 请求,数据直接写入数据库,方法本身没有返回值。

save() 和 persist() 方法区别:

  • 在事务之内调用时,两者区别不大;事务之外,区别明显。
  • save() 返回主键值,persist() 方法没有返回值;
  • persist() 完全依赖事务组件,否则不会提交 Sql 请求;
  • persist() 方法除了可进行 save 操作,还可以进行 update 操作。

3.2 merge**()** 方法

方法原型:

复制代码
 public Object  merge(Object object);  

merge() 方法和 persist() 方法类似, 区别在于:

  • merge() 方法接收一个 PO 作为参数,创建并返回此 PO 的副本对象;
  • 此副本对象具有对象持久化能力 。这一点是 merge() 方法与其他方法最大的不同。

上一段实例:

复制代码
try{
    transaction = session.beginTransaction();
    //查询出来的stu具有持久化能力
    Student stu = (Student) session.get(Student.class, new Integer(2));    
    //转stu对象持久化状态转变成游离状态
    session.clear();
    //stu_对象具有持久化能力
    Student stu_ = (Student) session.merge(stu);
    //这个操作不能同步到数据库
    stu.setStuName("我已经不具有持久化能力");
    //这个操作能同步到数据库
    stu_.setStuName("我具有持久化能力");
    transaction.commit();
} catch(Exception e) {
    transaction.rollback();
} finally {
    session.close();
}  

merge() 方法返回的 stu 对象的副本 stu_,此对象具有持久化能力。执行下面代码,数据能同步到数据库中。

复制代码
stu_.setStuName("我具有持久化能力");  

Session 中提供的每一个方法都有其实际意义。

特别是 merge() 方法,既可以保护原对象中的数据不被污染,又能行使数据库同步操作。

在很多场景里都会有这个需求。

4. 小结

本节课程讲解 PO 对象的 3 种状态,以及 3 种状态之间的转化方式。了解处于持久化状态的 PO 具有持久化能力,这是 Hibernate 提供的一个很棒的程序中对象数据库数据自动同步的方案。

也是一种快速开发方案。

本节课区分了几个常用方法的差异性。但真相似乎就是:大家都和持久化状态有关系。

相关推荐
敖云岚2 小时前
【Redis】分布式锁的介绍与演进之路
数据库·redis·分布式
LUCIAZZZ2 小时前
HikariCP数据库连接池原理解析
java·jvm·数据库·spring·springboot·线程池·连接池
我在北京coding3 小时前
300道GaussDB(WMS)题目及答案。
数据库·gaussdb
小Tomkk3 小时前
阿里云 RDS mysql 5.7 怎么 添加白名单 并链接数据库
数据库·mysql·阿里云
明月醉窗台4 小时前
qt使用笔记二:main.cpp详解
数据库·笔记·qt
沉到海底去吧Go4 小时前
【图片自动识别改名】识别图片中的文字并批量改名的工具,根据文字对图片批量改名,基于QT和腾讯OCR识别的实现方案
数据库·qt·ocr·图片识别自动改名·图片区域识别改名·pdf识别改名
老纪的技术唠嗑局4 小时前
重剑无锋,大巧不工 —— OceanBase 中的 Nest Loop Join 使用技巧分享
数据库·sql
未来之窗软件服务5 小时前
JAVASCRIPT 前端数据库-V6--仙盟数据库架构-—-—仙盟创梦IDE
数据库·数据库架构·仙盟创梦ide·东方仙盟·东方仙盟数据库
一只爱撸猫的程序猿6 小时前
构建一个简单的智能文档问答系统实例
数据库·spring boot·aigc
nanzhuhe6 小时前
sql中group by使用场景
数据库·sql·数据挖掘