“Hibernate笔记 4:核心知识”的版本间差异

来自Wikioe
跳到导航 跳到搜索
 
(未显示同一用户的16个中间版本)
第16行: 第16行:
#: 因为 Hibernate 中有延迟加载的机制,这个机制中会产生【代理对象】,Hibernate 产生代理对象使用的是【字节码的增强技术】完成的,其实就是产生了当前类的一个子类对象来实现的。
#: 因为 Hibernate 中有延迟加载的机制,这个机制中会产生【代理对象】,Hibernate 产生代理对象使用的是【字节码的增强技术】完成的,其实就是产生了当前类的一个子类对象来实现的。
#: 如果使用了 final 修饰待久化类,那么就不能产生子类,从而不能产生【代理对象】,那么 Hibernate 的'''【延迟加载】策略就会失效'''。
#: 如果使用了 final 修饰待久化类,那么就不能产生子类,从而不能产生【代理对象】,那么 Hibernate 的'''【延迟加载】策略就会失效'''。
此外,“从代码规范上讲,<span style="color: blue">'''持久化对象是一定要实现序列化接口的'''</span>,保证能在异构化系统或网络中进行数据传输。”


=== 什么是 OID? ===
=== 什么是 OID? ===
第88行: 第91行:


=== 持久态的自动更新 ===
=== 持久态的自动更新 ===
  Hibernate 会依据 persistent 状态的实体对象的属性变化,而自动更新(无需 update 等操作)数据库中相对应的记录。
见:<big>'''[[Hibernate笔记 4:核心知识:Session缓存#持久态自动更新]]'''</big>
 
== Hibernate 缓存 ==
Hibernate 的缓存分为:“'''一级缓存'''”(<span style="color: blue; font-size: 120%">Session 缓存</span>)和“'''二级缓存'''”(<span style="color: blue; font-size: 120%">SessionFactory 缓存</span>),二者都位于持久化层,存储的都是数据库数据的备份。
作用:减少对数据库的访问次数。
 
其中:
# “'''一级缓存'''”:Session 级别的缓存,事务范围。  ——见:<big>'''[[Hibernate笔记 4:核心知识:Session缓存]]'''</big>
#* Hibernate 的内置缓存(默认的缓存机制),不能被卸载;
# “'''二级缓存'''”:SessionFactory 级别的缓存,应用范围(应用中的所有 Session 共享)。
#* 默认关闭,需要手动配置;
 
== 事务管理 ==
  在 Hibernate 中,可以通过代码来操作管理事务(如:开启、提交、回滚),此外,还可以在 Hibernate 的配置文件中对事务进行配置。
   
   
   
   
  注意:在一个 Session 中,对持久对象的改变不会马上对数据库进行变更,而是发生在 Transaction 终止,执行 commit 之后。
  事务管理方式:
1、代码。  ——如【'''[[Hibernate笔记 3:核心API#Transaction]]'''】中所示;
2、配置文件。


示例:
通过配置文件设置事务管理,步骤:
: <syntaxhighlight lang="Java" highlight="">
# 配置:事务隔离级别;
@Test
# 配置:Session 管理方式;(绑定到本地线程)
// 测试:持久态对象的自动更新
# HibernateUtil 工具类修改 Session 获取方式:
public void demo() {  
#: <syntaxhighlight lang="Java" highlight="">
Session session = HibernateUtils.openSession();
/**
Transaction tx = session.beginTransac七ion();
* 获取当前线程绑定的会话:
*/
// 获得持久态对象.
public static Session getCurrentSession(){  
Customer customer = session.get(Customer.class, 11);
return sessionFactory.getCurrentSession();  
// 修改持久态对象
customer.setCust name("主五");
// session.update(custorner);  // 不用手动调用 update 方法就可以更新
tx.commit();
session.close();  
}
}
</syntaxhighlight>
</syntaxhighlight>
: 如上,更改持久态对象属性之后,不调用 update 等方法,依旧可以更新数据库记录。
* 所有相关配置内容,见:<big>'''[[Hibernate笔记 2:配置文件详解#配置:事务管理]]'''</big>


  <span style="color: Coral; font-size: 120%">'''持久态的自动更新,依赖于 Hibernate 的一级缓存。'''</span>
=== 关于“Session 管理方式:绑定到本地线程” ===
  在真正进行事务管理的时候,<span style="color: blue">'''事务控制应该在 Service 层(而非 DAO 层)实现'''</span>, 并且在 Service 中调用多个 DAO 实现一个业务逻辑的操作。
