zoukankan      html  css  js  c++  java
  • hibernate关联对象的增删改查------查

    本篇博客是之前博客hibernate关联对象的增删改查------查 的后继,本篇代码的设定都在前文已经写好,因此读这篇之前,请先移步上一篇博客

       


       //代码片5
       

        SessionFactory sessionFactory = new AnnotationConfiguration().configure().buildSessionFactory();
            Session session = sessionFactory.getCurrentSession();
            session.beginTransaction();
                
            Dream d=new Dream();
            d.setDescription("marry glt");
            
            Person p=new Person();
            p.setName("dlf");
            
            d.setPerson(p);    
            session.save(d);
            session.save(p);
            session.getTransaction().commit();        
            
            session = sessionFactory.getCurrentSession();
            session.beginTransaction();
            Dream dream=(Dream) session.get(Dream.class, 1);
            System.out.println(dream.getPerson().getName()+"  ddddd");
            session.getTransaction().commit();    


    相应代码5而言,get dream的时候发的sql语句为
     select
            dream0_.id as id0_1_,
            dream0_.description as descript2_0_1_,
            dream0_.personId as personId0_1_,
            person1_.id as id1_0_,
            person1_.myname as myname1_0_
        from
            Dream dream0_
        left outer join
            Person person1_
                on dream0_.personId=person1_.id
        where
            dream0_.id=?


    换言之,get dream的时候,我们也获得了相应的person。


    有3个问题
    1 运行代码5的时候,dream的manytoone的cascade已经去掉了。
    2 假设把Dream dream=(Dream) session.get(Dream.class, 1);与之前保存对象的语句放到一个session里,发的sql语句居然是update。可是,System.out.println(dream.getPerson().getName()+"  ddddd");依旧能输入person的name。

    我们能得出规律
    在多对一,一对多的情况下,当我们读取多的一方时,默认也会读取一的一方。

    (这个规律与cascade无关)
    反过来,假设我读一的一方呢,会不会也自己主动读出多的一方呢?
    我们看代码

            //代码6
        public void testGetPerson() {
            Session s = sessionFactory.getCurrentSession();
            s.beginTransaction();
            Person p = (Person)s.get(Person.class, 2);
            s.getTransaction().commit();
        
        }
    这个时候它发的sql语句是:
        select
            person0_.id as id1_0_,
            person0_.myname as myname1_0_
        from
            Person person0_
        where
            person0_.id=?
    并没有去主动获得person相应的dream。


    那假设我想获得dream呢?
    hibernate的管理关系中另一个參数叫fetch。它管的就是在读取某个对象时,是否须要读取与之相关的另一个对象。


    通过查阅api文档,我们知道fetch的取值是fetchtype型的。

    而fetchtype是个Enum类型的。


    能够取值LAZY与EAGER。

    这个两个值是什么意思?
    猜一猜,我们大概都是知道,eager就是主动获取关联对象的数据,lazy就是不获取么。
    我仅仅能说,大概是对的。
    我们看代码
    在Person里面改动OneToMany的属性。
        @OneToMany(mappedBy="person",fetch=FetchType.EAGER)
        public Set<Dream> getDreams() {
            return dreams;
        }
    此时再执行代码6,发的sql语句就是:
     select
            person0_.id as id1_1_,
            person0_.myname as myname1_1_,
            dreams1_.personId as personId3_,
            dreams1_.id as id3_,
            dreams1_.id as id0_0_,
            dreams1_.description as descript2_0_0_,
            dreams1_.personId as personId0_0_
        from
            Person person0_
        left outer join
            Dream dreams1_
                on person0_.id=dreams1_.personId
        where
            person0_.id=?
    我们就能知道,假设想在取一的时候同一时候取多的一方,就在一的一方上加上fetch=feachType.eager。
    那么依据前面的代码,我们就能猜測出来
    在默认情况下
    一的那一方的fetch是lazy
    多的那一方的fetch是eager
    用eager修饰关联关系:hibernate会发关联的sql
    用lazy修饰关联关系:hibernate不会主动发关联sql

    注意,我上面说的是 用lazy修饰关联关系:hibernate不会发主动发关联的sql
    为什么说主动呢?看看以下的代码
    我们改一改代码6
        //代码7
        public void testGetPerson() {        
            Session s = sessionFactory.getCurrentSession();
            s.beginTransaction();
            Person p = (Person)s.get(Person.class, 2);
            System.out.println(p.getDreams().size());
            s.getTransaction().commit();
        }
    此时不设置person的fetch值。

    (保持默认的lazy)
    此时发的sql语句是:

    Hibernate:
        select
            person0_.id as id1_0_,
            person0_.myname as myname1_0_
        from
            Person person0_
        where
            person0_.id=?

    Hibernate: select dreams0_.personId as personId1_, dreams0_.id as id1_, dreams0_.id as id0_0_, dreams0_.description as descript2_0_0_, dreams0_.personId as personId0_0_ from Dream dreams0_ where dreams0_.personId=?

    此时我们能够得到一个结论,假设在session里,我们仅仅是获得"一"的那一方,hibernate默认不会去取多的那一方;可是假设在session里,訪问了获得的"一"里面"多"的那一方数据(就是訪问了person里面的dream)。

    就会发关联sql。

    如此一来,就有了一个比較尴尬的事了
    无论我在一的那一方设不设立fetch=FetchType.eager,我在session里面获得多的那一方的时候,都是能够的。
    此时,还不如不设置fetch=FetchType.eager呢,由于有的时候,我确实不须要获得多的那一方,假设一的那一方设置成eager,岂不是要多查询非常多没用的数据。


    再看一个样例:

        //代码8
        public void testGetPerson() {
            
            testSavePerson();
            
            Session s = sessionFactory.getCurrentSession();
            s.beginTransaction();
            Person p = (Person)s.get(Person.class, 2);
            s.getTransaction().commit();
            System.out.println(p.getDreams().size());
        }
    就是把获得多的那一方数据的代码放到了session的外面。


    假设此时person那边还有fetch=FetchType.eager,那么一切OK
    屏幕上输出:

    Hibernate:
        select
            person0_.id as id1_1_,
            person0_.myname as myname1_1_,
            dreams1_.personId as personId3_,
            dreams1_.id as id3_,
            dreams1_.id as id0_0_,
            dreams1_.description as descript2_0_0_,
            dreams1_.personId as personId0_0_
        from
            Person person0_
        left outer join
            Dream dreams1_
                on person0_.id=dreams1_.personId
        where
            person0_.id=?
    2   //dream的size是2
    但是假设我把fetch=FetchType.eager去掉,在执行代码8,就会报错:
    <span style="color:#FF0000;">failed to lazily initialize a collection of role: com.bjsxt.hibernate.Person.dreams, no session or session was closed</span>
    说的非常清楚,session已经关闭了。
    为什么呢?
    我们知道,从person到dream,是一对多,而默认情况下,一对多的fetch是lazy的。
    也就是说,正常情况下,取了pseron是不会再取dream。
    我们注意代码7发出的sql,是两个。
    如今我要获得多的那方的命令,跑到了sesssion的外面,也就是说在session关闭后再去数据库里执行sql,那铁定会报错喽。
    我们再试试:
        //代码9
        public void testGetPerson() {
            
            testSavePerson();
            
            Session s = sessionFactory.getCurrentSession();
            s.beginTransaction();
            Person p = (Person)s.get(Person.class, 2);
            s.getTransaction().commit();
            System.out.println(p.getDreams().size());
            s.getTransaction().commit();
        }
    此时的person的fetch还是默认的lazy。
    是否会报错呢?自己试试吧。


    关于取对象这部分,hibernate还是比較麻烦的,我觉得最好的方法就是保持默认的情况,一旦有了问题再查api文档,或其它的资料。

    那既然这样,我为什么还要写这篇博客呢?反正最好的方法就是保持默认情况嘛。

    由于:

          世之奇伟、瑰怪、很之观。常在于险远。而人之所罕至焉,故非有志者不能至也。



    感谢glt


  • 相关阅读:
    PHP和数据库相关的事
    angular的学习进阶(十七)
    javascript原生技巧篇
    ScrollReveval.js 视差滚动的探索
    django 武沛齐老师-最后一篇随笔
    去除HTML中input[type="number"]的默认样式
    git push时报错 ssh: Could not resolve hostname origin: Name or service not known fatal: Could not read from remote repository.
    Heroku 免费容器服务申请,搭建 V2+CF 方法教程
    96000个「企业码」背后,余杭区政企数智化能量爆发
    数据生态的过去、现在和未来
  • 原文地址:https://www.cnblogs.com/tlnshuju/p/6796053.html
Copyright © 2011-2022 走看看