程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 數據庫知識 >> SqlServer數據庫 >> 關於SqlServer >> 對象/關系數據庫映射基礎(Basic O/R Mapping)

對象/關系數據庫映射基礎(Basic O/R Mapping)

編輯:關於SqlServer
 

check (可選): 這是一個SQL表達式, 用於為自動生成的schema添加多行(multi-row)約束檢查。 (19)

rowid (可選): Hibernate可以使用數據庫支持的所謂的ROWIDs,例如: Oracle數據庫,如果你設置這個可選的rowid, Hibernate可以使用額外的字段rowid實現快速更新。ROWID是這個功能實現的重點, 它代表了一個存儲元組(tuple)的物理位置。 (20)

subselect (可選): 它將一個不可變(immutable)並且只讀的實體映射到一個數據庫的 子查詢中。它用於實現一個視圖代替一張基本表,但是最好不要這樣做。更多的介紹請看下面內容。 (21)

abstract (可選): 用於在<union-subclass>的繼承結構 (hierarchies)中標識抽象超類。 (22)

entity-name (可選, 默認為類名): 顯式指定實體名

若指明的持久化類實際上是一個接口,這也是完全可以接受的。 之後你可以用<subclass>來指定該接口的實際實現類。 你可以持久化任何static(靜態的)內部類。 你應該使用標准的類名格式來指定類名,比如:Foo$Bar。

不可變類,mutable="false"不可以被應用程序更新或者刪除。 這可以讓Hibernate做一些小小的性能優化。

可選的proxy屬性允許延遲加載類的持久化實例。 Hibernate開始會返回實現了這個命名接口的CGLIB代理。當代理的某個方法被實際調用的時候, 真實的持久化對象才會被裝載。參見下面的“用於延遲裝載的代理”。

Implicit (隱式)的多態是指,如果查詢時給出的是任何超類、該類實現的接口或者該類的 名字,都會返回這個類的實例;如果查詢中給出的是子類的名字,則會返回子類的實例。 Explicit (顯式)的多態是指,只有在查詢時給出明確的該類名字時才會返回這個類的實例; 同時只有在這個<class>的定義中作為<subclass> 或者<joined-subclass>出現的子類,才會可能返回。 在大多數情況下,默認的polymorphism="implicit"都是合適的。 顯式的多態在有兩個不同的類映射到同一個表的時候很有用。(允許一個“輕型”的類,只包含部分表字段)。

persister屬性可以讓你定制這個類使用的持久化策略。 你可以指定你自己實現 org.hibernate.persister.EntityPersister的子類,你甚至可以完全從頭開始編寫一個 org.hibernate.persister.ClassPersister接口的實現, 比如是用儲存過程調用、序列化到文件或者LDAP數據庫來實現。 參閱org.hibernate.test.CustomPersister,這是一個簡單的例子 (“持久化”到一個Hashtable)。

請注意dynamic-update和dynamic-insert的設置並不會繼承到子類, 所以在<subclass>或者<joined-subclass>元素中可能 需要再次設置。這些設置是否能夠提高效率要視情形而定。請用你的智慧決定是否使用。

使用select-before-update通常會降低性能。如果你重新連接一個脫管(detache)對象實例 到一個Session中時,它可以防止數據庫不必要的觸發update。 這就很有用了。

如果你打開了dynamic-update,你可以選擇幾種樂觀鎖定的策略:

 

version(版本檢查) 檢查version/timestamp字段


all(全部) 檢查全部字段


dirty(髒檢查)只檢察修改過的字段


none(不檢查)不使用樂觀鎖定


我們非常強烈建議你在Hibernate中使用version/timestamp字段來進行樂觀鎖定。 對性能來說,這是最好的選擇,並且這也是唯一能夠處理在session外進行操作的策略(例如: 在使用Session.merge()的時候)。

對Hibernate映射來說視圖和表是沒有區別的,這是因為它們在數據層都是透明的( 注意:一些數據庫不支持視圖屬性,特別是更新的時候)。有時你想使用視圖,但卻不能在數據庫 中創建它(例如:在遺留的schema中)。這樣的話,你可以映射一個不可變的(immutable)並且是 只讀的實體到一個給定的SQL子查詢表達式:

<class name="Summary"> <subselect> select item.name, max(bid.amount), count(*) from item join bid on bid.item_id = item.id group by item.name </subselect> <synchronize table="item"/> <synchronize table="bid"/> <id name="name"/> ...</class>

定義這個實體用到的表為同步(synchronize),確保自動刷新(auto-flush)正確執行, 並且依賴原實體的查詢不會返回過期數據。<subselect>在屬性元素 和一個嵌套映射元素中都可見。


第 9.4 節 “組件作為聯合標識符(Components as composite identifiers)”章中說明一種 更加便捷的方法,把聯合標識實現為一個獨立的類,下面描述的屬性只對這種備用方法有效:

 

name (可選):一個組件類型,持有復合標識(參見下一節)。


class (可選 - 默認為通過反射(reflection)得到的屬性類型) : 作為聯合標識的組件類名(參見下一節)。


unsaved-value (可選 - 默認為 undefined): 如果設置為any,就表示瞬時(transient)實例應該被重新初始化,或者如果 設置為none,則表示該實例是脫管對象。最好在所有的情況下都保持默認的值。

 

第 11.11 節 “傳播性持久化(transitive persistence)”.

一個典型的簡單many-to-one定義例子:

<many-to-one name="product" class="Product" column="PRODUCT_ID"/>

property-ref屬性只應該用來對付老舊的數據庫系統, 可能有外鍵指向對方關聯表的是個非主鍵字段(但是應該是一個惟一關鍵字)的情況下。 這是一種十分丑陋的關系模型。比如說,假設Product類有一個惟一的序列號, 它並不是主鍵。(unique屬性控制Hibernate通過SchemaExport工具生成DDL的過程。)

<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>

那麼關於OrderItem 的映射可能是:

<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>

當然,我們決不鼓勵這種用法。

如果被引用的唯一主鍵由關聯實體的多個屬性組成,你應該在名稱為<properties>的元素 裡面映射所有關聯的屬性。


第 9.5 節 “動態組件 (Dynamic components)”.


第 10 章 繼承映射(Inheritance Mappings)章節.


第 10 章 繼承映射(Inheritance Mappings)。


第 10 章 繼承映射(Inheritance Mappings)。


6.1.17. 連接(join)


使用 <join> 元素,可以將一個類的屬性映射到多張表中。

<join table="tablename" (1) schema="owner" (2) catalog="catalog" (3) fetch="join|select" (4) inverse="true|false" (5) optional="true|false"> (6) <key ... /> <property ... /> ...</join>

(1)
table: 被連接表的名稱。

(2)
schema (可選):覆蓋由根<hibernate-mapping>元素指定的模式名稱。

(3)
catalog (可選): 覆蓋由根 <hibernate-mapping>元素指定的目錄名稱。

(4)
fetch (可選 - 默認是 join): 如果設置為默認值join, Hibernate 將使用一個內連接來得到這個類或其超類定義的<join>,而使用一個外連接來得到其子類定義的<join>。如果設置為select,則 Hibernate 將為子類定義的 <join>使用順序選擇。這僅在一行數據表示一個子類的對象的時候才會發生。對這個類和其超類定義的<join>,依然會使用內連接得到。

(5)
inverse (可選 - 默認是 false): 如果打開,Hibernate 不會插入或者更新此連接定義的屬性。

(6)
optional (可選 - 默認是 false): 如果打開,Hibernate 只會在此連接定義的屬性非空時插入一行數據,並且總是使用一個外連接來得到這些屬性。
 


例如,一個人(person)的地址(address)信息可以被映射到單獨的表中(並保留所有屬性的值類型語義):

<class name="Person" table="PERSON"> <id name="id" column="PERSON_ID">...</id> <join table="ADDRESS"> <key column="ADDRESS_ID"/> <property name="address"/> <property name="zip"/> <property name="country"/> </join> ...