所以,最主要的是:如何保证“在 Service 中开启的事务时所使用的”和“DAO 中多个操作所使用的”是'''同一个 Session 对象'''。


== Hibernate 缓存 ==
对于保证“同一 Session 对象”,有两种方式:
# 在业务层获取到 Session, 并将 Session 作为参数传递给 DAO;
# 使用 '''ThreadLocal''' 将业务层获取的 Session 绑定到当前线程中, 然后在 DAO 中获取 Session 的时候, 都从当前线程中获取。【⭐绑定 Session 到当前线程⭐】




=== 一级缓存(Session 缓存) ===
  显然,“绑定 Session 到当前线程”是更优解。  —— 对此,Hibernate 中,对此已经提供了支持,只需配置即可。
  Session 中有一个缓存,被称为“Hibernate 的<span style="color: blue; font-size: 120%">'''一级缓存'''</span>”。


Session 缓存是由一系列的 Java 集合构成的:
而后,常用的 HibernateUtil 工具类中,获取 Session 的方式也应由 '''<syntaxhighlight lang="Java" inline>sessionFactory.openSession()</syntaxhighlight>''' 替换为使用 '''<syntaxhighlight lang="Java" inline>sessionFactory.getCurrentSession()</syntaxhighlight>'''。
    当一个对象被加入到 Session 缓存中,这个对象的引用就加入到了 Java 的集合中,即使以后应用程序中的引用变量不再引用该对象,只要 Session 缓存不被清空,这个对象一直处于生命周期中。


作用:
=== 关于“事务” ===
# 减少访问数据库的频率。
  事务的“<span style="color: blue">'''ACID'''</span>”:Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)
# 保证缓存中的对象与数据库中的相关记录保持同步。
   
 
    ——见:'''[[InnoDB:InnoDB 和 ACID 模型]]'''
==== 与实体状态 ====
  Session 缓存,用于存放被当前工作单元加载的对象,这块缓存中有一个 '''map'''
事务的“<span style="color: blue">'''并发问题'''</span>”:脏读、不可重复读、幻读
  1、在执行 '''save()''' 方法后,会生成一个 '''id'''(<span style="color: blue">'''OID'''</span>),这个 id 会保存在 map 的 '''key''' 中;
  2、与之对应的 '''value''' 值就是该是该对象的引用,执行 '''commit()''' 方法后,数据库就有对应这条数据了,此时该实体处于“持久状态”;
    ——见:'''[[InnoDB 锁机制解析#锁问题]]'''
  3、当执行完 Session.'''close()'''后,处于“游离状态”。
   
 
  事务的“<span style="color: blue">'''隔离级别'''</span>”:“READ UNCOMMITTED”、“READ COMMITTED”、“REPEATABLE READ”、“SERIALIZABLE”
区分实体的三种状态:
 
:{| class="wikitable"
    ——见:'''[[InnoDB:InnoDB 事务模型#事务隔离级别]]'''
! 实体状态 !! OID !! Session缓存 !! 数据库
   
|-
| 瞬时状态 || null || 不存在 || 无记录
  相关内容,见:'''[[InnoDB:InnoDB 锁和事务模型]]'''
|-
| 持久状态 || 非null || 存在 || 有记录
|-
| 托管状态 || 非null || 不存在 || (可能)有记录
|}
 
==== 清理时机 ====
Session 清理缓存的时机:
# 提交前:当调用 Transaction 的 commit() 方法时,commit() 方法先清理缓存(前提是:FlushMode.COMMIT/AUTO),然后再向数据库提交事务。
# 查询前:当应用程序调用 Session 的 find() 或者 iterate() 时,如果缓存中的持久化对象的属性发生了变化,就会先清理缓存,以保证查询结果能反映持久化对象的最新状态。
# flush:当应用程序显示调用 Session 的 flush() 方法的时候。
: 如下图:
:{| class="wikitable"
! FlashMode !! Session.find()/iterate() !! Session.commit() !! Session.flush()
|-
| FlashMode.AUTO || ✔ || ✔ || ✔
|-
| FlashMode.COMMIT || ✘ || ✔ || ✔
|-
| FlashMode.NEVER || ✘ || ✘ || ✔
|}
:* 清理(✔),不清理(✘)
 
