作者abola921 (宅魔)
看板java
标题Re: [问题] hibernate 新手问题
时间Mon Feb 25 00:48:48 2013
※ 引述《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)
1F:推 PsMonkey:看来我还是乖乖地付钱给 GAE 用 BitTable 就好 [死] 02/25 12:07
2F:推 cyclone350:明白,当初已经用DAO写HQL解决问题了!! 02/25 12:43
3F:→ phstudy:第一次听过lazy fetching是设计给懒人用的 = = 02/25 16:23
这点我得解释一下原因,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)
4F:→ qrtt1:conversation 完就能关了,快慢没有绝对,要依profiling为准 02/26 00:32
5F:→ qrtt1:过长的 method chaining 应该建议改写,或提供直接的query 02/26 00:35
6F:推 phstudy:lazy fetching在collection上可以减少不必要的join 02/26 10:04
7F:→ phstudy:在LOB上可以减少不必要的资料传输 02/26 10:06
8F:→ phstudy:hibernate 3中elementCollection, OneToMany, ManyToMany 02/26 11:38
9F:→ phstudy:预设皆为lazy fetching, 目的就是避免一次捞太多资料 02/26 11:40
10F:→ abola921:lazy是没有解答中的最佳解,而且能让程式码更易维护 02/26 11:59
11F:→ abola921:但在hibernate团队管理中,lazy的使用一直是问题 02/26 12:07
12F:→ abola921:因太容易使用,没人觉得自己的code有效能风险 02/26 12:10
13F:→ abola921:在tuning时,别人的code,很难一眼看是出proxy object 02/26 12:12
/* 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)
14F:→ abola921:以管理专案的立场,多人的专案lazy的使用必需有所控制 02/26 12:30