此特性常常對遺留數據模型有用,我們推薦表個數比類個數少,以及細粒度的領域模型。然而,在單獨的繼承樹上切換繼承映射策略是有用的,後面會解釋這點。


6.1.18. 鍵(key)


我們目前已經見到過<key>元素多次了。 這個元素在父映射元素定義了對新表的連接,並且在被連接表中定義了一個外鍵引用原表的主鍵的情況下經常使用。

<key column="columnname" (1) on-delete="noaction|cascade" (2) property-ref="propertyName" (3) not-null="true|false" (4) update="true|false" (5) unique="true|false" (6)/>

(1)
column (可選): 外鍵字段的名稱。也可以通過嵌套的 <column>指定。

(2)
on-delete (可選, 默認是 noaction): 表明外鍵關聯是否打開數據庫級別的級聯刪除。

(3)
property-ref (可選): 表明外鍵引用的字段不是原表的主鍵(提供給遺留數據)。

(4)
not-null (可選): 表明外鍵的字段不可為空(這意味著無論何時外鍵都是主鍵的一部分)。

(5)
update (可選): 表明外鍵決不應該被更新(這意味著無論何時外鍵都是主鍵的一部分)。

(6)
unique (可選): 表明外鍵應有唯一性約束 (這意味著無論何時外鍵都是主鍵的一部分)。
 


對那些看重刪除性能的系統,我們推薦所有的鍵都應該定義為on-delete="cascade",這樣 Hibernate 將使用數據庫級的ON CASCADE DELETE約束,而不是多個DELETE語句。 注意,這個特性會繞過 Hibernate 通常對版本數據(versioned data)采用的樂觀鎖策略。

not-null 和 update 屬性在映射單向一對多關聯的時候有用。如果你映射一個單向一對多關聯到非空的(non-nullable)外鍵,你必須 用<key not-null="true">定義此鍵字段。


6.1.19. 字段和規則元素(column and formula elements)


任何接受column屬性的映射元素都可以選擇接受<column> 子元素。同樣的,formula也可以替換<formula>屬性。

<column name="column_name" length="N" precision="N" scale="N" not-null="true|false" unique="true|false" unique-key="multicolumn_unique_key_name" index="index_name" sql-type="sql_type_name" check="SQL expression"/>

<formula>SQL expression</formula>

column 和 formula 屬性甚至可以在同一個屬性或關聯映射中被合並來表達,例如,一些奇異的連接條件。

<many-to-one name="homeAddress" class="Address" insert="false" update="false"> <column name="person_id" not-null="true" length="10"/> <formula>'MAILING'</formula></many-to-one>


6.1.20. 引用(import)


假設你的應用程序有兩個同樣名字的持久化類,但是你不想在Hibernate查詢中使用他們的全限定名。除了依賴auto-import="true"以外,類也可以被顯式地“import(引用)”。你甚至可以引用沒有明確被映射的類和接口。

<import class="java.lang.Object" rename="Universe"/>

<import class="ClassName" (1) rename="ShortName" (2)/>

(1)
class: 任何Java類的全限定名。

(2)
rename (可選 - 默認為類的全限定名): 在查詢語句中可以使用的名字。
 

 

6.1.21. any


這是屬性映射的又一種類型。<any> 映射元素定義了一種從多個表到類的多態關聯。這種類型的映射常常需要多於一個字段。第一個字段持有被關聯實體的類型,其他的字段持有標識符。對這種類型的關聯來說,不可能指定一個外鍵約束,所以這當然不是映射(多態)關聯的通常的方式。你只應該在非常特殊的情況下使用它(比如,審計log,用戶會話數據等等)。

meta-type 屬性使得應用程序能指定一個將數據庫字段的值映射到持久化類的自定義類型。這個持久化類包含有用id-type指定的標識符屬性。 你必須指定從meta-type的值到類名的映射。

<any name="being" id-type="long" meta-type="string"> <meta-value value="TBL_ANIMAL" class="Animal"/> <meta-value value="TBL_HUMAN" class="Human"/> <meta-value value="TBL_ALIEN" class="Alien"/> <column name="table_name"/> <column name="id"/></any>