=== 二级缓存(SessionFactory 缓存) ===
  SessionFactory 在 Hibernate 中起到一个缓冲区作用,Hibernate 可以将自动生成的 SQL 语句、映射数据以及某些可重复利用的的数据放在这个缓冲区中。同时它还保存了对数据库配置的所有映射关系,维护了当前的<span style="color: blue; font-size: 120%">'''二级缓存'''</span>。

2022年6月15日 (三) 13:11的最新版本


持久化类的编写规则

如果一个 Java 类与数据库表建立了映射关系,那么这个类称为是持久化类。 (该类有对应的映射文件与数据库表相关联)

编写规则:

  1. 持久化类衙要提供无参数构造方法
    因为在 Hibernate 的底层需要使用反射生成类的实例;
  2. 持久化类必须:属性私有,且提供公有的 get 和 set 方法
    因为在 Hibernate 底 层会将查询到的数据进行封装;
  3. 持久化类的属性要尽量使用包装类的类型。
    因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更消晰而基本数据类型不容易描述;
  4. 持久化类要有一个唯一标识 OID 与数据库表的主键相对应。
    Hibernate 通过 OID 区分在内存中的持久化类;
  5. 持久化类尽量不要使用 final 进行修饰。
    因为 Hibernate 中有延迟加载的机制,这个机制中会产生【代理对象】,Hibernate 产生代理对象使用的是【字节码的增强技术】完成的,其实就是产生了当前类的一个子类对象来实现的。
    如果使用了 final 修饰待久化类,那么就不能产生子类,从而不能产生【代理对象】,那么 Hibernate 的【延迟加载】策略就会失效
此外,“从代码规范上讲,持久化对象是一定要实现序列化接口的,保证能在异构化系统或网络中进行数据传输。”


什么是 OID?

OID(object identifier,“对象标识符”),是 hibernate 用于区分两个对象(持久化类)是否是同一个对象的标识。

持久化类的 OID 用于与数据库表的主键相映射,所以一般不手动指定。

OID 在对象持久化之前是 null,持久化的时候 hibernate 或者我们手动指定一个 id(被插入到数据库当做主键,在 session中 当做索引),所以,需要保证 OID 与主键的一致性,比如:类型、长度等。

主键生成策略

Hibernate 的主键依据:不同的主键类型、不同的数据库,可以有不同生成策略。

主键类型:

  1. 自然主键:带有业务含义的主键(比如:学号,工作编号)。
    • 如果不手动指定主键就提交缓存进行更新,则会报错!
    • 主键生成策略:
      1. assinged:Hibernate不维护主键,开发人员需要手动设置主键;
        • 如果不指定过元素的 <generator> 属性,则默认使用该主键生成策略。
  2. 代理主键:通过编码自动生成的,无业务含义的主键。
    • 主键生成策略:
      1. increment:(由 Hibernate 提供)自动增长
        • 适用主键:short、int、long 类型的主键;
        • 不适用与并发访问数据库;
      2. identity:(由数据库提供)自动增长
        • 适用主键:short、int、long 类型的主键;
        • 适用数据库:支持自动增长的数据库(如:DB2、 MySQL、 MS SQL Server、 Sybase 和 HypersomcSQL);(不适用于 Oracle)
      3. sequence:(由数据库提供)序列
        • 适用主键:short、int、long 类型的主键;
        • 适用数据库:支持序列的方式的数据库(如:Oracle、db2、sap、db、postgresql);
      4. native:本地策略,根据底层的数据库不同,自动选择使用 identity 还是 sequence。
        • 适用主键:short、int、long 类型的主键;
      5. uuid:Hibernate 采用 128 位的 UUID 算法来生成标识符。
        该算法能够在网络环境中生成唯一的字符串标识符,其 UUID 被编码为一个长度为 32 位的十六进制字符串
        • 适用于:字符串类型的主键。
        • 这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间。

生命周期(实体状态)

Session 的生命周期是以一个逻辑事物的开始和结束为边界,Session 的主要功能是提供创建、读取和删除映射的实体类的操作。

