655 lines
20 KiB
XML
655 lines
20 KiB
XML
<chapter id="example-mappings">
|
|
<title>Ejemplo: Varios Mapeos</title>
|
|
|
|
<para>
|
|
Este capítulo muestra mapeos de asociaciones más complejos.
|
|
</para>
|
|
|
|
<sect1 id="example-mappings-emp">
|
|
<title>Empleador/Empleado</title>
|
|
|
|
<para>
|
|
El siguiente modelo de la relación entre <literal>Employer</literal> y <literal>Employee</literal>
|
|
usa una clase de entidad real (<literal>Employment</literal>) para representar la asociación.
|
|
Esto se ha hecho esto porque podría haber más de un período de empleo para los mismos dos participantes.
|
|
Se usan componentes para modelar valores monetarios y nombres de empleado.
|
|
</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>
|
|
He aquí un documento de mapeo posible:
|
|
</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>
|
|
Y he aquí el esquema de tablas generado por <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>Autor/Obra</title>
|
|
|
|
<para>
|
|
Considera el siguiente modelo de las relaciones entre <literal>Work</literal>,
|
|
<literal>Author</literal> y <literal>Person</literal>. Representamos la relación entre <literal>Work</literal>
|
|
y <literal>Author</literal> como una asociación muchos-a-muchos. Elegimos representar la relación entre
|
|
<literal>Author</literal> y <literal>Person</literal> como una asociación uno-a-uno. Otra posibilidad
|
|
hubiese sido que <literal>Author</literal> extendiera <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>
|
|
El siguiente documento de mapeo representa estas relaciones correctamente:
|
|
</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>
|
|
Hay cuatro tablas en este mapeo. <literal>works</literal>, <literal>authors</literal> y <literal>persons</literal>
|
|
tienen los datos de obra, autor y persona respectivamente. <literal>author_work</literal> es una tabla de
|
|
asociación enlazando autores a obras. He aquí el esquema de tablas, tal como fue generado por
|
|
<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>Cliente/Orden/Producto</title>
|
|
|
|
<para>
|
|
Ahora considera un modelo de las relaciones entre <literal>Customer</literal>,
|
|
<literal>Order</literal> y <literal>LineItem</literal> y <literal>Product</literal>.
|
|
Hay una asociación uno-a-muchos entre <literal>Customer</literal> y <literal>Order</literal>,
|
|
pero, ¿cómo deberíamos representar <literal>Order</literal> / <literal>LineItem</literal> / <literal>Product</literal>?
|
|
He elegido mapear <literal>LineItem</literal> como una clase de asociación representando la
|
|
asociación muchos-a-muchos entre <literal>Order</literal> y <literal>Product</literal>. En Hibernate,
|
|
esto se llama un elemento compuesto.
|
|
</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>
|
|
El documento de mapeo:
|
|
</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> y
|
|
<literal>products</literal> tienen los datos de cliente, orden, ítem de línea de orden y producto
|
|
respectivamente. Además <literal>line_items</literal> actúa como una tabla de asociación enlazando
|
|
órdenes con productos.
|
|
</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>Mapeos misceláneos de ejemplo</title>
|
|
|
|
<para>
|
|
Todos estos ejemplos están tomados de la batería de pruebas de Hibernate.
|
|
Encontrarás muchos otros mapeos de ejemplo útiles allí. Mira en la carpeta
|
|
<literal>test</literal> de la distribución de Hibernate.
|
|
</para>
|
|
|
|
<para>POR HACER: poner palabras alrededor de este material</para>
|
|
|
|
<sect2 id="example-mappings-typed-onetone">
|
|
<title>Asociación uno-a-uno "Tipificada"</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>Ejemplo de clave compuesta</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>Muchos-a-muchos con atributo de clave compuesta compartido</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>Discriminación basada en contenido</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>Asociaciones sobre claves alternativas</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>
|
|
|