hibernat之一级缓存的原理

来源:互联网 发布:linux 重启服务器命令 编辑:程序博客网 时间:2024/05/22 17:41

Po对象

Po对象的三种状态以及相互转化的结果

Po对象处于瞬时(临时)状态和脱管状态的时候,都不能直接于数据库交互

只有对象处于持久化状态的时候,才能被保存到数据库

通俗的解释三种状态:

瞬时态:通过new出来的对象,如果是自然主键,则必须指定主键。如果是代理主键(主键己增长)可以没有主键(即使有后来也会被掩盖)

持久态:必须在session处于连接状态,且必须有主键(主键自增长除外),可以与数据库不一样(该对象的主键可以再数据库找不到)

托管态:可以通过new出来,但必须有主键却必须在数据库能找到。

三种状态的相互转化:

瞬时态(产生)转化为持久态:只需要通过save即可,通过sessionsave方法就把一个瞬时态的po对象就变成了持久化状态,

注意:如果是代理主键,即使在瞬时态的时候指定主键(new的时候给了主键),save以后也都会被掩盖,不管数据库有没有与之该对应的,因为如果是代理主键,save会发生抢占主键的动作,不管最后有没有commit,都会去数据库抢主键。如果是自然主键则不会。

持久态的产生:必须在session处于连接的情况下的(且拥有oId),且经过save的临时po对象才能变成持久态,还有就是在session连接状态下,通过get方法获取的一个对象,这中对象不是有瞬时态转化来的,是直接通过id(主键)到数据库查询出来的,所有该po对象直接是持久态。

脱管态的转化:当session关闭以后所有的持久态po对象都变成了拖管状态,所以托管态是一个有id(主键)op对象,除了关闭session能把持久态的op转化为脱管态以外,给一个瞬时态的id赋值(必须在数据库有对应)也能把一个瞬时态转化为托管态。

托管态可以再次转化为持久态,update能把一个托管态转化为持久态,其实还有一个方法也可以save,不过这个方法变数太多,要通过save方法吧一个托管态转化为持久态,必须在save之前没有get(通过该托管对象的id(主键)到数据库去查询)。说的超前一点,必须一级缓存里面没有改对象(没有id与该对象相同),才能save进去,不然会报错。而且通过这种方式save进去的对象,如果没有经过refresh而直接提交,会把数据库里面的原来的数据掩盖。

一级缓存

生命周期:一级缓存的本质是一个map集合,当session创建的时候,map缓存也就产生,当session关闭是,map缓存也就销毁和释放。

缓存的数据:当session打开的时候必然要与数据库连接,那么连接对象什么时候产生,什么时候销毁,怎么样保存呢?其实当session打开的时候,就会与数据库产生一个连接,并把这个连接一直保存(缓存)在一级缓存(map)里面,通过多次查询同一个对象(在session处于连接状态下),可以发现,其实是得到的是同一个对象(内存地址相同),说明在第一次查询的时候产生一个对象,第二次查询的时候没有产生对象,而是直接获取到了上一个的对象,说明第一次查询的时候,产生了对象并把该对象缓存起来了(保存到缓存),当以后查询的时候,就向数据库发请求,这样就大大的增加了查询效率,比较快。

一级缓存原理:当数据被缓存起来了以后,那修改的时候肿么办呢?被缓存的po对象(主要讲解po对象,其实还缓存了很多对象)肯定都是持久化的状态。当请求吧吧数据从数据加载到内存以后,jvm为每个po对象分配一个内存地址,hibernate内部机制在当数据加载到内存的同事,还吧该数据备份了一份(专业叫快照),这才是一级缓存的核心,备份的对象被保存到一级缓存(map)通过键值对(key-value)的方式保存了该副本,并且该副本不能主动(手动设置该对象的属性值)更改,保存的key就是该对象(本体)在内存中的地址(这个地址有jvm自动分配的),value对呀该对象的副本,所以当数据(其实是数据副本)被保存到一级缓存以后,此时内存中的局面就成了,po对象本体在内存中,一级缓存中保存了该po对象的副本和本体的内存地址,如果再次查询,首先到内存中区查找看有没有符合条件的,如果有则把请求返回的对象的内存地址指向在内存中已经存在且满足条件的地址地址,那么这次请求得到的对象就是上次的对象。,如果没有则想数据库发请求,然后把返回的对象加载到内存,并为返回的对象分配内存地址,此时hibernate按照上次的方法吧该对象再次缓存起来(备份到一级缓存),依次类推。快照(备份到一级缓存)中的数据不能主动的被更改。只能通过持久状态的po对象去修改,当持久化对象的po对象的属性值发生变化的时候,调用set属性,或者update该对象的时候,会去比较该po对象的本体与副本的差别,如果发现不一样,则修改快照(一级缓存中副本)的属性值,并始终让副本一本体的属性值保持一致,其实调用po对象的set属性的时候隐式的触发了update方法,可以发现当你在set第一个属性值的时候,在通过id(主键)get该对象的是时候发现第一个属性值已经改变,在set第二个属性,再通过id(主键)get该对象的是时候发现第二个属性值已经改变,一次类推,每set一次属性,get一次对象,发现对象属性的更改书瞬间完成的,不是等你set完了再一次性更改副本,这样使的更改更加及时和精确。所以用户(程序员)只能手动更改ppo对象的属性值,而不能直接更改快照里面的值,只有hibernate自己才能更改快照。其实更改原理就是当po对象的属性值发生变化的时候,通过该po对象的内存地址去一级缓存或者说快照里通过key(此时key就是该po对象的内存地址)起找到该对象的副本然后对比修改。随带提一下,不管是update,还是set,只要最后没有commit,数据都不会保存到数据库,所有的修改和变化都是内存和快照之间的数据交互和统一。

(一级)缓存的刷出和统一:这里说的统一主要是主要是内存的数据与快照的数据统一,与数据库无关。而刷出则是内存的数据和数据库的数据保持一致。缓存的统一通过flush(其实如果不出意外,内存的数据和快照的数据应该是保持一致的),因为快照的数据不能手动修改也没有办法证实,其实每次在commit提交数据的时候都会隐式的调用flush方法,但是与数据库无关可以证明,首先在flush之前加一个断点,然后让程序执行2get方法并打印,第一次打印和第二次get之间加一个flush,当控制台打印第一次get对象的数据和内存地址的时候(此时还没有执行第二次get方法),然后手动的去数据库修改数据库的字段值(修改并提交),然后程序继续向下,get第二次,并打印,你会发现,里面的内容(对象属性值)和内存地址一模一样,说明flush与数据无关。缓存的刷出通过refersh方法,此时是以数据库数据为标准,然后更新内存中的数据(po对象的属性值,内存地址没有改),同样的方法,get两次打印2次,在第一次打印和第二次get之间refersh,并加上断点(和上面的方法一模一样,只是吧flush换成refersh),你会发现当你吧数据库字段的值修改提交以后,2次打印的数据不一样(对象属性值),但是值得注意的是内存地址居然是一模一样的,说明刷出模式,值修改了对象的内容,并没有吧对象销毁在重造。

 

 

 

 

 

 

 

 

 

 

 

 

 

0 0
原创粉丝点击