实体可能存在于三种状态:

  1. 瞬时态(transient):实体对象在内存是自由存在的(即为普通的 Java 对象),即:“该实体从未与任何持久化上下文关联过,它没有持久化标识”。
    • 特点:
      1. 不存在待久化标识 OID (相当于主键值);
      2. 尚未与 Session 关联;
      3. 数据库无对应记录;
    • 对象失去引用后将被 JVM 回收。
  2. 持久态(persistent):实体对象处于 Hibernate 框架所管理的状态,即:“该实体在数据库中有对应的记录,并拥有一个持久化标识”。
    • 特点:
      1. 存在待久化标识 OID
      2. 已与 Session 关联(加入到了 Session 缓存,且 Session 未关闭);
      3. 数据库有对应记录;
    • 持久态对象发生改变时,Hibernate 将会依据其改变自动更新。【无需 update 等操作】
      • 在一个 Session 中,对持久对象的改变不会马上对数据库进行变更,而是发生在 Transaction 终止,执行 commit 之后。
  3. 游离态(detached,脱管态):由持久态实体对象转变(关联的 Session 被关闭)而来。
    • 特点:
      1. 存在持久化标识 OID
      2. 失去了与 Session 的关联;
      3. 数据库有对应记录;
    • 游离态对象发生改变时,Hibernate 不能检测到。
      对象的引用依然有效,也可以继续被修改,但修改将不会影响到到数据库中的数据。
“瞬时态”与“游离态”的区别是“是否存在 OID”。(数据库对应记录不是重点)???

如:
   Customer customer = new Customer();   // 瞬时态
   customer.setCust_id(1);   // 脱管态
   customer.setCust_id(null);   // 瞬时态

状态转换

三种状态可以互相转换:

Hibernate 生命周期图.png

持久态的自动更新

见:Hibernate笔记 4:核心知识:Session缓存#持久态自动更新

Hibernate 缓存

Hibernate 的缓存分为:“一级缓存”(Session 缓存)和“二级缓存”(SessionFactory 缓存),二者都位于持久化层,存储的都是数据库数据的备份。

作用:减少对数据库的访问次数。

其中:

  1. 一级缓存”:Session 级别的缓存,事务范围。 ——见:Hibernate笔记 4:核心知识:Session缓存
    • Hibernate 的内置缓存(默认的缓存机制),不能被卸载;
  2. 二级缓存”:SessionFactory 级别的缓存,应用范围(应用中的所有 Session 共享)。
    • 默认关闭,需要手动配置;

事务管理

在 Hibernate 中,可以通过代码来操作管理事务(如:开启、提交、回滚),此外,还可以在 Hibernate 的配置文件中对事务进行配置。 


事务管理方式:
1、代码。   ——如【Hibernate笔记 3:核心API#Transaction】中所示;
2、配置文件。

通过配置文件设置事务管理,步骤:

  1. 配置:事务隔离级别;
  2. 配置:Session 管理方式;(绑定到本地线程)
  3. HibernateUtil 工具类修改 Session 获取方式:
    	/**
    	* 获取当前线程绑定的会话:
    	*/
    	public static Session getCurrentSession(){ 
    		return sessionFactory.getCurrentSession(); 
    	}
    

关于“Session 管理方式:绑定到本地线程”

在真正进行事务管理的时候,事务控制应该在 Service 层(而非 DAO 层)实现, 并且在 Service 中调用多个 DAO 实现一个业务逻辑的操作。

所以,最主要的是:如何保证“在 Service 中开启的事务时所使用的”和“DAO 中多个操作所使用的”是同一个 Session 对象

对于保证“同一 Session 对象”,有两种方式:

  1. 在业务层获取到 Session, 并将 Session 作为参数传递给 DAO;
  2. 使用 ThreadLocal 将业务层获取的 Session 绑定到当前线程中, 然后在 DAO 中获取 Session 的时候, 都从当前线程中获取。【⭐绑定 Session 到当前线程⭐】


显然,“绑定 Session 到当前线程”是更优解。   —— 对此,Hibernate 中,对此已经提供了支持,只需配置即可。

而后,常用的 HibernateUtil 工具类中,获取 Session 的方式也应由 sessionFactory.openSession() 替换为使用 sessionFactory.getCurrentSession()

关于“事务”

事务的“ACID”:Atomicity(原子性)、Consistency(一致性)、Isolation(隔离性)、Durability(持久性)

    ——见:InnoDB:InnoDB 和 ACID 模型

事务的“并发问题”:脏读、不可重复读、幻读

    ——见:InnoDB 锁机制解析#锁问题

 事务的“隔离级别”:“READ UNCOMMITTED”、“READ COMMITTED”、“REPEATABLE READ”、“SERIALIZABLE”
 
    ——见:InnoDB:InnoDB 事务模型#事务隔离级别


相关内容,见:InnoDB:InnoDB 锁和事务模型