<any name="propertyName" (1) id-type="idtypename" (2) meta-type="metatypename" (3) cascade="cascade_style" (4) access="field|property|ClassName" (5) optimistic-lock="true|false" (6)> <meta-value ... /> <meta-value ... /> ..... <column .... /> <column .... /> .....</any>

(1)
name: 屬性名

(2)
id-type: 標識符類型

(3)
meta-type (可選 -默認是 string): 允許辨別標志(discriminator)映射的任何類型

(4)
cascade (可選 -默認是none): 級聯的類型

(5)
access (可選 -默認是 property): Hibernate 用來訪問屬性值的策略。

(6)
optimistic-lock (可選 -默認是 true): 表明更新此組件是否需要獲取樂觀鎖。換句話說,當這個屬性變髒時,是否增加版本號(Version)
 

 


6.2. Hibernate 的類型

 

6.2.1. 實體(Entities)和值(values)


為了理解很多與持久化服務相關的Java語言級對象的行為,我們需要把它們分為兩類:

實體entity 獨立於任何持有實體引用的對象。與通常的Java模型相比,不再被引用的對象會被當作垃圾收集掉。實體必須被顯式的保存和刪除(除非保存和刪除是從父實體向子實體引發的級聯)。這和ODMG模型中關於對象通過可觸及保持持久性有一些不同——比較起來更加接近應用程序對象通常在一個大系統中的使用方法。實體支持循環引用和交叉引用,它們也可以加上版本信息。

一個實體的持久狀態包含指向其他實體和值類型實例的引用。值可以是原始類型,集合(不是集合中的對象),組件或者特定的不可變對象。與實體不同,值(特別是集合和組件)是通過可觸及性來進行持久化和刪除的。因為值對象(和原始類型數據)是隨著包含他們的實體而被持久化和刪除的,他們不能被獨立的加上版本信息。值沒有獨立的標識,所以他們不能被兩個實體或者集合共享。

直到現在,我們都一直使用術語“持久類”(persistent class)來代表實體。我們仍然會這麼做。 然而嚴格說來,不是所有的用戶自定義的,帶有持久化狀態的類都是實體。組件就是用戶自定義類,卻是值語義的。java.lang.String類型的java屬性也是值語義的。給了這個定義以後,我們可以說所有JDK提供的類型(類)都是值類型的語義,而用於自定義類型可能被映射為實體類型或值類型語義。采用哪種類型的語義取決於開發人員。在領域模型中,尋找實體類的一個好線索是共享引用指向這個類的單一實例,而組合或聚合通常被轉化為值類型。

我們會在本文檔中重復碰到這兩個概念。

挑戰在於將java類型系統(和開發者定義的實體和值類型)映射到 SQL/數據庫類型系統。Hibernate提供了連接兩個系統之間的橋梁:對於實體類型,我們使用<class>, <subclass> 等等。對於值類型,我們使用 <property>, <component> 及其他,通常跟隨著type屬性。這個屬性的值是Hibernate 的映射類型的名字。Hibernate提供了許多現成的映射(標准的JDK值類型)。你也可以編寫自己的映射類型並實現自定義的變換策略,隨後我們會看到這點。

所有的Hibernate內建類型,除了collections以外,都支持空(null)語義。


6.2.2. 基本值類型


The built-in basic mapping types may be roughly categorized into 內建的 基本映射類型可以大致分為


integer, long, short, float, double, character, byte, boolean, yes_no, true_false

這些類型都對應Java的原始類型或者其封裝類,來符合(特定廠商的)SQL 字段類型。boolean, yes_no 和 true_false都是Java 中boolean 或者java.lang.Boolean的另外說法。

string

從java.lang.String 到 VARCHAR (或者 Oracle的 VARCHAR2)的映射。

date, time, timestamp

從java.util.Date和其子類到SQL類型DATE, TIME 和TIMESTAMP (或等價類型)的映射。

calendar, calendar_date

