由于最近在把以前的一個(gè)設(shè)計(jì)移到hibernate上來(lái),所以需要用到one-to-one,因?yàn)樵谝郧暗脑O(shè)計(jì)中需要用到在一個(gè)主表中對(duì)于多個(gè)子表的主鍵關(guān)聯(lián),所以一開始就想到了one-to-one的應(yīng)用,覺得這樣解決不但不會(huì)引起以前數(shù)據(jù)設(shè)計(jì)的改變,也能夠很好的利用hibernate所帶來(lái)的OR優(yōu)勢(shì),可是當(dāng)實(shí)際使用的時(shí)候發(fā)現(xiàn),在插入數(shù)據(jù)的時(shí)候可以有選擇的在任意子表中進(jìn)行插入,所有的結(jié)果都在原來(lái)的預(yù)期之中,但是在查詢的時(shí)候,比如說(shuō)只查詢主表中的內(nèi)容
From tableMain僅僅執(zhí)行看起來(lái)十分簡(jiǎn)單的一條語(yǔ)句,你所期望的是他緊緊查詢T_MAIN這張主表,可是結(jié)果確實(shí)hibernate通過(guò)多個(gè)外連接將所有的子表一口氣的全部查詢出來(lái)
select * from t_main main outer join t_sub1 sub1 on main.id = sub1.id outer join t_sub2 sub2 on main.id = sub2.id... 如此的效率絕對(duì)讓你頭痛不已,不僅如此,如果你通過(guò)首先獲得子表t_sub1的某個(gè)主鍵ID,然后通過(guò)這個(gè)主鍵查詢出子表對(duì)象,在關(guān)聯(lián)至住表,同樣的情況又會(huì)發(fā)生,又會(huì)生成類似的SQL語(yǔ)句,這樣一來(lái)看來(lái)對(duì)于這個(gè)設(shè)計(jì)應(yīng)用one-to-one本身就是一種錯(cuò)誤,是這樣嗎?
或許有人認(rèn)為我們?cè)诿總€(gè)one-to-one中加入lazy="true"這個(gè)屬性會(huì)杜絕上述情況的發(fā)生,經(jīng)過(guò)筆者的證實(shí)即便你加入了lazy="true",也不會(huì)帶來(lái)任何的改變;又或者在hibernate.config中加入fetch depth屬性以及在每個(gè)關(guān)聯(lián)中設(shè)置outer-join="false",這些都不會(huì)引起本質(zhì)上的變化,加入outer-join="false"其實(shí)結(jié)果只是將原有的outer join語(yǔ)句改變成多條sql語(yǔ)句而已,并沒(méi)發(fā)生什么本質(zhì)變化,反而效率更低了。
該怎么辦呢?我們先仔細(xì)研究一下one-to-one的概念,one to one代表一對(duì)一,在一般的模型中很少會(huì)遇到one-to-one這種概念,因?yàn)樗謴?qiáng)調(diào)一對(duì)一的概念,就好比一個(gè)人他只有一個(gè)身體和一個(gè)頭而已,頭和身體是十分好的例子,因?yàn)橛猩眢w必定只有一個(gè)頭,而且說(shuō)到了身體必定要說(shuō)頭,就好像看了某個(gè)女孩的身材必定想知道她的長(zhǎng)相如何(-_-),所以在這時(shí)我們使用one-to-one,因?yàn)檫@種一對(duì)一的關(guān)系是很強(qiáng)的,而且從對(duì)象中取得body必定會(huì)取得他所關(guān)聯(lián)的head,這樣的情況下使用outer-join是十分方便和有效率的,因?yàn)樗褂昧薿uter join查詢從而避免了兩條到數(shù)據(jù)庫(kù)的查詢語(yǔ)句,而且在這種情況下也只需要在body_hbm.xml中設(shè)置一個(gè)one-to-one即可,所以在這種確實(shí)是一對(duì)一
而且在主表中一對(duì)一的關(guān)聯(lián)個(gè)數(shù)(即主表中one-to-one標(biāo)簽)十分少的情況下,使用one-to-one是一種很不錯(cuò)的解決辦法。
如果一個(gè)主表會(huì)對(duì)多個(gè)子表都進(jìn)行one-to-one關(guān)聯(lián)呢,就像我們一開始遇到的這種情況,比如你不僅僅只想了解那個(gè)你中意的女孩的身材和臉蛋,而且還想知道他的學(xué)歷,身世等等一切,在這種情況下,如果我們都是用多個(gè)one-to-one在主表中的話,那情況正如我們一開始看見的,是十分可怕的,該怎么做呢?不妨考慮一下使用one-to-many,什么,many?一開始聽到many這個(gè)詞的時(shí)候,我也覺得挺驚訝的這明明是多個(gè)一對(duì)一的關(guān)聯(lián)為什么要用到many呢?其實(shí)many并沒(méi)有一定要說(shuō)是大于一的,你就只在它的many中存在一個(gè)關(guān)聯(lián)它有能乃你何呢?如果用到many的話,我們就需要改動(dòng)數(shù)據(jù)表的設(shè)計(jì)了,在每個(gè)有關(guān)連的子表中加入一列main_id代表主表中該記錄的主鍵子段值,只需要這樣子改動(dòng)就可以了,這樣所帶來(lái)的效果絕對(duì)是值得你這樣做的,然后我們就按照以往的one-to-many來(lái)設(shè)計(jì)就好了
在body.hbm.xml加入(一到head的關(guān)聯(lián)舉例,其他的關(guān)聯(lián)按照這樣的格式添加即可)
<set name="head" inverse="true" lazy="true" cascade="all-delete-orphan">
<key column="ID0000"/>
<one-to-many class="com.xx.Head"/>
</set>
在head.hbm.xml加入
<many-to-one name="body" column="ID0000" class="com.xx.Body" not-null="true"/>
行了,經(jīng)過(guò)上面的改動(dòng)我們就擺脫了查詢時(shí)多個(gè)outer-join的困擾,只在需要的時(shí)候才對(duì)子表進(jìn)行查詢,因?yàn)樵O(shè)置了lazy="true",所以一切的一切都在我們的預(yù)料之中,我們?nèi)绻M@得body的話hibernate絕對(duì)不會(huì)把它的head 也查詢出來(lái),節(jié)省了查詢是所需要的負(fù)擔(dān),除非到了我們十分需要head的情況才會(huì)進(jìn)行關(guān)聯(lián)查詢,獲得所需要的head結(jié)果。
所以由此看來(lái)
在one-to-one這種一對(duì)一的關(guān)系不是很強(qiáng)的情況下,或者是在一張表中存在多個(gè)one-to-one的情況下,使用one-to-many來(lái)代替one-to-one不失為一種不錯(cuò)的做法,當(dāng)然更重要的良好的數(shù)據(jù)庫(kù)設(shè)計(jì),hibernate畢竟只是末,
千萬(wàn)不要本末倒置。
posted @
2005-03-30 13:26 一個(gè)人的日子,我獨(dú)來(lái)獨(dú)往 閱讀(217) |
評(píng)論 (2) |
編輯 收藏
one-to-one在hibernate中可以用來(lái)作為兩張表之間的主鍵關(guān)聯(lián),這也是hibernate中主鍵關(guān)聯(lián)的一種用法,這樣在一張表中的ID,在生成另外一張表的同時(shí)回自動(dòng)插入到相應(yīng)的ID字段中去,相應(yīng)的XML文件設(shè)置比較簡(jiǎn)單,舉例如下:
<!-- 建立一對(duì)一的到Address的映射,這個(gè)是寫在User的XML配置文件中的 -->
<!-- 相應(yīng)的User bean(PO)中也要添加屬性 com.xx.Address address-->
<one-to-one name="address" cascade="all" class="com.xx.Address"/>
<!-- cascade的屬性設(shè)置不再重復(fù)了,可以查看hibernate文檔 -->
<!-- 建立一對(duì)一的到User的映射,這個(gè)是寫在Address的XML配置文件中的 -->
<!-- 相應(yīng)的Address bean(PO)中也要添加屬性 com.xx.User user--> -->
<one-to-one name="user" class="com.xx.User" constrained="true"/>
為了在Address中使用User中的主鍵ID值,我們需要設(shè)置Address中的主鍵生成規(guī)則,如下所示,采用foreign關(guān)鍵字
<id column="ID" name="id" type="long" unsaved-value="0">
<generator class="foreign">
<param name="property">user</param>
</generator>
</id>
這里需要注意的是property的屬性值必須與上面到User的映射所填寫的name屬性值一致,這樣就完成了one-to-one的映射關(guān)系。
上面的過(guò)程都很簡(jiǎn)單,下面我來(lái)說(shuō)說(shuō)這里需要注意的地方:
1. 在設(shè)置屬性ID的時(shí)候必須注意字段的長(zhǎng)度,如筆者這樣使用oracle的sequence來(lái)生成ID,其長(zhǎng)度有14位之長(zhǎng),則應(yīng)選擇hibernate類型long,對(duì)應(yīng)的實(shí)體中應(yīng)選擇Long,這樣不會(huì)出現(xiàn)溢出的情況。
2. 在測(cè)試的時(shí)候必須要注意這兩張表之間因?yàn)橐呀?jīng)存在了一對(duì)一的關(guān)系,所以我們不能只寫
user.setAddress(address);
而忽略了
address.setUser(user);
這樣在做插入的時(shí)候會(huì)報(bào)出attempted to assign id from null one-to-one property: address的錯(cuò)誤,這一點(diǎn)初學(xué)者會(huì)經(jīng)常犯,筆者也是其中之一。
3. 如果不寫cascade="all"或者寫成cascade="none"的話,即使你寫了
user.setAddress(address);
address.setUser(user);
也不會(huì)發(fā)生任何事情,只有user會(huì)被存儲(chǔ)。
以上是一些筆者經(jīng)歷的小經(jīng)驗(yàn),如果有不對(duì)的地方歡迎指正。
posted @
2005-03-23 17:27 一個(gè)人的日子,我獨(dú)來(lái)獨(dú)往 閱讀(437) |
評(píng)論 (5) |
編輯 收藏
在很多情況下,我們使用Hibernate在已經(jīng)建立好數(shù)據(jù)庫(kù)的基礎(chǔ)上。在oracle中,如果已經(jīng)建立好的數(shù)據(jù)庫(kù)中使用了sequence,則可以按照下面的步驟把它引入到Hibernate中:
1、在oracle 首先創(chuàng)建sequence
create sequence seq_id
minvalue 1
start with 1
increment by 1
cache 20;
2.在你的hbm.xml中的配置
<id column="ID0000" name="id" type="integer">
<generator class="sequence">
<param name="sequence">seq_id</param>
</generator>
</id>
這樣再插入數(shù)據(jù)的時(shí)候,Hibernate回自動(dòng)生成如下語(yǔ)句:
hibernate: select seq_id.nextval from dual
hibernate: insert into YXJK.T_YXJK_WHRYTXL (XM0000, ZW0000, LXDH00, SJHM00, DZYJ00,
IP0000, ID0000) values (?, ?, ?, ?, ?, ?, ?)
自動(dòng)生成下一個(gè)序列值,然后將對(duì)象插入表中。
在使用的時(shí)候需要注意,Hibernate對(duì)于sequence的主鍵的要求是一定要是shor,long,或者integer
posted @
2005-03-23 10:30 一個(gè)人的日子,我獨(dú)來(lái)獨(dú)往 閱讀(313) |
評(píng)論 (0) |
編輯 收藏
在做具有MVC結(jié)構(gòu)的B/S程序時(shí),怎樣將這三層隔離開是十分關(guān)鍵的,一般用DAO封裝Hibernate來(lái)獲得對(duì)數(shù)據(jù)庫(kù)的具體操作,在這里我們可以為每一個(gè)需要建立O-R MAPPING的對(duì)象(通過(guò)Hibernate實(shí)現(xiàn)OR映射)實(shí)現(xiàn)一個(gè)DAO,然后通過(guò)這個(gè)DAO來(lái)獲得具體的數(shù)據(jù)庫(kù)操作,用DAO的好處是我們可以把對(duì)一個(gè)對(duì)象的操作集中在同一個(gè)DAO中,便于管理,另外向上層只提供了接口屏蔽了底層對(duì)數(shù)據(jù)庫(kù)的操作,通過(guò)hibernate,我們向上層直接提供建立了O-R MAPPING的OBJECT;同時(shí)在領(lǐng)域模型這一層,也就是M這一層,我們將一些業(yè)務(wù)邏輯(business logic)封裝進(jìn)來(lái),這里所指的M這一層通常也就是我們?cè)贖ibernate中所用到的plain objectS,就是用來(lái)建立O-R MAPPING所需要用到的與表對(duì)應(yīng)的OBJECTs,一般的領(lǐng)域模型都是由這些plain objectS構(gòu)成;這樣我們?cè)诳刂茖右簿褪荂這一層只需要初始化DAO打開到持久層的通路,然后調(diào)用一些簡(jiǎn)單的方法執(zhí)行業(yè)務(wù)邏輯,請(qǐng)注意這時(shí)候我們的業(yè)務(wù)邏輯已經(jīng)被封裝在領(lǐng)域模型這一層中了,這樣我們每一層都是相互獨(dú)立的,控制層C和展現(xiàn)層V都不和持久層所提供的接口有關(guān)系
posted @
2005-03-17 23:14 一個(gè)人的日子,我獨(dú)來(lái)獨(dú)往 閱讀(123) |
評(píng)論 (0) |
編輯 收藏
今天的主要收獲是發(fā)現(xiàn)通過(guò)在servlet的Filter中實(shí)現(xiàn)session.begin(),session.close(),session.beginTransaction()以及transaction.commit()是一個(gè)不錯(cuò)的選擇
如上圖這樣,在從服務(wù)器端返回到客戶端的時(shí)候,也就是在轉(zhuǎn)向到最終頁(yè)面的時(shí)候,由Filter實(shí)現(xiàn)關(guān)閉session和transaction,是一個(gè)很好的實(shí)現(xiàn)方法