Re: [問題] hibernate 新手問題
※ 引述《cyclone350 (老子我最神)》之銘言:
: 最近利用空檔在練習 hibernate (進度緩慢)
: 因為這問題想了很久,在網路上也不知道關鍵字是甚麼,看了一些 document 也沒頭緒
: 所以就上來請教各位前輩
: 我使用的是 spring + hibernate
: 首先 hibernate 是一個 ORM framework。
: 如果我們要 select,利用他提供的 libery 就可以直接存取。
: 要查詢物件也很簡單,例如:
: String hql = "from people";
: List<people> peoplesss = this.getSessionTemplete().creatQuery(hql).list();
: 就可以得到這些Entity,相反的 insert 跟 delete 也類似
: 問題來啦,假設這是美食獵人世界的系統資料庫,如果只是用 from people,
: 則這個 List 的 size 可能好幾百億,這下不得了。
: 不過 hibernate 可以設定 pageSize 跟 pageNum 之類的東西,可以設定一次只拿到 n 筆
: 問題是 spring 提供的 hibernateTemplete 並沒有這個功能‧‧‧
: 好家在 google 大神跟我說 hibernateTemplete 有一個 callbackHibernate 方法,
: 所以此問題就解決了。
問題的解決方式很多,你用了比較曲折離奇的callbackHibernate
方法一:由hibernateTemplate中取得sessionFactory後再取得HibernatSession
方法二:直接使用HibernateDaoSupport,就有getSession
: 接下來要設定關聯資料庫
: 假設美食世界的人有三種,美食獵人,廚師,一般人,
: 我們可以增加一個 Table 名為 FOOD_TAG。
: 並且在 PEOPLE 的 table 增加一個 FK 名為 TAG,對應到 FOOD_TAG 的 PK,
: 所以我們在 PEOPLE 的 Entity 要設定一個 @ManyToOne 對應到 FOOD_TAG
: 而 FOOD_TAG 的 Entity 要設定一個 @OneToMany 對應到 PEOPLE。
: 這樣想要知道屬於美食獵人Tag的人有哪些,只需要從 FOOD_TAG 的 Entity 拿到 people
: 的 list 就好了。
老實說,你這個例子讓我百思不得其解
這明明只是一個 Enum 的物件,為何要提出來變另一個table
people 底下的一個或多個屬性就解決的問題,而且還做成雙向鏈節的關連
: 問題來了,如果我只需要拿到 Tag 的名稱,例如:
: Tag tag = getHibernateTemplete().
: createQuery("from tag t where t.name = '一般人'");
: String tagName = tag.getName();
: 那這樣還需要抓取 people List 嗎?
: 還好可以設定 fetch lazy 來解決這問題,我可以等到要 getPeopleList 時才 query
: (不過 session 不能關啦,或是要設定其他的)
: 不過又有問題了,假設我要 getPeopleList,他會一次給我全部的人,
: 假設分類在 "一般人" 的 People 有 60 億人,那我的 memory 豈不就爆掉了。
: 於是我翻書,請 google 搜尋此類問題,皆找不到 OneToMany 的限制用法
: 我想要 OneToMany 可以想一般平常的 query 可以設定 maxSize 或是 page 的概念
: 不過 google 似乎也沒找到 ...
這是設計上嚴重的錯誤阿,你把巨量資料設計在屬性之下?
同上,這個問題,你把tag作為people的一個屬性就不會有這種問題
: 另外本來想用 FK 當 where 條件當 query,然後就用一開始的方法。
: 可是在 entity 的 FK 是隱藏的 (joinColumn)。
: 想請問有沒有此類方法可以解? Annotation 要怎麼設定才會有這樣的效果
: 看過很多網站都沒講這問題
當然,因為你正使用了一個不合理的方式在查詢資料,事實上沒這個問題的
你只要稍微改動一下你取得資料方式
方式一: select people
from TAG as tag
inner join tag.peopleList as people
where tag.name = '一般人'
用PEOPLE 的List來接,並設定setFirstResult / setMaxResults
方式二:from PEOPLE as people
inner join people.tag as tag
where tag.name = '一般人'
兩者都用PEOPLE 的List來接,並設定setFirstResult / setMaxResults
這兒同時顯示,只要單向鏈結,就可以完成你的需求,雙向的設定應不需要
: 有看到一個網頁有用 @Size(max=30) 這樣,但是我的專案根本沒有 @Size
: 另外還有一個問題,我還沒自己試過,請問 Entity 的 @oneToMany 之類的會有
: 連鎖效應嗎?
: 例如 A -> B -> C -> D <- A
: A 對 B , 一對多
: B 對 C , 一對多
: C 對 D , 一對多
: A 對 D , 一對多
: 假設我都是 fetch eager 的話
: 會在 query A 的時候,把 B 一起 query。
: 因為 B -> C,hibernate 會順便 query C 嗎?
: 然後 C -> D 再 query D ?
: 假設我要拿到D
: 就直接 a.getB().get(0).getC().get(0).getD() ?
: 這樣找一個資料,數量級大一點的話,hibernate 不就死掉了嗎?
: 文有點長,看不董跟我說我再解釋一下,文筆有點不順。
這兒我要強調一下 Hibernate 是一個重視設計的 framework
任何關連的設計,都要考量著資料量,資料變動性,使用不同的策略
任何一個資料量過大的關連,都會立刻造成OOM,沒有例外
你這個問題,如果你採用Lazy的方式,慢慢的get.get.get自然是如此
而這問題我會採用 DAO的方式來解決
直接將取得 D的過程寫成一組 HQL,條件下去直接取出 DAO.getD()
Lazy功能是給懶人用的,懶人是不能寫資料量太大,或是太難的系統
針對大量的資料,設計時就要考量負載了,在取資料時,更要一針見血
高變動,大量的資料,就是用StatelessSession + dao 做為取值策略
低變動,交易性的資料,才用StatefulSession + cache為策略
Hibernate 要學好,就要先自宮掉關連資料庫的設計方式
最好,在熟練前也不要摸Spring,在Spring下的Hibernate太容易了
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 118.168.111.216
※ 編輯: abola921 來自: 118.168.111.216 (02/25 00:51)
推
02/25 12:07, , 1F
02/25 12:07, 1F
推
02/25 12:43, , 2F
02/25 12:43, 2F
→
02/25 16:23, , 3F
02/25 16:23, 3F
這點我得解釋一下原因,lazy不可否認,他真的很好用!!
一路用過來,隨著系統漸漸龐大,lazy的使用開始造成問題
首先,使用lazy的最大前提是『HibernateSession』不能close不能斷
光是這點,就讓維護的人很頭大了,接到issue的一方,要去抓斷在那
先不提是那一方的不成熟,解決問題方絕對是罵到翻過來
再來,lazy的方便,就是有如原文所提,要取得資料,寫程式你很容易
就會寫成 a.getB().getC().getD() (這很合理,也符合羅輯)
但這段程式碼的背後,隱含的就是hibernate不斷的在進行query
長久下來,其實也不用多久,一個半年的專案全使用lazy在寫的話,
你就會莫名奇秒,效能怎麼就是越來越差,把show_sql一打開來,沒幾
個動作就出現10M以上的SQL。
到這時才警覺到不可以太過依賴lazy時都為時已晚了。
我用這字眼並沒有其它意思,單單就對字面解釋,跟不建議對他依賴
※ 編輯: abola921 來自: 118.168.111.216 (02/25 22:27)
→
02/26 00:32, , 4F
02/26 00:32, 4F
→
02/26 00:35, , 5F
02/26 00:35, 5F
推
02/26 10:04, , 6F
02/26 10:04, 6F
→
02/26 10:06, , 7F
02/26 10:06, 7F
→
02/26 11:38, , 8F
02/26 11:38, 8F
→
02/26 11:40, , 9F
02/26 11:40, 9F
→
02/26 11:59, , 10F
02/26 11:59, 10F
→
02/26 12:07, , 11F
02/26 12:07, 11F
→
02/26 12:10, , 12F
02/26 12:10, 12F
→
02/26 12:12, , 13F
02/26 12:12, 13F
/* tuning點 */
public void processPeople(People p){
log.debug( p.getCLass() );
}
People people = (People) hibernateSession.load( People.class, "who" );
processPeople( people ); /* 結果為 People的proxy class */
People people = new People();
processPeople( people ); /* 結果為 People.class */
※ 編輯: abola921 來自: 140.109.20.106 (02/26 12:26)
※ 編輯: abola921 來自: 140.109.20.106 (02/26 12:27)
→
02/26 12:30, , 14F
02/26 12:30, 14F
討論串 (同標題文章)