★hibernate(二)一级缓存和三种状态解析

一、一级缓存和快照

    什么是一级缓存呢?

      很简单,每次hibernate跟数据库打交道时,都是通过session来对要操作的对象取得关联,然后在进行操作,那么具体的过程是什么样的呢?

        1、首先session将一个对象加入自己的管理范围内,其实也就是把该对象放入自己的一级缓存中,例如,session.save(xxx);这个语句就是将xxx保存在自己的一级缓存中,等待事务提交后,hibernate才真正的发sql语句,对数据库进行操作。注意:session进行操作的时候,是将对象加入自己的一级缓存,并不是就直接跟数据库打交道了。

        2、在一级缓存中会做些什么事情呢?为什么能够知道是发insert、还是update又或者delete呢?那这里就要提到一个快照的概念了,讲讲内部是什么原理。

举例来说明问题吧。

session.save()

User user = new User();
        user.setUsername("xiaoming");
        user.setPassword("123");
        
        session.save(user);//加入了一级缓存,这其中做了什么事情呢?看图
        user.setUsername("baibai");//那这里改了user的属性,具体会发生什么事情呢
        //提交事务
        tx.commit();
        //关闭session,持久化对象就会变为脱管状态。
        session.close();

        

 发送的sql语句 

//insert语句
Hibernate: 
    insert 
    into
        user
        (username, password, c_id) 
    values
        (?, ?, ?)
//update语句
Hibernate: 
    update
        user 
    set
        username=?,
        password=?,
        c_id=? 
    where
        id=?

session.get()

        //通过图中分析,通过select语句查询,并且user会在session的一级缓存中,
        User user =(User)session.get(User.class, "8");
        //会发出update语句。
        user.setUsername("heihei");

分析图

      

结果

//select语句
Hibernate: 
    select
        user0_.id as id0_0_,
        user0_.username as username0_0_,
        user0_.password as password0_0_,
        user0_.c_id as c4_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?
//update语句
Hibernate: 
    update
        user 
    set
        username=?,
        password=?,
        c_id=? 
    where
        id=?

在举一个例子说明有一级缓存的存在,对于缓存来说,肯定有对缓存的一些操作,比如evict(xx)清除xx缓存、clear():清除一级缓存中所有数据、flush()刷出一级缓存(也就是不用事务提交缓存就刷出来了,就会发sql语句,事务提交也是一个刷出缓存的方法,) 就用上面这个get的例子

session.evict()


        //通过图中分析,通过select语句查询,并且user会在session的一级缓存中,
        User user =(User)session.get(User.class, "15");
        //将user移除session一级缓存
        session.evict(user);
        //不会发出update语句。
        user.setUsername("heihei");      

 结果就发送一条select查询语句

Hibernate: 
    select
        user0_.id as id0_0_,
        user0_.username as username0_0_,
        user0_.password as password0_0_,
        user0_.c_id as c4_0_0_ 
    from
        user user0_ 
    where
        user0_.id=?

二、hibernate中对象的三种状态  

       瞬时状态transient、持久状态(托管)persistent、游离(脱管)detached状态      
          注意:托管、脱管要分清楚,分不清楚就用持久和游离

      瞬时状态:使用new操作符初始化的对象的状态就是瞬时的,

          1、在数据库表中,没有任何一条数据与它对应

          2、不在session的缓存中

      持久状态:在session的缓存中,

          1、在session的缓存中(注意,很多书上都觉得持久化状态都在数据库表中有相应记录,这个是错误的,比如一个瞬时状态的对象刚被session.save(),事务还没提交,此时瞬时状态就已经变为持久化状态了,但是在数据库中还没有记录)

          2、在数据库中可能有记录。

      脱管状态:从session的缓存中移除出来了

          1、是从session缓存中出来的,也就是从持久状态转变而来的,没有别的方式能到达游离(脱管)状态,只有这一种。

          2、不在session的管理范围内

          3、在数据库中有记录

        

      根据上面的文字性描述,可能还不太理解,那现在根据我画的图和官方给予的图来加深印象吧。

 

误区1、很多书上觉得通过id值就可以判断对象是在哪个状态,比如说,没有id值,就是瞬时状态,有id并且在session管理范围内,就是持久状态,有id不在session范围内就是脱管状态,   

