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