十一 02

本章展示了一些较为复杂的关系映射。

23.1. Employer(雇主)/Employee(雇员)
下面关于Employer 和 Employee的关系模型使用了一个真实的实体类 (Employment)来表述,这是因为对于相同的雇员和雇主可能会有多个雇佣时间段。 对于金额和雇员姓名,用Components建模。


映射文件可能是这样:

<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>
用SchemaExport生成表结构。

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
23.2. Author(作家)/Work(作品)
考虑下面的Work,Author 和 Person模型的关系。 我们用多对多关系来描述Work 和 Author, 用一对一关系来描述Author 和 Person, 另一种可能性是Author继承Person。


下面的映射文件正确的描述了这些关系:

<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>
映射中有4个表。works, authors 和 persons 分别保存着work,author和person的数据。author_work是authors和works的关联表。 表结构是由SchemaExport生成的。

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
23.3. Customer(客户)/Order(订单)/Product(产品)
现在来考虑Customer,Order , LineItem 和 Product关系的模型。Customer 和 Order之间 是一对多的关系,但是我们怎么来描述Order / LineItem / Product呢? 我可以把LineItem作为描述Order 和 Product 多对多关系的关联类,在Hibernate,这叫做组合元素。


映射文件如下:

<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>
customers, orders, line_items 和 products 分别保存着customer, order, order line item 和 product的数据。 line_items也作为连接orders 和 products的关联表。

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
23.4. 杂例
这些例子全部来自于Hibernate的test suite,同时你也可以找到其他有用的例子。 可以参考Hibernate的test目录。

TODO: put words around this stuff

23.4.1. "Typed" one-to-one association
<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>
23.4.2. Composite key example
<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>
23.4.3. 共有组合键属性的多对多(Many-to-many with shared composite key attribute)
<class name="User" table="`User`">
   <composite-id>
       <key-property name="name"/>
       <key-property name="org"/>
   </composite-id>
   <set name="groups" table="UserGroup">
       <key>
           <column name="userName"/>
           <column name="org"/>
       </key>
       <many-to-many class="Group">
           <column name="groupName"/>
           <formula>org</formula>
       </many-to-many>
   </set>
</class>
   
<class name="Group" table="`Group`">
   <composite-id>
       <key-property name="name"/>
       <key-property name="org"/>
   </composite-id>
   <property name="description"/>
   <set name="users" table="UserGroup" inverse="true">
       <key>
           <column name="groupName"/>
           <column name="org"/>
       </key>
       <many-to-many class="User">
           <column name="userName"/>
           <formula>org</formula>
       </many-to-many>
   </set>
</class>

23.4.4. Content based discrimination
<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>
23.4.5. Associations on alternate keys
<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"/>
   </id>
   
   <many-to-one name="user"
       column="userId"
       property-ref="userId"/>
   
   <property name="type" not-null="true"/>
   
</class>
完整PDF文档下载:
[sfile]attachment/hibernate_reference.rar[/sfile]


Tags:

作者:Jock

Leave a Reply

You must be logged in to post a comment.

Switch to our mobile site