Hibernate[延迟加载] [三种状态] [脏检查] [缓存机制]
一、持久化对象的唯一标识
java中按内存地址不同区分同一个类的不同对象,关系数据库用主键区分同一条记录,Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系
什么是OID?
解析:OID是持久化与数据表主键对应属性,用来区分持久化对象
------------------------------------------------------------------------------------------------------------------------------------------------
二、主键生成策略
increment
identity
sequence
native
uuid
assigned
1) increment
由hibernate完成 主键递增,
原理:select max(id) , insert时max(id)+1 ,完成主键递增
优点:跨数据库
缺点:多线程并发访问问题(第一个线程执行成功,第二个线程报错)
2) identity
由底层数据库来完成自增 ,要求数据库必须支持自增主键 mysql支持 ,oracle不支持
3) sequence
编号列生成由底层数据库提供序列,来完成主键自增,要求数据库必须支持序列 mysql不支持,oracle支持
create sequence myseq; 创建序列
insert into customer values (myseq.nextval); 插入数据时调用序列,序列+1
4) native
采用数据库支持自增策略, mysql就用identity 、oracle就用sequence
策略1) ---> 策略4) 要求数据库主键必须为数字 ,因为只有数字才能自增
5) uuid
32位 唯一字符串, 主键使用varchar 类型
真实开发中,用程序提供uuid值
6) assigned
手动指定主键的值,该主键一般有实际意义,例如订单单号(20160114-A002)20160114-B001 20160114-C002。
复合主键,是一种特殊 assigned类型 自然主键 (通常需要手动指定),PO类必须实现Serializable接口
<class name="cn.happy.entity.Person" table="person">
<composite-id>
<key-property name="firstname"></key-property>
<key-property name="secondname"></key-property>
</composite-id>
</class>
7)foreign
使用另外一个相关联的对象的标识符。它通常和 <one-to-one>
联合起来使用。
8)hilo
使用一个高/低位算法高效的生成 long
,short
或者 int
类型的标识符。给定一个表和字段(默认分别是hibernate_unique_key
和 next_hi
)作为高位值的来源。高/低位算法生成的标识符只在一个特定的数据库中是唯一的。
9)select
通过数据库触发器选择一些唯一主键的行并返回主键值来分配一个主键。
三、延迟加载
延迟加载是Hibernate为提高程序执行效率而提供的一种机制,即只有真正需要数据的时候,才真正执行数据加载操作。
get()和load()区别:
① 在程序中提供的OID,对应的底层数据库没有编号.load()报错,get()得到null
② Load()没有使用对象的其他属性的时候,没有SQL 延迟加载
Get() :没有使用对象的其他属性的时候,也生成了SQL 立即加载
get()方法的执行顺序如下:
1 :首先通过id在session缓存中查找对象,如果存在此id的对象,直接将其返回
2:在二级缓存中查找,找到后将 其返回。
3 :如果在session缓存和二级缓存中都找不到此对象,则从数据库中加载有此ID的对象
因此get()方法并不总是导致SQL语句,只有缓存中无此数据时,才向数据库发送SQL!
是什么导致了延迟加载?
解析:因为内存中构建了代理对象
/* * get */ @Test public void getTest(){ Student stu=(Student)session.get(Student.class, 12); System.out.println(stu); } /* * load */ @Test public void loadTest(){ //直接返回的是代理对象 Student stu=(Student)session.load(Student.class, 12); System.out.println(stu.getClass()); System.out.println(stu); }
四、java对象的三种状态
持久态:
Student stu=new Student()
Session.save(stu);
Session以及数据库都有
游离态:
stu.setId(1);
Session.close();
Session没有 数据库中有
瞬时态:
Student stu=new Student()
体现:在Session中以及DB都没有
三种状态之间的转换:
该图从类型上划分为“活动图”
使用new关键字构建对象,该对象的状态是瞬时状态。
1 瞬时状态转为持久状态
使用Session对象的save()或saveOrUpdate()方法保存对象后,该对象的状态由瞬时状态转换为持久状态。
使用Session对象的get()或load()方法获取对象,该对象的状态是持久状态。
2 持久状态转为瞬时状态
执行Session对象的delete()方法后,对象由原来的持久状态变为瞬时状态,因为此时该对象没有与任何的数据库数据关联。
3 持久状态转为游离状态
执行了Session对象的evict()、clear()或close()方法,对象由原来的持久状态转为游离状态。
4 游离状态转为持久状态
重新获取Session对象,执行Session对象的update()或saveOrUpdate()方法,对象由游离状态转为持久状态,该对象再次与Session对象相关联。
5 游离状态转为瞬时状态
执行Session对象的delete()方法,对象由游离状态转为瞬时状态。
处于瞬时状态或游离状态的对象不再被其他对象引用时,会被Java虚拟机按照垃圾回收机制处理。
五、脏检查以及刷新缓存机制
Session到底是如何进行脏检查的呢?当一个Customer对象被加入到Session缓存中时,Session会为Customer对象的值类型的属性复制一份快照。当Session清理缓存时,会先进行脏检查,即比较Customer对象的当前属性与它的快照,来判断Customer对象的属性是否发生了变化,如果发生了变化,就称这个对象是“脏对象”,Session会根据脏对象的最新属性来执行相关的SQL语句,从而同步更新数据库。
Hibernate的一级缓存其实就是Session内置的一个Map,用来缓存它操作过的实体对象,对象的主关键字ID是Map的key,实体对
象就是对应的值。一级缓存是以实体对象为单位进行存储的,访问时也是以实体为单位的(直接访问属性是不能使用缓存的),并且要求使用主关键字ID来进行访问
六、Session<线程非安全>/SessionFactory<线程安全>
SessionFactoryImpl的源码。里面的实例变量大部分是final类型的,不可改变
SessionImpl的源码。大部分的实例变量是transient非final类型的。