11---Session中的一级缓存

Session中的一级缓存

1.Session中的一级缓存

    Hibernate框架共有两级缓存, 一级缓存(Session级别缓存)、二级缓存(SessionFactory级别缓存)
    在Session接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存.  持久化对象保存Session一级缓存中(一级缓存引用持久化对象地址),只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期
    Hibernate Session接口的实现类SessionImpl类(查看源码,右击session,选择Open Type Hierarchy) ,里面有2个重要的字段:
* private transient ActionQueue actionQueue;                       ---- 行动队列(标记数据活动)
* private transient StatefulPersistenceContext persistenceContext; ---- 持久化上下文 

    当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、 load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库
    Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷出缓存(flush)
        *  Transaction的commit()
        *  应用程序执行一些查询操作时
        *  调用Session的flush()方法

    验证一级缓存的存在
Book book = (Book) session.get(Book.class, 1); // 第一次查询,缓存中没有
System.out.println(book);
 
Book book2 = (Book) session.get(Book.class, 1);// 因为第一次查询,对象已经被放入1级缓存,不会查询数据
System.out.println(book2);
* 生成一条SQL语句,返回同一个对象 ,第一次查询生成SQL,查询对象,将对象放入一级缓存,第二次查询,直接从一级缓存获得

    测试Hibernate快照 (深入理解一级缓存内存结构原理)
    hibernate 向一级缓存放入数据时,同时保存快照数据(数据库备份),当修改一级缓存数据,在flush操作时,对比缓存和快照,如果不一致,自动更新(将缓存的内容同步到数据库,更新快照)

*  快照区使用,在Session 保存一份与数据库相同的数据,在session的flush时, 通过快照区比较得知一级缓存数据是否改变,如果改变执行对应操作(update)
/**  
* 测试快照区的使用
  */
@Test
public void demo3() {
    // 获得Session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction transaction = session.beginTransaction();
 
// 查询id 为1 的图书对象
Book book = (Book) session.get(Book.class, 1); // 第一次查询,将对象加入一级缓存
System.out.println(book);
 
book.setName("深入浅出Hibernate技术"); // 修改书名(一级缓存被修改,自动update)
 
// 没有手动执行update,因为快照区原因,自动更新
 
// 提交事务,关闭Session
transaction.commit();
session.close();
}  
    使用Debug模式进行截图说明:
    我们重点关注session中的2个属性actionQueue和persistenceContext

    大白话解析
        **当执行get后,缓存里面有数据了,活动队列没有发生变化,说明没有需要提交到数据的内容,PersistenceContext里面有内容了。
            我们说,持久化上下文里面存放的是一个Map,它的键为一级缓存对象,值为快照(它是一级缓存对象的一个副本)。
        **当执行setName后,一级缓存里面的数据发生了改变,在缓存flush时,会对比缓存和快照,如果不一致,那么会将缓存中的内容同步到数据库,并更新快照!

*  Hibernate中 持久态 对象具有自动更新数据库能力 (持久态对象 才保存在 Session中,才有快照 )

2.一级缓存常见操作 

    所有操作需要使用断点调试才能看得比较清楚!

    1)flush : 修改一级缓存数据针对内存操作,需要在session执行flush操作时,将缓存变化同步到数据库 
     * 只有在缓存数据与快照区不同时,生成update语句
    2)clear : 清除所有对象 一级缓存 
    3)evict : 清除一级缓存指定对象 
    4)refresh :重新查询数据库,更新快照和一级缓存 
@Test
// Session 对于 一级缓存操作
public void demo4() {
// 获得Session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction transaction = session.beginTransaction();
 
// 查询id 为1 的图书对象
Book book = (Book) session.get(Book.class, 1); // 第一次查询,将对象加入一级缓存
System.out.println(book);
 
// book.setPrice(80d); // 修改一级缓存数据
// 将缓存内容同步到数据库
// session.flush();
 
// 清除一级缓存所有数据
// session.clear();
 
// 清除一级缓存中 指定对象
// session.evict(book);
 
book.setPrice(30d);// 一级缓存改变
session.refresh(book); // 用数据库内容 覆盖快照区和一级缓存
 
// 提交事务,关闭Session
transaction.commit();
session.close();
}

3.一级缓存刷出时间点设置 (FlushMode)