解释:从上面的解释来看,瞬时状态变为脱管状态,只要加上id就行了,但是从官方图来说,瞬时状态不能直接变为游离状态,而游离状态可以通过delete直接到达瞬时状态(其实说直接,也是先将游离状态的对象加入到session缓存中变为持久状态,然后delete,在变为瞬时状态而已。),那么上面所用的通过有没有id值来判断三种状态就是有偏差的,可以这么理解,瞬时状态在数据库中就一定没有对应的记录,而游离状态一定是通过持久状态转变而来的,并且在数据库中可能有记录(没有记录是因为多线程,有别的程序把那条记录给删除了,所以一般就觉得是有记录的),所以瞬时状态和游离状态的区分点就在:从什么转变而来的,如果直接new的,那么就是瞬时状态对象,如果从持久态转变的,那么就是游离状态。

 误区2:很多书上或者博客中会说识别是不是持久化状态,是看在session缓存内,还有就是在数据库中有记录。

解释:这里的前半句对,但是后半句错误,持久状态可能在数据库中有记录,也可能在数据库中没有记录,说说没有记录的时候吧,就是当session.save保存瞬时状态时,事务还没有提交,对象还只是在session的一级缓存中,数据库中就没有该记录,但此时它就已经是持久状态对象了。

    

误区3:认为session一操作,就会发出sql语句,这个上面已经说明白了,应该很清楚了。

   

   小总结一下:怎么区分这三种状态

          1、如果是在session缓存中,那么就一定是持久状态

          2、如果是刚new出来的对象,那么就肯定是瞬时状态

          3、如果是从session缓存中出来的,也就是通过一些session.clear、ecivt等操作,清除了缓存的,那么就是游离状态,

   只有瞬时状态能确定数据库中没有对应记录,其他两个状态,都是不确定数据库中是否有对应记录

      一切都以官方给出的图为准,其他的快速识别状态的方法,我认为是不可取的,自己还是没弄明白其中的道理,也就永远没把握自己是不是对的。

    

    讲了那么久的理论,现在就通过那张官方给的图,让我们来实验试验各种状态间的转换吧。

1、怎么变成瞬时状态、和如何从瞬时状态变为持久状态?

        User user = new User();//瞬时状态
        user.setUsername("aaa");//瞬时状态
        session.save(user);//持久状态,这里使用saceOrUpdate也一样。将user放在session的一级缓存中,快照区也有一份        
        //提交事务
        tx.commit();//事务提交之后,才发送sql语句
        //关闭session
        session.close();//session关闭后,user就变为游离(脱管)状态了

2、怎么直接变为持久状态   

        //从数据库中查询id为7的用户,并且此时user在session的一级缓存中,快照区也有一份一样的
        User user = (User) session.get(User.class, "7");
        
        //提交事务
        tx.commit();
        //关闭session,持久化对象就会变为脱管状态。
        session.close();

3、持久状态变为瞬时状态

        //并且user会在session的一级缓存中,持久状态,快照区也有一份一样的
        User user =(User)session.get(User.class, "15");
        //将user移除session一级缓存,并且从数据库中删除该记录,快照区中的对象也删除了,所以瞬时状态,而不是变成游离状态          
        session.delete(user);
        //不会发出update语句。改变的只是瞬时状态的属性
        user.setUsername("heihei");
                                                             

4、持久状态变为游离状态

        //并且user会在session的一级缓存中,持久状态,快照区中也有一份一样的
        User user =(User)session.get(User.class, "15");
        //将user移除session一级缓存,并没有删除数据库记录,所以变为了游离状态,清除缓存,那么快照区中也没了
        session.evict(user);
        //不会发出update语句。改变的只是游离状态的属性,没影响
        user.setUsername("heihei");

5、游离状态变为持久状态

        //并且user会在session的一级缓存中,持久状态,快照区有一份一样的
        User user =(User)session.get(User.class, "15");

        //将user移除session一级缓存,并没有删除数据库记录,所以变为了游离状态。快照区中也没了
        session.evict(user);

        //不会发出update语句。改变的只是游离状态的属性,没影响
        user.setUsername("heihei");

        //会发出update语句进行更新,重新回到持久状态。加入到一级缓存中。快照区中也有一份一样的。
        session.update(user);

     //猜一下这里会不会发两条update语句?如果觉得是两条的话,就没记住我说的话,肯定是一条update语句呀,自己可以看上面讲解快照区时的图片,这里改变了快照区中user的属性,
     //因为上面用的是update方法,快照区会先跟一级缓存中的对比,如果有不一样,那么就发送update语句,而不会先发一个update然后再对比,不一样在发update,跟insert不一样。
     user.setUsername("sss");

转自http://www.cnblogs.com/whgk/p/6103038.html

猜你喜欢

转载自blog.csdn.net/qq_39665334/article/details/82946699