從java.util.Calendar 到SQL 類型TIMESTAMP和 DATE(或等價類型)的映射。

big_decimal, big_integer

從java.math.BigDecimal和java.math.BigInteger到NUMERIC (或者 Oracle 的NUMBER類型)的映射。

locale, timezone, currency

從java.util.Locale, java.util.TimeZone 和java.util.Currency 到VARCHAR (或者 Oracle 的VARCHAR2類型)的映射. Locale和 Currency 的實例被映射為它們的ISO代碼。TimeZone的實例被影射為它的ID。

class

從java.lang.Class 到 VARCHAR (或者 Oracle 的VARCHAR2類型)的映射。Class被映射為它的全限定名。

binary

把字節數組(byte arrays)映射為對應的 SQL二進制類型。

text

把長Java字符串映射為SQL的CLOB或者TEXT類型。

serializable

把可序列化的Java類型映射到對應的SQL二進制類型。你也可以為一個並非默認為基本類型的可序列化Java類或者接口指定Hibernate類型serializable。

clob, blob

JDBC 類 java.sql.Clob 和 java.sql.Blob的映射。某些程序可能不適合使用這個類型,因為blob和clob對象可能在一個事務之外是無法重用的。(而且, 驅動程序對這種類型的支持充滿著補丁和前後矛盾。)


 

實體及其集合的唯一標識可以是除了binary、 blob 和 clob之外的任何基礎類型。(聯合標識也是允許的,後面會說到。)

在org.hibernate.Hibernate中,定義了基礎類型對應的Type常量。比如,Hibernate.STRING代表string 類型。


6.2.3. 自定義值類型


開發者創建屬於他們自己的值類型也是很容易的。比如說,對象/關系數據庫映射基礎 - 513394217 - ㄨiao愛你可能希望持久化java.lang.BigInteger類型的屬性,持久化成為VARCHAR字段。Hibernate沒有內置這樣一種類型。自定義類型能夠映射一個屬性(或集合元素)到不止一個數據庫表字段。比如說,你可能有這樣的Java屬性:getName()/setName(),這是java.lang.String類型的,對應的持久化到三個字段:FIRST_NAME, INITIAL, SURNAME。

要實現一個自定義類型,可以實現org.hibernate.UserType或org.hibernate.CompositeUserType中的任一個,並且使用類型的Java全限定類名來定義屬性。請查看org.hibernate.test.DoubleStringType這個例子,看看它是怎麼做的。

<property name="twoStrings" type="org.hibernate.test.DoubleStringType"> <column name="first_string"/> <column name="second_string"/></property>

注意使用<column>標簽來把一個屬性映射到多個字段的做法。

CompositeUserType, EnhancedUserType, UserCollectionType, 和 UserVersionType 接口為更特殊的使用方式提供支持。

你甚至可以在一個映射文件中提供參數給一個UserType。 為了這樣做,你的UserType必須實現org.hibernate.usertype.ParameterizedType接口。為了給自定義類型提供參數,你可以在映射文件中使用<type>元素。

<property name="priority"> <type name="com.mycompany.usertypes.DefaultValueIntegerType"> <param name="default">0</param> </type></property>

現在,UserType 可以從傳入的Properties對象中得到default 參數的值。

如果你非常頻繁地使用某一UserType,可以為他定義一個簡稱。這可以通過使用 <typedef>元素來實現。Typedefs為一自定義類型賦予一個名稱,並且如果此類型是參數化的,還可以包含一系列默認的參數值。

<typedef class="com.mycompany.usertypes.DefaultValueIntegerType" name="default_zero"> <param name="default">0</param></typedef>

<property name="priority" type="default_zero"/>

也可以根據具體案例通過屬性映射中的類型參數覆蓋在typedef中提供的參數。

盡管 Hibernate 內建的豐富的類型和對組件的支持意味著你可能很少 需要使用自定義類型。不過,為那些在你的應用中經常出現的(非實體)類使用自定義類型也是一個好方法。例如,一個MonetaryAmount類使用CompositeUserType來映射是不錯的選擇,雖然他可以很容易地被映射成組件。這樣做的動機之一是抽象。使用自定義類型,以後假若你改變表示金額的方法時,它可以保證映射文件不需要修改。