ALWAYS :在每次查询时,session都会flush  (不要求 )
AUTO   : 在有些查询时,session会flush  (默认)  ---------- 查询、commit 、session.flush 
COMMIT : 在事务提交时,session会flush   ------- commit 、session.flush
MANUAL :只有手动调用  session.flush 才会刷出  ----  session.flush 
刷出条件(时间点严格程度 )
MANUAL > COMMIT> AUTO> ALWAYS 
@Test
// 理解 FlushMode作用
public void demo5() {
// 获得Session
Session session = HibernateUtils.openSession();
 
// 设置 flushMode
session.setFlushMode(FlushMode.MANUAL);
 
// 开启事务
Transaction transaction = session.beginTransaction();
 
// 查询id 为1 的图书对象
Book book = (Book) session.get(Book.class, 1); // 第一次查询,将对象加入一级缓存
System.out.println(book);
 
book.setPrice(1000d);// 修改价格
 
session.createQuery("from Book").list();// 查询所有图书 (AUTO 级别 flush)
 
// 提交事务,关闭Session
transaction.commit(); // (COMMIT 级别 flush)
 
// session.flush(); // MANUAL 级别 flush
 
session.close();
}

4.session持久化对象操作方法 

    1) save 将数据保存到数据库 , 将瞬时对象转换持久对象 
持久化对象,不允许随便修改 OID 

    2) update 更新数据 ,主要用于脱管对象的更新(持久对象,可以根据快照自动更新 ), 将脱管对象转换持久对象         
@Test
// 脱管对象更新
public void demo6() {
// 获得Session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction transaction = session.beginTransaction();
 
Book book = new Book(); // 瞬时
book.setId(1); // 脱管
book.setName("java入门");
book.setPrice(40d);
 
session.update(book); // 持久
 
session.flush();
 
// book.setPrice(50d);
 
// 提交事务,关闭Session
transaction.commit();
session.close();
}
        问题一: 调用update,默认直接生成update语句,如果数据没有改变,不希望生成update 
    在hbm文件 <class>元素 添加 select-before-update="true"
<class name="cn.itcast.domain.firstcache.Book" table="book" catalog="hibernate3day2" select-before-update="true">
问题二: 当update,脱管对象变为持久对象, 一级缓存不允许出现相同OID 两个持久对象 
@Test
// 一级缓存 存在两个相同OID 持久态对象 报错
public void demo7() {
// 获得Session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction transaction = session.beginTransaction();
 
// 查询
// Book b = (Book) session.get(Book.class, 1); // 持久
 
Book book = new Book(); // 瞬时
book.setId(1); // 脱管
book.setName("java入门");
book.setPrice(50d);
session.update(book); // 持久
 
// 提交事务,关闭Session
transaction.commit();
session.close();
}
    org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [cn.itcast.domain.firstcache.Book#1]
问题三: 脱管对象 OID 在数据表中不存在,update时,发生异常
    org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): [cn.itcast.domain.firstcache.Book#20]

    3) saveOrUpdate , 如果参数是一个瞬时对象执行save, 如果参数是一个脱管对象执行update, 如果参数是持久对象直接返回
判断对象是瞬时对象 : OID为null , 在hbm文件中为 <id>元素指定 unsaved-value属性,如果PO对象OID为 unsaved-value 也是瞬时对象 
<id name="id" unsaved-value="-1">  如果对象 OID为-1 也是瞬时对象,此时执行的是save操作
@Test
// PO对象,OID为 hbm文件 配置 unsaved-value 也是瞬时对象, saveOrUpdate 执行 save操作
public void demo8() {
// 获得Session
Session session = HibernateUtils.openSession();
// 开启事务
Transaction transaction = session.beginTransaction();
 
Book book = new Book();// 瞬时
book.setId(-1); // 存在OID , -1是unsaved-value 也是瞬时
book.setName("xxx");
book.setPrice(100d);
 
session.saveOrUpdate(book);
 
// 提交事务,关闭Session
transaction.commit();
session.close();
}

    4) get/load 
        如果查询OID不存在, get方法返回 null , load 方法返回代理对象 (代理对象初始化时抛出 ObjectNotFoundException )

    5) delete 方法既可以删除一个脱管对象, 也可以删除一个持久化对象
        **如果删除脱管,先将脱管对象 与 Session 关联,然后再删除 
**执行delete,先删除一级缓存数据,在session.flush 操作时,删除数据表中数据
阅读更多

扫码向博主提问

寒夕若梦

非学,无以致疑;非问,无以广识
去开通我的Chat快问
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页