659 lines
19 KiB
XML
659 lines
19 KiB
XML
<chapter id="example-mappings">
|
|
<title>Example: Various Mappings</title>
|
|
|
|
<para>
|
|
This chapters shows off some more complex association mappings.
|
|
</para>
|
|
|
|
<sect1 id="example-mappings-emp">
|
|
<title>Employer/Employee</title>
|
|
|
|
<para>
|
|
The following model of the relationship between <literal>Employer</literal> and
|
|
<literal>Employee</literal> uses an actual entity class (<literal>Employment</literal>)
|
|
to represent the association. This is done because there might be more than one
|
|
period of employment for the same two parties. Components are used to model monetary
|
|
values and employee names.
|
|
</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>
|
|
Heres a possible mapping document:
|
|
</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>
|
|
And heres the table schema generated by <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>
|
|
Consider the following model of the relationships between <literal>Work</literal>,
|
|
<literal>Author</literal> and <literal>Person</literal>. We represent the relationship
|
|
between <literal>Work</literal> and <literal>Author</literal> as a many-to-many
|
|
association. We choose to represent the relationship between <literal>Author</literal>
|
|
and <literal>Person</literal> as one-to-one association. Another possibility would be to
|
|
have <literal>Author</literal> extend <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>
|
|
The following mapping document correctly represents these relationships:
|
|
</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>
|
|
There are four tables in this mapping. <literal>works</literal>,
|
|
<literal>authors</literal> and <literal>persons</literal> hold work, author
|
|
and person data respectively. <literal>author_work</literal> is an association
|
|
table linking authors to works. Heres the table schema, as generated by
|
|
<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>
|
|
Now consider a model of the relationships between <literal>Customer</literal>,
|
|
<literal>Order</literal> and <literal>LineItem</literal> and <literal>Product</literal>.
|
|
There is a one-to-many association between <literal>Customer</literal> and
|
|
<literal>Order</literal>, but how should we represent <literal>Order</literal> /
|
|
<literal>LineItem</literal> / <literal>Product</literal>? I've chosen to map
|
|
<literal>LineItem</literal> as an association class representing the many-to-many
|
|
association between <literal>Order</literal> and <literal>Product</literal>. In
|
|
Hibernate, this is called a composite element.
|
|
</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>
|
|
The mapping document:
|
|
</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> and
|
|
<literal>products</literal> hold customer, order, order line item and product data
|
|
respectively. <literal>line_items</literal> also acts as an association table linking
|
|
orders with 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>Miscellaneous example mappings</title>
|
|
|
|
<para>
|
|
These examples are all taken from the Hibernate test suite. You
|
|
will find many other useful example mappings there. Look in the
|
|
<literal>test</literal> folder of the Hibernate distribution.
|
|
</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-composite-key-manytomany">
|
|
<title>Many-to-many with shared composite key attribute</title>
|
|
<programlisting><![CDATA[<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>
|
|
]]></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" revision="2">
|
|
<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"/>
|
|
</id>
|
|
|
|
<many-to-one name="user"
|
|
column="userId"
|
|
property-ref="userId"/>
|
|
|
|
<property name="type" not-null="true"/>
|
|
|
|
</class>]]></programlisting>
|
|
</sect2>
|
|
|
|
</sect1>
|
|
|
|
</chapter>
|
|
|