6.3. SQL中引號包圍的標識符


你可通過在映射文檔中使用反向引號(`)把表名或者字段名包圍起來,以強制Hibernate在生成的SQL中把標識符用引號包圍起來。Hibernate會使用相應的SQLDialect(方言)來使用正確的引號風格(通常是雙引號,但是在SQL Server中是括號,MySQL中是反向引號)。

<class name="LineItem" table="`Line Item`"> <id name="id" column="`Item Id`"/><generator class="assigned"/></id> <property name="itemNumber" column="`Item #`"/> ...</class>


6.4. 其他元數據(Metadata)


XML 並不適用於所有人, 因此有其他定義Hibernate O/R 映射元數據(metadata)的方法。


6.4.1. 使用 XDoclet 標記


很多Hibernate使用者更喜歡使用[email protected]將映射信息直接嵌入到源代碼中。我們不會在本文檔中涉及這個方法,因為嚴格說來,這屬於XDoclet的一部分。然而,我們包含了如下使用XDoclet映射的Cat類的例子。

package eg;import java.util.Set;import java.util.Date;/** * @hibernate.class * table="CATS" */public class Cat { private Long id; // identifier private Date birthdate; private Cat mother; private Set kittens private Color color; private char sex; private float weight; /* * @hibernate.id * generator-class="native" * column="CAT_ID" */ public Long getId() { return id; } private void setId(Long id) { this.id=id; } /** * @hibernate.many-to-one * column="PARENT_ID" */ public Cat getMother() { return mother; } void setMother(Cat mother) { this.mother = mother; } /** * @hibernate.property * column="BIRTH_DATE" */ public Date getBirthdate() { return birthdate; } void setBirthdate(Date date) { birthdate = date; } /** * @hibernate.property * column="WEIGHT" */ public float getWeight() { return weight; } void setWeight(float weight) { this.weight = weight; } /** * @hibernate.property * column="COLOR" * not-null="true" */ public Color getColor() { return color; } void setColor(Color color) { this.color = color; } /** * @hibernate.set * inverse="true" * order-by="BIRTH_DATE" * @hibernate.collection-key * column="PARENT_ID" * @hibernate.collection-one-to-many */ public Set getKittens() { return kittens; } void setKittens(Set kittens) { this.kittens = kittens; } // addKitten not needed by Hibernate public void addKitten(Cat kitten) { kittens.add(kitten); } /** * @hibernate.property * column="SEX" * not-null="true" * update="false" */ public char getSex() { return sex; } void setSex(char sex) { this.sex=sex; }}

參考Hibernate網站更多的Xdoclet和Hibernate的例子


6.4.2. 使用 JDK 5.0 的注解(Annotation)


JDK 5.0 在語言級別引入了 XDoclet 風格的標注,並且是類型安全的,在編譯期進行檢查。這一機制比XDoclet的注解更為強大,有更好的工具和IDE支持。例如, IntelliJ IDEA,支持JDK 5.0注解的自動完成和語法高亮 。EJB規范的新修訂版(JSR-220)使用 JDK 5.0的注解作為entity beans的主要元數據(metadata)機制。Hibernate 3 實現了JSR-220 (the persistence API)的EntityManager,支持通過Hibernate Annotations包定義映射元數據。這個包作為單獨的部分下載,支持EJB3 (JSR-220)和Hibernate3的元數據。

這是一個被注解為EJB entity bean 的POJO類的例子

@Entity(access = AccessType.FIELD)public class Customer implements Serializable { @Id; Long id; String firstName; String lastName; Date birthday; @Transient Integer age; @Dependent private Address homeAddress; @OneToMany(cascade=CascadeType.ALL, targetEntity="Order") @JoinColumn(name="CUSTOMER_ID") Set orders; // Getter/setter and business methods}

注意:對 JDK 5.0 注解 (和 JSR-220)支持的工作仍然在進行中,並未完成。
 

 
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved