持久化对象的状态
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的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);
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