hibernate關聯對象的增刪改查------查,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文檔,或其他的資料。
那既然這樣,我為什麼還要寫這篇部落格呢?反正最好的方法就是保持預設情況嘛。
因為:
世之奇偉、瑰怪、非常之觀,常在於險遠,而人之所罕至焉,故非有志者不能至也。
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。