Hibernate3.0的Formulas編程
Hibernate2.x 提供了基本的表到表的映射,正常的關聯映射(包括一對一,一對多,多對多關系),多態映射,等等。Hibernate3.x通過formula, filter, subselect來增強映射的靈活性,把這些映射提個到另一高度。
在本文中,我們將要向你展現formula的各種特征是如何輔助模式轉換的。在Hibernate3.x之前,formula的屬性僅僅出現在一個property元素中。Hibernate3.x在保持原來用法的同時,還提供了一個formula的屬性或元素(兩者在formula的使用方面都是等價的),能夠用于任何元素中,包括discriminator, many-to-one, one-to-one, element, many-to-many, map-key, map-key-many-to-many, 和 property。這將給對象關系映射(O-R)增加非常大的靈活性,并且可以對復雜的數據模型進行更加精致的解釋。
兩種必須使用formula的主要情景是:
1.需要使用formula計算結果的情形。帶有元素discriminator, element, map-key, map-key-many-to-many, 和 property的formula歸為這一類。
2.需要使用formula來進行連接操作的情形。在元素many-to-one, one-to-one, 和 many-to-many中使用的formula歸為這一類。
第一類:通過公式計算結果
識別器
在實際的數據模式中,常常存在使用一個表來描述另一個表的情況。formula能夠有助于在O-R映射中提供靈活的多態。
在圖1例子中,有兩個表Product和ProductRelease。每一個Product的記錄都在ProductRelease有一個ProductReleaseID的引用,包括產品發布名稱,類型,發行日期等。
圖1.產品和產品發布數據模型
在表ProductRelease中有一個讓人感興趣的屬性是SubProductAllowable,它的值要么是0,要么是1。值1就意味著這個產品發布下的任意產品允許有子產品,而值0則意味著不允許這樣的子產品。例如,一些產品是從多個子產品裝配而來的,而另一些產品,他們單獨地成為獨立的單元。
圖2展現了一個解釋這種數據模式的對象模型。Nested的接口定義了getSubProducts和setSubProducts方法。類NestedProduct擴展了基類Product,也實現了接口Nested。產品數據記錄是一個Product還是一個NestedProduct,依賴于產品相應的產品發布記錄的域SubProductAllowable的值。
圖2.產品和產品發布對象領域模型
為了實現這個模型轉換,使用下面的Hibernate3.x的映射:

表1.表ProductRelease中的記錄
表2表Product中的記錄
Property
Property元素中的formula允許對象屬性包含某些衍生值,像sum, average, max,等計算的結果,
例如:
<property name="averagePrice" formula="(select avg(pc.price) from PriceCatalogue pc, SelectedItems si where si.priceRefID=pc.priceID)"/>
而且,formula也有助于從另一表中通過當前記錄的某個屬性值來獲取數據。例如:
<property name="currencyName" formula="(select cur.name from currency cur where cur.id= currencyID)"/>
這有助于從表currency中檢索currency名稱。正如你看到的那樣,這些直接映射能夠省下許多轉換的代碼。
map-key
formula允許map-key擁有任何可能的值。在下面的例子(圖3)中,我們將Role_roleID作為對象模型(圖4)的map-key。

圖3.User Role數據模式

圖4.User Role對象模型
在上面的數據模型中,User和Role通過一個叫做User_has_Role的關系表連接成多對多(many-to-many)關系。為了獲得一個User及其所有與之關聯的Roles,我們可以使用下面的映射:
另一種情景:element, map-key-many-to-many, 及其它與property一樣,element能夠被賦給任何有效formula表達式的運算值。
帶有map-key-many-to-many的formula的使用,類似于帶有map-key的formula。然而,map-key-many-to-many常常使用在三重關系中。在三重關系中,一個映射的鍵本身是被參照的對象,而不是一個被參照的屬性。
然而,有幾種formula不支持的情形。一些數據庫(例如Oracle 7)不支持嵌入SQL語句(這就是說,在SQL語句的select部分嵌入SQL語句),在這種情況下,不支持使用formula來計算結果。因此,你需要首先檢查是否支持嵌入SQL語句。
由于來自于Hibernate映射的SQL使用formula表達式作為它的select目標結果的表達式部分,所以,知道一些你所使用的數據庫SQL語句的知識,將有助于你使用formula,盡管這會減少代碼的移植性。
第二種情形:使用formula來連接
多對一
另一個在實際中常見的數據模型是屬性關系映射(proprietary relationship mapping),也就是除了基本的一對一,一對多,多對多關系而外的映射關系。formula是一個用于這種屬性關系管理的元素之一。圖5展現了一個例子,在這個例子中,一個公司可能有許多聯系人,但是他們中僅僅只有一個是缺省的聯系人。有許多聯系人的公司是典型的一對多關系。然而,為了標識缺省聯系人,表ContactPerson使用屬性defaultFlag來標識他們(1表示是,0表示不是)。

圖5.用戶角色數據模式

圖6. 用戶角色對象模型
為了解釋在對象模型中(圖6)缺省聯系人關系,我們使用下面的映射:
在Hibernate中,一對一關系主要用于有著相同主鍵的兩個表連接。我們常常使用多對一關系來表示外部鍵關聯。然而,通過使用formula,一對一關系就能通過外部鍵將表連接。上面的多對一的例子能夠使用如下的一對一的關系映射:
盡管不常常使用多對多元素的formula,但是,這種formula能夠用于從關系表到一個實體表的特別聯接。
結論
本文中的例子展現了大多數formula的使用場景。當需要計算一個formula的值的時候,formula表達式將會出現在最終的SQL語句的select部分。并且當使用一個formula來進行連接的時候,它就出現在最終SQL語句的where部分。而且,只要是目標數據庫支持的任意SQL語句,formula的表達式就能夠使用。其結果是,不需要代碼,formula就能有助于實現從數據模型到對象模型的精確的映射關系。
在本文中,我們將要向你展現formula的各種特征是如何輔助模式轉換的。在Hibernate3.x之前,formula的屬性僅僅出現在一個property元素中。Hibernate3.x在保持原來用法的同時,還提供了一個formula的屬性或元素(兩者在formula的使用方面都是等價的),能夠用于任何元素中,包括discriminator, many-to-one, one-to-one, element, many-to-many, map-key, map-key-many-to-many, 和 property。這將給對象關系映射(O-R)增加非常大的靈活性,并且可以對復雜的數據模型進行更加精致的解釋。
兩種必須使用formula的主要情景是:
1.需要使用formula計算結果的情形。帶有元素discriminator, element, map-key, map-key-many-to-many, 和 property的formula歸為這一類。
2.需要使用formula來進行連接操作的情形。在元素many-to-one, one-to-one, 和 many-to-many中使用的formula歸為這一類。
第一類:通過公式計算結果
識別器
在實際的數據模式中,常常存在使用一個表來描述另一個表的情況。formula能夠有助于在O-R映射中提供靈活的多態。
在圖1例子中,有兩個表Product和ProductRelease。每一個Product的記錄都在ProductRelease有一個ProductReleaseID的引用,包括產品發布名稱,類型,發行日期等。
圖1.產品和產品發布數據模型
在表ProductRelease中有一個讓人感興趣的屬性是SubProductAllowable,它的值要么是0,要么是1。值1就意味著這個產品發布下的任意產品允許有子產品,而值0則意味著不允許這樣的子產品。例如,一些產品是從多個子產品裝配而來的,而另一些產品,他們單獨地成為獨立的單元。
圖2展現了一個解釋這種數據模式的對象模型。Nested的接口定義了getSubProducts和setSubProducts方法。類NestedProduct擴展了基類Product,也實現了接口Nested。產品數據記錄是一個Product還是一個NestedProduct,依賴于產品相應的產品發布記錄的域SubProductAllowable的值。
圖2.產品和產品發布對象領域模型
為了實現這個模型轉換,使用下面的Hibernate3.x的映射:
<hibernate-mapping>如果formula表達式計算的結果是0,也就是說,該產品不存在子產品,那么,該對象就是類Product的實例。如果結果是1,那么對象就是類NestedProduct的實例。 在表1和2中,對于表Product中的第一個記錄(ProductID=10000001),因為它引用了一個有著SubProductAllowable=1的ProductRelease產品記錄,所以,初始化的類將是NestedProduct。而在產品Product表中的第二個記錄(ProductID=20000001),因為它引用了有著SubProductAllowable=0的表ProductRelease的記錄,所以,初始化類將會是Product。
<class name="Product"
discriminator-value="0" lazy="false">
<id name="id" type="long"/>
<discriminator
formula="(select pr.SubProductAllowable
from ProductRelease pr
where pr.productReleaseID=
productReleaseID)"
type="integer" />
<subclass name="NestedProduct"
discriminator-value="1"/>
</class>
</hibernate-mapping>

表1.表ProductRelease中的記錄
表2表Product中的記錄
Property
Property元素中的formula允許對象屬性包含某些衍生值,像sum, average, max,等計算的結果,
例如:
<property name="averagePrice" formula="(select avg(pc.price) from PriceCatalogue pc, SelectedItems si where si.priceRefID=pc.priceID)"/>
而且,formula也有助于從另一表中通過當前記錄的某個屬性值來獲取數據。例如:
<property name="currencyName" formula="(select cur.name from currency cur where cur.id= currencyID)"/>
這有助于從表currency中檢索currency名稱。正如你看到的那樣,這些直接映射能夠省下許多轉換的代碼。
map-key
formula允許map-key擁有任何可能的值。在下面的例子(圖3)中,我們將Role_roleID作為對象模型(圖4)的map-key。

圖3.User Role數據模式

圖4.User Role對象模型
在上面的數據模型中,User和Role通過一個叫做User_has_Role的關系表連接成多對多(many-to-many)關系。為了獲得一個User及其所有與之關聯的Roles,我們可以使用下面的映射:
<hibernate-mapping>Role_RoleID被用來作為many-to-many元素的連接域的值。然而,Hibernate并不允許Role_RoleID同時出現在map-key和many-to-many中。但是,使用formula,Role_RoleIDf卻也能夠用于map-key中。
<class name="User">
<id name="userID"/>
<map name="roles"
table="UserRole"/>
<key column="User_userID"/>
<map-key
formula="Role_RoleID"
type="string"/>
<many-to-many
column="Role_RoleID"
class="Role"/>
</map>
</class>
<class name="Role">
<id name="roleID"/>
</class>
</hibernate-mapping>
另一種情景:element, map-key-many-to-many, 及其它與property一樣,element能夠被賦給任何有效formula表達式的運算值。
帶有map-key-many-to-many的formula的使用,類似于帶有map-key的formula。然而,map-key-many-to-many常常使用在三重關系中。在三重關系中,一個映射的鍵本身是被參照的對象,而不是一個被參照的屬性。
然而,有幾種formula不支持的情形。一些數據庫(例如Oracle 7)不支持嵌入SQL語句(這就是說,在SQL語句的select部分嵌入SQL語句),在這種情況下,不支持使用formula來計算結果。因此,你需要首先檢查是否支持嵌入SQL語句。
由于來自于Hibernate映射的SQL使用formula表達式作為它的select目標結果的表達式部分,所以,知道一些你所使用的數據庫SQL語句的知識,將有助于你使用formula,盡管這會減少代碼的移植性。
第二種情形:使用formula來連接
多對一
另一個在實際中常見的數據模型是屬性關系映射(proprietary relationship mapping),也就是除了基本的一對一,一對多,多對多關系而外的映射關系。formula是一個用于這種屬性關系管理的元素之一。圖5展現了一個例子,在這個例子中,一個公司可能有許多聯系人,但是他們中僅僅只有一個是缺省的聯系人。有許多聯系人的公司是典型的一對多關系。然而,為了標識缺省聯系人,表ContactPerson使用屬性defaultFlag來標識他們(1表示是,0表示不是)。

圖5.用戶角色數據模式

圖6. 用戶角色對象模型
為了解釋在對象模型中(圖6)缺省聯系人關系,我們使用下面的映射:
<hibernate-mapping>上面,我們將companyID和defaultFlag分組到名為defaultContactPerson的properties元素中,它構成了表Person的唯一鍵值。在Company類中的many-to-one元素與類Person中的defaultContactPerson properties元素相連接。最后的SQL語句如下:
<class name="Company" table="Company">
<id name="id" />
<many-to-one
name="defaultContactPerson"
property-ref="defaultContactPerson">
<column name="id"/>
<formula>1</formula>
</many-to-one>
</class>
<class name="Person" >
<id name="id" />
<properties name="defaultContactPerson">
<property name="companyID" />
<property name="defaultFlag" />
</properties>
</class>
</hibernate-mapping>
select c.id, p.id from Company c, Person p where p.companyID=c.id and p.defaultFlag=1一對一
在Hibernate中,一對一關系主要用于有著相同主鍵的兩個表連接。我們常常使用多對一關系來表示外部鍵關聯。然而,通過使用formula,一對一關系就能通過外部鍵將表連接。上面的多對一的例子能夠使用如下的一對一的關系映射:
<hibernate-mapping>其他關系:多對多
<class name="Company" table="Company" >
<id name="id" />
<one-to-one name="defaultContactPerson"
property-ref="defaultContactPerson" >
<formula>id</formula>
<formula>1</formula>
</many-to-one>
</class>
<class name="Person" >
<id name="id" />
<properties name="defaultContactPerson">
<property name="companyID" />
<property name="defaultFlag" />
</properties>
</class>
</hibernate-mapping>
盡管不常常使用多對多元素的formula,但是,這種formula能夠用于從關系表到一個實體表的特別聯接。
結論
本文中的例子展現了大多數formula的使用場景。當需要計算一個formula的值的時候,formula表達式將會出現在最終的SQL語句的select部分。并且當使用一個formula來進行連接的時候,它就出現在最終SQL語句的where部分。而且,只要是目標數據庫支持的任意SQL語句,formula的表達式就能夠使用。其結果是,不需要代碼,formula就能有助于實現從數據模型到對象模型的精確的映射關系。