610 lines
18 KiB
XML
610 lines
18 KiB
XML
<chapter id="example-mappings">
|
||
<title>示例:复杂映射实例</title>
|
||
<para>
|
||
本章展示了一些较为复杂的关系映射。
|
||
</para>
|
||
|
||
<sect1 id="example-mappings-emp">
|
||
<title>Employer(雇主)/Employee(雇员)</title>
|
||
|
||
<para>
|
||
下面关于<literal>Employer</literal> 和 <literal>Employee</literal>的关系模型使用了一个真实的实体类
|
||
(<literal>Employment</literal>)来表述,这是因为对于相同的雇员和雇主可能会有多个雇佣时间段。
|
||
对于金额和雇员姓名,用Components建模。
|
||
|
||
</para>
|
||
|
||
<mediaobject>
|
||
<imageobject role="fo">
|
||
<imagedata fileref="images/EmployerEmployee.gif" format="GIF" align="center"/>
|
||
</imageobject>
|
||
<imageobject role="html">
|
||
<imagedata fileref="../shared/images/EmployerEmployee.gif" format="GIF" align="center"/>
|
||
</imageobject>
|
||
</mediaobject>
|
||
|
||
<para>
|
||
映射文件可能是这样:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[<hibernate-mapping>
|
||
|
||
<class name="Employer" table="employers">
|
||
<id name="id">
|
||
<generator class="sequence">
|
||
<param name="sequence">employer_id_seq</param>
|
||
</generator>
|
||
</id>
|
||
<property name="name"/>
|
||
</class>
|
||
|
||
<class name="Employment" table="employment_periods">
|
||
|
||
<id name="id">
|
||
<generator class="sequence">
|
||
<param name="sequence">employment_id_seq</param>
|
||
</generator>
|
||
</id>
|
||
<property name="startDate" column="start_date"/>
|
||
<property name="endDate" column="end_date"/>
|
||
|
||
<component name="hourlyRate" class="MonetaryAmount">
|
||
<property name="amount">
|
||
<column name="hourly_rate" sql-type="NUMERIC(12, 2)"/>
|
||
</property>
|
||
<property name="currency" length="12"/>
|
||
</component>
|
||
|
||
<many-to-one name="employer" column="employer_id" not-null="true"/>
|
||
<many-to-one name="employee" column="employee_id" not-null="true"/>
|
||
|
||
</class>
|
||
|
||
<class name="Employee" table="employees">
|
||
<id name="id">
|
||
<generator class="sequence">
|
||
<param name="sequence">employee_id_seq</param>
|
||
</generator>
|
||
</id>
|
||
<property name="taxfileNumber"/>
|
||
<component name="name" class="Name">
|
||
<property name="firstName"/>
|
||
<property name="initial"/>
|
||
<property name="lastName"/>
|
||
</component>
|
||
</class>
|
||
|
||
</hibernate-mapping>]]></programlisting>
|
||
|
||
<para>
|
||
用<literal>SchemaExport</literal>生成表结构。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[create table employers (
|
||
id BIGINT not null,
|
||
name VARCHAR(255),
|
||
primary key (id)
|
||
)
|
||
|
||
create table employment_periods (
|
||
id BIGINT not null,
|
||
hourly_rate NUMERIC(12, 2),
|
||
currency VARCHAR(12),
|
||
employee_id BIGINT not null,
|
||
employer_id BIGINT not null,
|
||
end_date TIMESTAMP,
|
||
start_date TIMESTAMP,
|
||
primary key (id)
|
||
)
|
||
|
||
create table employees (
|
||
id BIGINT not null,
|
||
firstName VARCHAR(255),
|
||
initial CHAR(1),
|
||
lastName VARCHAR(255),
|
||
taxfileNumber VARCHAR(255),
|
||
primary key (id)
|
||
)
|
||
|
||
alter table employment_periods
|
||
add constraint employment_periodsFK0 foreign key (employer_id) references employers
|
||
alter table employment_periods
|
||
add constraint employment_periodsFK1 foreign key (employee_id) references employees
|
||
create sequence employee_id_seq
|
||
create sequence employment_id_seq
|
||
create sequence employer_id_seq]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="example-mappings-authorwork">
|
||
<title>Author(作家)/Work(作品)</title>
|
||
|
||
<para>
|
||
|
||
考虑下面的<literal>Work</literal>,<literal>Author</literal> 和 <literal>Person</literal>模型的关系。
|
||
我们用多对多关系来描述<literal>Work</literal> 和 <literal>Author</literal>,
|
||
用一对一关系来描述<literal>Author</literal> 和 <literal>Person</literal>,
|
||
另一种可能性是<literal>Author</literal>继承<literal>Person</literal>。
|
||
</para>
|
||
|
||
<mediaobject>
|
||
<imageobject role="fo">
|
||
<imagedata fileref="images/AuthorWork.gif" format="GIF" align="center"/>
|
||
</imageobject>
|
||
<imageobject role="html">
|
||
<imagedata fileref="../shared/images/AuthorWork.gif" format="GIF" align="center"/>
|
||
</imageobject>
|
||
</mediaobject>
|
||
|
||
<para>
|
||
下面的映射文件正确的描述了这些关系:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[<hibernate-mapping>
|
||
|
||
<class name="Work" table="works" discriminator-value="W">
|
||
|
||
<id name="id" column="id">
|
||
<generator class="native"/>
|
||
</id>
|
||
<discriminator column="type" type="character"/>
|
||
|
||
<property name="title"/>
|
||
<set name="authors" table="author_work">
|
||
<key column name="work_id"/>
|
||
<many-to-many class="Author" column name="author_id"/>
|
||
</set>
|
||
|
||
<subclass name="Book" discriminator-value="B">
|
||
<property name="text"/>
|
||
</subclass>
|
||
|
||
<subclass name="Song" discriminator-value="S">
|
||
<property name="tempo"/>
|
||
<property name="genre"/>
|
||
</subclass>
|
||
|
||
</class>
|
||
|
||
<class name="Author" table="authors">
|
||
|
||
<id name="id" column="id">
|
||
<!-- The Author must have the same identifier as the Person -->
|
||
<generator class="assigned"/>
|
||
</id>
|
||
|
||
<property name="alias"/>
|
||
<one-to-one name="person" constrained="true"/>
|
||
|
||
<set name="works" table="author_work" inverse="true">
|
||
<key column="author_id"/>
|
||
<many-to-many class="Work" column="work_id"/>
|
||
</set>
|
||
|
||
</class>
|
||
|
||
<class name="Person" table="persons">
|
||
<id name="id" column="id">
|
||
<generator class="native"/>
|
||
</id>
|
||
<property name="name"/>
|
||
</class>
|
||
|
||
</hibernate-mapping>]]></programlisting>
|
||
|
||
<para>
|
||
|
||
映射中有4个表。<literal>works</literal>, <literal>authors</literal> 和 <literal>persons</literal>
|
||
分别保存着work,author和person的数据。<literal>author_work</literal>是authors和works的关联表。
|
||
表结构是由<literal>SchemaExport</literal>生成的。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[create table works (
|
||
id BIGINT not null generated by default as identity,
|
||
tempo FLOAT,
|
||
genre VARCHAR(255),
|
||
text INTEGER,
|
||
title VARCHAR(255),
|
||
type CHAR(1) not null,
|
||
primary key (id)
|
||
)
|
||
|
||
create table author_work (
|
||
author_id BIGINT not null,
|
||
work_id BIGINT not null,
|
||
primary key (work_id, author_id)
|
||
)
|
||
|
||
create table authors (
|
||
id BIGINT not null generated by default as identity,
|
||
alias VARCHAR(255),
|
||
primary key (id)
|
||
)
|
||
|
||
create table persons (
|
||
id BIGINT not null generated by default as identity,
|
||
name VARCHAR(255),
|
||
primary key (id)
|
||
)
|
||
|
||
alter table authors
|
||
add constraint authorsFK0 foreign key (id) references persons
|
||
alter table author_work
|
||
add constraint author_workFK0 foreign key (author_id) references authors
|
||
alter table author_work
|
||
add constraint author_workFK1 foreign key (work_id) references works]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="example-mappings-customerorderproduct">
|
||
<title>Customer(客户)/Order(订单)/Product(产品)</title>
|
||
|
||
<para>
|
||
|
||
现在来考虑<literal>Customer</literal>,<literal>Order</literal> , <literal>LineItem</literal>
|
||
和 <literal>Product</literal>关系的模型。<literal>Customer</literal> 和 <literal>Order</literal>之间
|
||
是一对多的关系,但是我们怎么来描述<literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>呢?
|
||
我可以把<literal>LineItem</literal>作为描述<literal>Order</literal> 和 <literal>Product</literal>
|
||
多对多关系的关联类,在Hibernate,这叫做组合元素。
|
||
</para>
|
||
|
||
<mediaobject>
|
||
<imageobject role="fo">
|
||
<imagedata fileref="images/CustomerOrderProduct.gif" format="GIF" align="center"/>
|
||
</imageobject>
|
||
<imageobject role="html">
|
||
<imagedata fileref="../shared/images/CustomerOrderProduct.gif" format="GIF" align="center"/>
|
||
</imageobject>
|
||
</mediaobject>
|
||
|
||
<para>
|
||
映射文件如下:
|
||
</para>
|
||
|
||
<programlisting><![CDATA[<hibernate-mapping>
|
||
|
||
<class name="Customer" table="customers">
|
||
<id name="id">
|
||
<generator class="native"/>
|
||
</id>
|
||
<property name="name"/>
|
||
<set name="orders" inverse="true">
|
||
<key column="customer_id"/>
|
||
<one-to-many class="Order"/>
|
||
</set>
|
||
</class>
|
||
|
||
<class name="Order" table="orders">
|
||
<id name="id">
|
||
<generator class="native"/>
|
||
</id>
|
||
<property name="date"/>
|
||
<many-to-one name="customer" column="customer_id"/>
|
||
<list name="lineItems" table="line_items">
|
||
<key column="order_id"/>
|
||
<list-index column="line_number"/>
|
||
<composite-element class="LineItem">
|
||
<property name="quantity"/>
|
||
<many-to-one name="product" column="product_id"/>
|
||
</composite-element>
|
||
</list>
|
||
</class>
|
||
|
||
<class name="Product" table="products">
|
||
<id name="id">
|
||
<generator class="native"/>
|
||
</id>
|
||
<property name="serialNumber"/>
|
||
</class>
|
||
|
||
</hibernate-mapping>]]></programlisting>
|
||
|
||
<para>
|
||
<literal>customers</literal>, <literal>orders</literal>, <literal>line_items</literal> 和
|
||
<literal>products</literal> 分别保存着customer, order, order line item 和 product的数据。
|
||
<literal>line_items</literal>也作为连接orders 和 products的关联表。
|
||
</para>
|
||
|
||
<programlisting><![CDATA[create table customers (
|
||
id BIGINT not null generated by default as identity,
|
||
name VARCHAR(255),
|
||
primary key (id)
|
||
)
|
||
|
||
create table orders (
|
||
id BIGINT not null generated by default as identity,
|
||
customer_id BIGINT,
|
||
date TIMESTAMP,
|
||
primary key (id)
|
||
)
|
||
|
||
create table line_items (
|
||
line_number INTEGER not null,
|
||
order_id BIGINT not null,
|
||
product_id BIGINT,
|
||
quantity INTEGER,
|
||
primary key (order_id, line_number)
|
||
)
|
||
|
||
create table products (
|
||
id BIGINT not null generated by default as identity,
|
||
serialNumber VARCHAR(255),
|
||
primary key (id)
|
||
)
|
||
|
||
alter table orders
|
||
add constraint ordersFK0 foreign key (customer_id) references customers
|
||
alter table line_items
|
||
add constraint line_itemsFK0 foreign key (product_id) references products
|
||
alter table line_items
|
||
add constraint line_itemsFK1 foreign key (order_id) references orders]]></programlisting>
|
||
|
||
</sect1>
|
||
|
||
<sect1 id="misc">
|
||
<title>杂例</title>
|
||
|
||
<para>
|
||
这些例子全部来自于Hibernate的test suite,同时你也可以找到其他有用的例子。
|
||
可以参考Hibernate的<literal>src</literal>目录。
|
||
</para>
|
||
|
||
<para>TODO: put words around this stuff</para>
|
||
|
||
<sect2 id="example-mappings-typed-onetone">
|
||
<title>"Typed" one-to-one association</title>
|
||
<programlisting><![CDATA[<class name="Person">
|
||
<id name="name"/>
|
||
<one-to-one name="address"
|
||
cascade="all">
|
||
<formula>name</formula>
|
||
<formula>'HOME'</formula>
|
||
</one-to-one>
|
||
<one-to-one name="mailingAddress"
|
||
cascade="all">
|
||
<formula>name</formula>
|
||
<formula>'MAILING'</formula>
|
||
</one-to-one>
|
||
</class>
|
||
|
||
<class name="Address" batch-size="2"
|
||
check="addressType in ('MAILING', 'HOME', 'BUSINESS')">
|
||
<composite-id>
|
||
<key-many-to-one name="person"
|
||
column="personName"/>
|
||
<key-property name="type"
|
||
column="addressType"/>
|
||
</composite-id>
|
||
<property name="street" type="text"/>
|
||
<property name="state"/>
|
||
<property name="zip"/>
|
||
</class>]]></programlisting>
|
||
</sect2>
|
||
|
||
<sect2 id="example-mappings-composite-key">
|
||
<title>Composite key example</title>
|
||
<programlisting><![CDATA[<class name="Customer">
|
||
|
||
<id name="customerId"
|
||
length="10">
|
||
<generator class="assigned"/>
|
||
</id>
|
||
|
||
<property name="name" not-null="true" length="100"/>
|
||
<property name="address" not-null="true" length="200"/>
|
||
|
||
<list name="orders"
|
||
inverse="true"
|
||
cascade="save-update">
|
||
<key column="customerId"/>
|
||
<index column="orderNumber"/>
|
||
<one-to-many class="Order"/>
|
||
</list>
|
||
|
||
</class>
|
||
|
||
<class name="Order" table="CustomerOrder" lazy="true">
|
||
<synchronize table="LineItem"/>
|
||
<synchronize table="Product"/>
|
||
|
||
<composite-id name="id"
|
||
class="Order$Id">
|
||
<key-property name="customerId" length="10"/>
|
||
<key-property name="orderNumber"/>
|
||
</composite-id>
|
||
|
||
<property name="orderDate"
|
||
type="calendar_date"
|
||
not-null="true"/>
|
||
|
||
<property name="total">
|
||
<formula>
|
||
( select sum(li.quantity*p.price)
|
||
from LineItem li, Product p
|
||
where li.productId = p.productId
|
||
and li.customerId = customerId
|
||
and li.orderNumber = orderNumber )
|
||
</formula>
|
||
</property>
|
||
|
||
<many-to-one name="customer"
|
||
column="customerId"
|
||
insert="false"
|
||
update="false"
|
||
not-null="true"/>
|
||
|
||
<bag name="lineItems"
|
||
fetch="join"
|
||
inverse="true"
|
||
cascade="save-update">
|
||
<key>
|
||
<column name="customerId"/>
|
||
<column name="orderNumber"/>
|
||
</key>
|
||
<one-to-many class="LineItem"/>
|
||
</bag>
|
||
|
||
</class>
|
||
|
||
<class name="LineItem">
|
||
|
||
<composite-id name="id"
|
||
class="LineItem$Id">
|
||
<key-property name="customerId" length="10"/>
|
||
<key-property name="orderNumber"/>
|
||
<key-property name="productId" length="10"/>
|
||
</composite-id>
|
||
|
||
<property name="quantity"/>
|
||
|
||
<many-to-one name="order"
|
||
insert="false"
|
||
update="false"
|
||
not-null="true">
|
||
<column name="customerId"/>
|
||
<column name="orderNumber"/>
|
||
</many-to-one>
|
||
|
||
<many-to-one name="product"
|
||
insert="false"
|
||
update="false"
|
||
not-null="true"
|
||
column="productId"/>
|
||
|
||
</class>
|
||
|
||
<class name="Product">
|
||
<synchronize table="LineItem"/>
|
||
|
||
<id name="productId"
|
||
length="10">
|
||
<generator class="assigned"/>
|
||
</id>
|
||
|
||
<property name="description"
|
||
not-null="true"
|
||
length="200"/>
|
||
<property name="price" length="3"/>
|
||
<property name="numberAvailable"/>
|
||
|
||
<property name="numberOrdered">
|
||
<formula>
|
||
( select sum(li.quantity)
|
||
from LineItem li
|
||
where li.productId = productId )
|
||
</formula>
|
||
</property>
|
||
|
||
</class>]]></programlisting>
|
||
</sect2>
|
||
|
||
<sect2 id="example-mappings-content-discrimination">
|
||
<title>Content based discrimination</title>
|
||
<programlisting><![CDATA[<class name="Person"
|
||
discriminator-value="P">
|
||
|
||
<id name="id"
|
||
column="person_id"
|
||
unsaved-value="0">
|
||
<generator class="native"/>
|
||
</id>
|
||
|
||
|
||
<discriminator
|
||
type="character">
|
||
<formula>
|
||
case
|
||
when title is not null then 'E'
|
||
when salesperson is not null then 'C'
|
||
else 'P'
|
||
end
|
||
</formula>
|
||
</discriminator>
|
||
|
||
<property name="name"
|
||
not-null="true"
|
||
length="80"/>
|
||
|
||
<property name="sex"
|
||
not-null="true"
|
||
update="false"/>
|
||
|
||
<component name="address">
|
||
<property name="address"/>
|
||
<property name="zip"/>
|
||
<property name="country"/>
|
||
</component>
|
||
|
||
<subclass name="Employee"
|
||
discriminator-value="E">
|
||
<property name="title"
|
||
length="20"/>
|
||
<property name="salary"/>
|
||
<many-to-one name="manager"/>
|
||
</subclass>
|
||
|
||
<subclass name="Customer"
|
||
discriminator-value="C">
|
||
<property name="comments"/>
|
||
<many-to-one name="salesperson"/>
|
||
</subclass>
|
||
|
||
</class>]]></programlisting>
|
||
</sect2>
|
||
|
||
<sect2 id="example-mappings-association-alternatekeys" >
|
||
<title>Associations on alternate keys</title>
|
||
<programlisting><![CDATA[<class name="Person">
|
||
|
||
<id name="id">
|
||
<generator class="hilo"/>
|
||
</id>
|
||
|
||
<property name="name" length="100"/>
|
||
|
||
<one-to-one name="address"
|
||
property-ref="person"
|
||
cascade="all"
|
||
fetch="join"/>
|
||
|
||
<set name="accounts"
|
||
inverse="true">
|
||
<key column="userId"
|
||
property-ref="userId"/>
|
||
<one-to-many class="Account"/>
|
||
</set>
|
||
|
||
<property name="userId" length="8"/>
|
||
|
||
</class>
|
||
|
||
<class name="Address">
|
||
|
||
<id name="id">
|
||
<generator class="hilo"/>
|
||
</id>
|
||
|
||
<property name="address" length="300"/>
|
||
<property name="zip" length="5"/>
|
||
<property name="country" length="25"/>
|
||
<many-to-one name="person" unique="true" not-null="true"/>
|
||
|
||
</class>
|
||
|
||
<class name="Account">
|
||
<id name="accountId" length="32">
|
||
<generator class="uuid.hex"/>
|
||
</id>
|
||
|
||
<many-to-one name="user"
|
||
column="userId"
|
||
property-ref="userId"/>
|
||
|
||
<property name="type" not-null="true"/>
|
||
|
||
</class>]]></programlisting>
|
||
</sect2>
|
||
|
||
</sect1>
|
||
|
||
</chapter> |