zoukankan      html  css  js  c++  java
  • hibernate

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

    一、一级缓存和快照

        什么是一级缓存呢?

          很简单,每次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=?
    复制代码

         总结一下:通过上面的例子和讲解,我们应该知道,并不是session一操作对象,就立马会发sql语句,而是先将其保存到自己内部的一级缓存中,如果不手动刷出缓存,就会等待事务提交时刷出缓存,然后再进行发送sql语句,对数据库进行操作。并且其中有一个快照区需要知道是干嘛的,理解了快照区,就可以知道每次会发送几条sql语句。注意一点,session每次操作的是在一级缓存中的对象,不会操作快照区中的对象,快照区只是用来当作比较的一个地方,当对象加入一级缓存之后,外部在怎么改变,也只是改变快照区中的数据,并不是改变一级缓存中对象的属性。这点就记清楚。如果还不是很清楚,等下面在带你们看几个例子,就瞬间明白了。

    二、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");

    复制代码

    6、游离状态变为瞬时状态

         这个也没什么好讲的,上面已经分析过了,游离状态直接变为瞬时状态其实也就是先进过持久状态,然后再变为瞬时状态。


    三、经过上面的学习,现在深入一点,让你知道会发送多少条sql语句。

         1、Test01

    复制代码
           session = HibernateUtil.openSession();
                session.beginTransaction();
                User user = new User();
                user.setUsername("aaa");
                user.setPassword("aaa");
                user.setBorn(new Date());
                //以上u就是Transient(瞬时状态),表示没有被session管理并且数据库中没有
                //执行save之后,被session所管理,而且,数据库中已经存在,此时就是Persistent状态
                session.save(user);
                //此时u是持久化状态,已经被session所管理,当在提交时,会把session中的对象和快照区的对象进行比较
                //如果两个对象中的值不一致就会继续发出相应的sql语句
                user.setPassword("bbb");
                //此时会发出2条sql,一条用户做插入,一条用来做更新
                session.getTransaction().commit();
    复制代码

        Hibernate: insert into t_user (born, password, username) values (?, ?, ?)

        Hibernate: update t_user set born=?, password=?, username=? where id=?

        Test02

    复制代码
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                session = HibernateUtil.openSession();
                session.beginTransaction();
                User u = new User();
                u.setBorn(new Date());
                u.setUsername("zhangsan");
                u.setPassword("zhangsan");
                session.save(u);
                u.setPassword("222");
                //该条语句没有意义
                session.save(u);
                u.setPassword("zhangsan111");
                //没有意义
                session.update(u);
                u.setBorn(sdf.parse("1988-12-22"));
                //没有意义
                session.update(u);
                session.getTransaction().commit();
    复制代码

        没有意义是什么意思呢?记得我一开始说的那个注意点嘛,所有的操作都会先存放在session的一级缓存中,当对象进入一级缓存中,在怎么改变属性,都只改变快照区中对象的属性,在用session进行操作,还是操作的一级缓存中的对象,记住了这一点,那么上面的程序就简单了,u保存到了缓存中,u改变属性,session在save u没一点意义,并且连续改改,也只是改快照区中的属性,等到事务提交的时候,在做对比和进行相应的操作。

        Hibernate: insert into t_user (born, password, username) values (?, ?, ?)

        Hibernate: update t_user set born=?, password=?, username=? where id=?

       Test03

    复制代码
           SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                session = HibernateUtil.openSession();
                session.beginTransaction();
                User u = new User();
                u.setId(5);
                //完成update之后也会变成持久化状态
                session.update(u);
                u.setBorn(sdf.parse("1998-12-22"));
                u.setPassword("lisi");
                u.setUsername("lisi");
                //会抛出异常
                u.setId(333);
                session.getTransaction().commit();
    复制代码

          这个例子会报异常,就是上面我说过的,在一级缓存中的对象,如果改变快照区中的id属性,就会报异常

  • 相关阅读:
    leetcode 576. Out of Boundary Paths 、688. Knight Probability in Chessboard
    leetcode 129. Sum Root to Leaf Numbers
    leetcode 542. 01 Matrix 、663. Walls and Gates(lintcode) 、773. Sliding Puzzle 、803. Shortest Distance from All Buildings
    leetcode 402. Remove K Digits 、321. Create Maximum Number
    leetcode 139. Word Break 、140. Word Break II
    leetcode 329. Longest Increasing Path in a Matrix
    leetcode 334. Increasing Triplet Subsequence
    leetcode 403. Frog Jump
    android中webView加载H5,JS不能调用问题的解决
    通过nginx中转获取不到IP的问题解决
  • 原文地址:https://www.cnblogs.com/pjlhf/p/8742708.html
Copyright © 2011-2022 走看看