Глава 18. Пример: Различные Мэппинги

Данная глава демонстрирует несколько мэппингов для более сложных ассоциаций.

18.1. Работодатель/Служащий (Employer/Employee)

Следующая модель взаимоотношений между работодателем, класс Employer и работником Employee использует реальный класс-сущность (entity class) Employment, для представления ассоциации. Это сделано по причине того, что возможно более одного зарегистрированного периода работы одного и того же служащего на одного и того же работодателя. Для представления денежных значений и имен слежащих, используются компоненты (components), MonetoryAmmounts и Name соответсвенно.

Вот возможный мэппинг для данной модели:

<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="MonetoryAmount">
<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

18.2. Инициатор/Работа (Author/Work)

Рассмотрим следующую модель взаимодействий между работой, класс Work инициатором, класс Author и человеком, Person. Мы изображаем взаимодействие между классами Work и Author, как отношение многие-ко-многим (many-to-many). Для отображения взаимотношений между сущностями Author и Person, мы используем ассоциацию один-к-одному (one-to-one). Другой вариант, это использование наследования следующим образом: класс Author расширяет (extend) 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" lazy="true">
<key>
<column name="work_id" not-null="true"/>
</key>
<many-to-many class="Author">
<column name="author_id" not-null="true"/>
</many-to-many>
</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" lazy="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>

В данном мэппинге используется четыре таблицы. Таблицы works, authors и persons содержат информацию о работе, инициаторе и человеке соответсвенно. Таблица author_work, является таблицей-ассоциаций (association table), связывающей инициаторов с работами. Вот схема таблиц, сгенерированная при помощи 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

18.3. Клиент/Заказ/Товар (Customer/Order/Product)

Теперь рассмотрим модель взаимоотношений между клиентом, класс Customer; заказом Order; и классами пункт заказа LineItem, и товар Product. Отоношение между классами Customer и Order явлется отношением один-ко-многим (one-to-many); но как мы должны отобразить отношение Order / LineItem / Product? Мы выбрали вариант мэппига с использованием LineItem, как класса-ассоциации (association class), для представления ассоциации многие-ко-многим (many-to-many) между сущностями Order и Product. В Hibernate такой мэппинг называется мэппингом с использованием составного элемента (composite element), в данном случае LineItem -- класс составного элемента.

Мэппинг:

<hibernate-mapping>

<class name="Customer" table="customers">
<id name="id">
<generator class="native"/>
</id>
<property name="name"/>
<set name="orders" inverse="true" lazy="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" lazy="true">
<key column="order_id"/>
<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, содержат информацию о клиенте, заказе, элементах заказа и товаре соответсвенно. Таблица line_items, также выполняет функции связывающей таблицы для заказов и товаров.

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