JAVAWEB开发之Hibernate详解(二)——Hibernate的持久化类状态与转换、以及一级缓存详解、关联关系的映射(一对多、多对多、级联)

持久化对象的状态

 Hibernate持久化对象存在三种状态:
瞬时态 transient:(临时态)  不存在持久化标识OID,尚未与Hibernate  Session关联对象,被认为是瞬时状态,失去引用将被JVM回收。特点:无持久化标识OID,未与Session关联
持久态 persistent:存在持久化标识OID,与当前Session有关联,并且相关联的Session没有关闭,并且事务未提交。特点:存在持久化标识OID,与Session关联。*****注意 持久态对象具有自动更新数据库的能力。
托管态 detached:存在持久化标识OID,但没有与当前session关联,托管状态发送改变  Hibernate不能检测到。特点:存在持久化标识OID,未与Session关联

测试Hibernate中持久化对象的状态:

对象的状态总结:

持久化对象状态转换:

三种状态的获得与转换如下:
瞬时态:
       *获得:
                Book book= new Book();
       *瞬时—>持久
                * save(book);
                * save() / saveOrUpdate();
       *瞬时—>脱管
                * book.setId(1);
持久态:
       *获得:
                Book book = (Book)session.get(Book.class, 1);
       *持久—>瞬时
                * delete(book);
                     *特殊状态:删除态.(被删除的状态,不建议去使用)
       *持久—>脱管
                * session.close();
                * close() / clear() / evict();
托管态:
       *获得:
                Book book = new Book();
                book.setId(1);
       *脱管—>持久
                * session.update();
                * update() / saveOrUpdate() / lock();
       *脱管—>瞬时
                * book.setId(null);
注意:持久态对象有自动更新数据库的能力;

Hibernate的一级缓存(Session缓存)

什么是缓存:

       * 缓存将数据库/硬盘上文件中的数据,放入到缓存中(就是内存中的一片空间)。当再次使用的时候,可以直接从内存中获得。
缓存的好处:
       * 提升程序运行的效率。缓存技术是Hibernate的一个优化的手段。

Hibernate分成两个基本的缓存:

  • 一级缓存:Session级别的缓存.一级缓存与Session的生命周期是一致的。自带的,不可卸载。
  • 二级缓存:SessionFactory级别的缓存,不是自带的。

深入理解Hibernate中的Session缓存:

  • 在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存。只要Session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。
  • 当session的save()方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清理,该对象仍然处于生命周期中。当试图get()、load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库。没有再查询数据库。
  • Session能够在某些时间点,按照缓存中对象的变化来执行相关的SQL语句,来同步更新数据库,这一过程被称为刷出缓存(flush)。

默认情况下Session在以下时间点刷出缓存:

  • 当应用程序调用Transaction的commit()方法时,该方法先刷出缓存(session.flush() ),然后再向数据库提交事务(tx.commit() )。
  • 当应用程序执行一些查询操作时,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态。
  • 手动调用Session的flush()方法。

注意:flush与commit是有区别的,区别如下:

  • flush()方法进行清理缓存的操作,执行一系列的SQL语句,但不会提交事务;commit()方法会先调用flush()方法,然后提交事务. 
  • 提交事务意味着对数据库所做的更新会永久保持下来 
  • 所谓清理,是指Hibernate 按照持久化象的状态来同步更新数据库
  • Flush()后只是将Hibernate缓存中的数据提交到数据库,如果这时数据库处在一个事物当中,则数据库将这些SQL语句缓存起来,当Hibernate进行commit时,会告诉数据库,你可以真正提交了,这时数据才会永久保存下来,也就是被持久化了.
  • commit针对事物的
  • flush针对缓存的
  • 同步到数据库中后只要没有commit还是可以rollback的

Hibernate快照区:

当session加载了customer对象后,会为customer对象的值类型的属性复制一份快照。当刷出缓存时,通过比较对象的当前属性和快照,来判断对象的哪些属性发生了变化  
                
 向一级缓存中存入数据的时候,放入一级缓存区和缓存快照区,当更新了一级缓存的数据的时候,事务一旦提交,会比对一级缓存和快照区,如果数据一致,不更新,如果数据不一致,自动更新数据库。

Hibernate管理一级缓存:

一级缓存是与session的生命周期相关的.session生命周期结束,一级缓存就结束了。
  • clear() / evict() / flush() / refresh()  管理一级缓存
  • clear()                  :清空一级缓存中的所有对象。
  • evict(Object obj)  :清空一级缓存中的某个对象。
  • flush()                  :刷出缓存。
  • refresh(Object obj):将快照区的数据重新覆盖了一级缓存的数据。
