持久化对象 PO = POJO + xml映射
1、 hibernate 将持久化对象分为三种状态
transient(瞬时态、临时态) : 没有持久化标识 OID 、未与Session关联
persistent(持久态):存在持久化标识OID 、与Session关联(在事务提交 和 Session关闭之前)
detached (脱管态、离线态) : 存在持久化标识OID ,未与Session关联
2 、持久化对象三种状态转换
1) 瞬时态对象: new Book() 获得
瞬时 -- 持久 save() saveOrUpdate()
瞬时 -- 脱管 book.setId(1) ; 为瞬时对象设置OID
2) 持久态对象: get() / load() 、 Query、 Criteria 从数据库查询
持久 -- 瞬时 delete()
持久 -- 脱管 close() 、evict() 、clear()
3) 脱管态对象 无法直接获得
脱管 -- 瞬时 book.setId(null); 移除对象OID
脱管 -- 持久 update() 、saveOrUpdate() 、 lock() 过时
3、 Session缓存(一级缓存)
Hibernate框架中提供两级缓存 SessionFactory级别的缓存、Session级别的缓存
SessionFactory 级别的缓存 ---- 二级缓存 (外置,需要引入第三方插件)
Session 级别缓存 ----- 一级缓存 (内置,直接使用 )
l 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
l 当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库
l Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷出缓存(flush)
l 默认情况下 Session 在以下时间点刷出缓存:
a 当应用程序调用 Transaction 的 commit()方法的时, 该方法先刷出缓存(session.flush()), 然后在向数据库提交事务(tx.commit())
b 当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先 刷出缓存,以保证查询结果能够反映持久化对象的最新状态
c 调用 Session 的 flush() 方法
hibernate快照
当session加载了customer对象后,会为customer对象的值类型的属性复制一份快照。当刷出缓存时, 通过比较对象的当前属性和快照,来判断对象的哪些属性发生了变化
案例: 编程证明一级缓存是存在的
Session session =HibernateUtils.openSession();
Transaction transaction =session.beginTransaction();
// 所有持久态对象 都会被放入一级缓存
Book book = (Book)session.get(Book.class, 1); // 持久态 放入缓存
System.out.println(book);
// 再次执行查询 --- 直接使用一级缓存中数据(控制台上的查询语句仍是只有一句)
Book book2 = (Book)session.get(Book.class, 1);
System.out.println(book2);
transaction.commit();
session.close();
案例 : 快照和缓存存储原理 ,测试快照自动 flush数据
Session session =HibernateUtils.openSession();
Transaction transaction =session.beginTransaction();
// 先将图书查询出
Book book = (Book)session.get(Book.class, 1);// 持久态
book.setPrice(200d); // 修改一级缓存对象数据
// 不调用update ,是否可以更新(有transaction.commit()就有flush操作,就能更新)
transaction.commit(); // 如果 不flush ,数据不会改变
session.close();
一级缓存常见操作 flush 、evict 、clear 、refresh
* flush 比较缓存数据和快照数据是否一致,如果改变,将缓存数据更新到数据库(快照也更新,不会 清除缓存数据)
* clear 会清除所有一级缓存数据(所有持久对象都会变为脱管对象)
* evict 清除指定对象的一级缓存数据(被清除的对象,由持久变为脱管)
* refresh 使用数据库中数据去覆盖缓存和快照的数据
一级缓存的flush 模式 FlushMode
* 通过session.setFlushMode 设置一级缓存刷出时间点
* 默认FlushModel.AUTO 在 Transaction.commit Query查询 session.flush 执行数据flush
刷出session.flush > Transaction.commit > Query 查询
* COMMIT 会在 session.flush和 Transaction、commit
* MANUAL 只会在session.flush时 执行数据flush
如果 使用MANUAL 必须 调用session.flush 才会将缓存数据更新到数据库,transaction的commit 不 会flush数据
ALWAYS和AUTO的区别
当hibernate缓存中的对象被改动之后,会被标记为脏数据(即与数据库不同步了)。当 session设置为 FlushMode.AUTO时,hibernate在进行查询的时候会判断缓存中的数据是否为脏数据,是则刷数据库, 不是则不刷,而always是直接刷新,不进行任何判断。很显然auto比always要高效得多
4、 持久化对象的操作
1) save 方法 瞬时态对象 转换 持久态对象
如果采用数据库主键自增策略,执行save方法时,第一时间将insert语句发送到数据库,获得生成id , 保存瞬时对象中,成为持久对象。持久化对象 OID 不能随意修改
2) update 方法 将脱管变为持久
将数据更新到数据库, 放入一级缓存
如果脱管数据和数据库是一样的,执行update就没有意义,但是update任然会执行。如果设置select-before-update 属性,就会在修改之前先查询
*<classname="cn.itcast.domain.Book" table="book"catalog="hibernate3day2" select-before-update="true">
如果更新脱管对象OID 在数据库不存在,就会发生如下异常
* org.hibernate.StaleObjectStateException: Row was updated or deleted byanother transaction (or unsaved-value mapping was incorrect):[cn.itcast.domain.Book#3]
如果 更新脱管对象时,一级缓存中已经存在相同OID的持久对象, 会发生异常
* org.hibernate.NonUniqueObjectException: a different object with thesame identifier value was already associated with the session:[cn.itcast.domain.Book#2]
3) saveOrUpdate
如果操作目标对象,没有OID (OID为null, OID为设置unsaved-value),执行save
如果操作目标对象,存在OID ,执行update
例:在Book.hbm.xml设置 <id name="id" unsaved-value="1000"> ,如果操作Book的 OID为1000,框架会认为Book对象是一个瞬时对象
4) get 、load
get 立即执行SQL语句,返回目标对象
load 默认采用延迟加载机制,返回目标类子类对象 (代理对象),不会立即执行SQL语句,会在访问对象内除了id之外属性时执行SQL
如果OID在目标数据库不存在,get立即执行返回 null,load 返回代理对象,访问对象的值时,抛出异常 ObjectNotFoundException
5) delete 删除
可以删除一个脱管对象或者一个持久对象的,对于单表操作删除脱管对象和持久对象无区别,对于多表操作有外键处理区别
* 原理:先将脱管对象转换为持久对象再删除
注:被删除对象不要再使用