清理session的缓存,测试flush和clear、evict方法的使用
session的flush方法让缓存的数据刷出到数据库
session的clear方法清空缓存数据         

执行过程:调用session的flush方法后会立即发出查询语句,然后将id为1的customer对象分别复制一份到session的快照区与缓存区。获取customer2时会首先查找比对缓存区中的对象 发现已经存在了OID为1的Customer对象,不再查询数据库,立即返回缓存区中的对应对象。最后调用session的flush方法,会将一级缓存区中的所有对象清除,获取customer3时,先上session一级缓存区中进行查找,没有查找到,再向数据库发出SQL语句进行查询。

session的evict方法清空指定对象一级缓存数据,使对象变为离线

执行过程:第一次获取customer对象 会发出SQL语句进行查询(因为在Session缓存区中没有找到),从数据库查询成功后,还是先将customer各复制一份到Session的缓存区与快照区。调用Session的evict方法,会将customer对象从Session的缓存区与快照区中清除,再次获取时 就会发出SQL查询语句。

refresh刷新一级缓存
当session.load 加载Customer对象后,修改city为 武汉,调用refresh方法更新一级缓存,此时设置的武汉重新被数据表中记录覆盖

一级缓存的刷出时机

清理session的缓存(设置缓存的flush模式)
   session.setFlushMode(FlushMode.AUTO);

FlushMode:
常量:
       * ALWAYS  :每次查询的时候都会刷出;手动调用flush;事务提交的时候。
       * AUTO      :默认值。有些查询会刷出;手动调用flush;事务提交的时候。
       * COMMIT  :在事务提交的时候,手动调用flush的时候。
       * MANUAL  :只有在手动调用flush的时候才会刷出。
****** 严格程度:MANUAL > COMMIT > AUTO > ALWAYS
ALWAYS和AUTO的区别:当hibernate缓存中的对象被改动之后,会被标记为脏数据(即与数据库不同步了)。当 session设置为FlushMode.AUTO时,hibernate在进行查询的时候会判断缓存中的数据是否为脏数据,是则刷数据库,不是则不刷, 而always是只要执行查询都直接刷新,不进行任何判断。很显然auto比always要高效得多 
看如下代码图:

会不会执行更新到数据库?
是不会更新到数据库的,因为设置了MANUAL模式,只有在手动调用flush的时候才会刷出缓存。

操纵持久化对象的方法

操作持久化对象 —save()

Session的save()方法保存一条记录,使一个瞬时态的对象转变为持久态对象。
Session的save()方法可以完成以下操作:
  • 把瞬时态对象加入Session缓存中,使它进入持久化状态
  • 选用映射文件指定的标识符生成器,为持久化对象分配唯一的OID,在使用代理主键的情况下,setId()方法为瞬时对象设置的OID是无效的。
  • 计划执行一条insert语句,把Customer对象当前的属性值组装到insert语句中
Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系,当Customer对象处于持久化状态时,不允许程序随意修改它的ID。

操纵持久化对象—update()

Session的update方法使一个托管对象转变为持久化对象,并且计划执行一条update语句。

以上默认情况下 不管Customer对象c的内容是否发生改变,都会执行更新操作。

若希望Session仅当修改了Customer对象的属性时,才执行update()语句,可以把映射文件中<class> 元素标签上的属性值select-before-update(更新之前先查询)设置为true,该属性的默认值是false。


当update()方法关联一个托管对象时,如果在Session的缓存中已经存在相同OID的持久化对象,就会抛出异常。

执行过程:执行evict(c);方法后对象c变为托管态的游离对象。查询获得c1对象并存入session缓存中,更新游离对象c时会从缓存区中查找和c1相同的对象(注意:缓存即内存,在缓存中查找相同对象是按照对象在内存中的地址进行比对的 因为c和c1的内存地址不同,c会保存到session缓存区中,这时Hibernate检查出缓存区中两个内存地址不同的对象拥有相同的OID时就会抛出异常,因为Hibernate是按照OID进行比对的,是不允许缓存区中存在两个内存地址不同而OID相同的对象的)
抛出异常的原因是:两个不同的对象拥有相同的OID

当update()方法关联一个托管对象时,如果在数据库中不存在相应的记录,也会抛出异常。

操作持久化对象—saveOrUpdate()

saveOrUpdate():
该方法同时包含save方法和update方法,如果参数是瞬时对象就调用save方法,如果是脱管对象就调用updat
  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值