Глава 5. Основы объектно-реляционного маппинга

5.1. Объявление маппинга

Объектно-реляционный маппинг описывается в виде XML документа. Документ описывающий маппинг проектировался как легко читаемый и ориентированный на ручное редактирования. Язык описания маппинга ориентирован на Java, это означает что маппинг констуируются вокруг объявлений персистентных java-классов, а не таблиц БД.

Несмотря на то, что большинство пользователей Hibernate определяют и редактируют маппинги в XML вручную, существует некоторое количество инструментов для генерации документов описывающих маппинги. В качестве примеров можно привести XDoclet, Middlegen и AndroMDA.

Начнем с примера описания маппинга:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping package="eg">

<class name="Cat" table="CATS" discriminator-value="C">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<discriminator column="subclass" type="character"/>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true" update="false"/>
<property name="weight"/>
<many-to-one name="mate" column="mate_id"/>
<set name="kittens">
<key column="mother_id"/>
<one-to-many class="Cat"/>
</set>
<subclass name="DomesticCat" discriminator-value="D">
<property name="name" type="string"/>
</subclass>
</class>

<class name="Dog">
<!-- Здесь описывается маппинг для собаки -->
</class>

</hibernate-mapping>

Мы не будем рассматривать содержание документа маппинга. Мы опишем только те элементы и атрибуты документа, которые используются Hibernate в процессе работы. Приведенный выше документ маппинга среди прочего содержит атрибуты и элементы которые используются при генерации схемы базы данных, например атрибут not-null.

5.1.1. Тип документа (Doctype)

Все XML документы описывающие маппинги должны содержать определение типа документа (doctype). Актуальный DTD может быть найден по URL, определенному выше, в директории hibernate-x.x.x/src/net/sf/hibernate или в файле hibernate.jar. Hibernate всегда сначала ищет DTD в classpath.

5.1.2. hibernate-mapping

Элемент имеет три необязательных атрибута. Атрибут schema определяет схему, в терминах СУБД, к которой относятся таблицы описываемые в документе маппинга. Если атрибут указан, Hibernate всегда именует таблицы с указанием схемы, в противном случае, имена таблиц формируются без указания схемы. Атрибут default-cascade определяет правила каскадов для свойств и коллекций, в описании которых явно не определен атрибут cascade. Атрибут auto-import позволяет использовать неполные имена классов (без имени пакета) в языке запросов.

<hibernate-mapping
 schema="schemaName"(1)
 default-cascade="none|save-update" (2)
 auto-import="true|false" (3)
 package="package.name" (4)
 />
(1)

schema (необязательный): Наименование схемы БД.

(2)

default-cascade (необязательный - по умолчанию none): Правила каскадов по умолчанию.

(3)

auto-import (необязательный - по умолчанию true): Определяет возможность использования неполных имен классов (описанных в данном документе) в языке запросов.

(4)

package (необязательный): Префикс пакета для определения полного наименования классов.

Необходимо устанавливать auto-import="false", если существует два или более персистентных класса с одинаковыми наименованиями (без учета имени пакета). Если Hibernate обнаружит два класса в разных пакетах соответсвующих указаному имени, при разборе файла маппинга будет сгенерировано исключение.

5.1.3. class

Персистентные классы определяются в элементе class:

<class
name="ClassName"(1)
table="tableName" (2)
discriminator-value="discriminator_value" (3)
mutable="true|false"(4)
schema="owner"(5)
proxy="ProxyInterface"(6)
dynamic-update="true|false" (7)
dynamic-insert="true|false" (8)
select-before-update="true|false" (9)
polymorphism="implicit|explicit"(10)
where="arbitrary sql where condition" (11)
persister="PersisterClass"(12)
batch-size="N"(13)
optimistic-lock="none|version|dirty|all"(14)
lazy="true|false" (15)
/>
(1)

name: Полное наименование Java класса персистентного класса или интерфейса.

(2)

table: Наименование таблицы БД.

(3)

discriminator-value (необязательный - по умолчанию имя класса): Значение для определения индивидуальных подклассов. Используется при полиморфизме. Допустимые значения включают null и not null

(4)

mutable (необязательный, по умолчанию true): Используется для определения (не)изменчивости класса (mutable/not mutable)

(5)

schema (необязательный): Переопределяет наименование схемы которая была специфицирована в корневом элементе <hibernate-mapping>.

(6)

proxy (необязательный): Определяет интерфейс используемый для ленивой инициализации прокси-класса. В качестве прокси-класса можно использовать тот же (персистетный) класс.

(7)

dynamic-update (необязательно, по умолчанию false): Если параметр установлен в true Hibernate будет генерировать UPDATE запрос в процессе выполнения, при этом запрос будет сгенерирован только на те столбцы, которые были изменены.

(8)

dynamic-insert (необязательный, по умолчанию false): Если параметр установлен в true Hibernate будет генерировать INSERT запрос в процессе выполнения, при этом запрос будет сгенерирован только на те столбцы, значение которых не null.

(9)

select-before-update (необязательный, по умолчанию false): Обязывает Hibernate перед выполнением SQL UPDATE всегда проверять действительно ли изменился объект. В определенных случаях (в действительности, только когда трансиентный объект ассоциируется с новой сессией с использованием update()), это означает что Hibernate генерирует SQL SELECT чтобы убедиться в том, что UPDATE действительно необходим.

(10)

polymorphism (необязательный, по умолчанию implicit): Определяет использования явного (explicit) или неявного (implicit) полиморфизма.

(11)

where (необязательный) определяет произвольное условие WHERE SQL запроса, который используется для получения обьектов определяемого класса.

(12)

persister (необязательный): Используется для указания реализации ClassPersister.

(13)

batch-size (необязательный, по умолчанию 1) определяет "размер пакета (batch size)" для выборки экземпляров определяемого класса по идентификатору.

(14)

optimistic-lock (необязательный, по умолчанию version): Устанавливает стратегию оптимистического блокирования (optimistic locking)

(15)

lazy (необязательный): Установка атрибута lazy="true" является эквивалентом установки интерфеса прокси на себя (смотри атрибут proxy).

Совершенно корректно использование java интерфейса в качестве персистентного класса. Реализации инфтрфейса можно определить при помощи элемента <subclass>. Так же возможно использование статических внутренних классов (static inner class), при этом следует использовать стандартную форму наименоваия класса (например eg.Foo$Bar)

Неизменяемые классы (immutable classes), с установленым атрибутом mutable="false" не могут быть изменены или удалены приложением. Это позволяет Hibernate немного оптимизировать скорость работы с такими классами.

Необязательный атрибут proxy разрешает ленивую инициализацию экземпляров персистентного класса. Изначально Hibernate возвращает CGLIB прокси, реализующие указанный интерфейс, и только после первого вызова метода прокси интерфейса загружается сам персистентный класс. Смотри так же раздел "Прокси для ленивой инициализации" ниже.

Неявный (implicit) полиморфизм означает что экземпляры класса будут созданы для запроса по любому суперклассу или реализованному персистентным классом интерфейсу. Явный (explicit) полиморфизм означает, что экземпляры класса будут создаваться только при запросах с явным указанием имени персистентного класса. TODO: Implicit polymorphism means that instances of the class will be returned by a query that names any superclass or implemented interface or the class and that instances of any subclass of the class will be returned by a query that names the class itself. Explicit polymorphism means that class instances will be returned only be queries that explicitly name that class and that queries that name the class will return only instances of subclasses mapped inside this <class> declaration as a <subclass> or <joined-subclass>. For most purposes the default, polymorphism="implicit", is appropriate. Explicit polymorphism is useful when two different classes are mapped to the same table (this allows a "lightweight" class that contains a subset of the table columns).

Атрибут persister позволяет настроить стратегию персистенции для класса. Например, вы можете написать свою реализацю net.sf.hibernate.persister.EntityPersister или же совершенно иную реализацию интерфейса net.sf.hibernate.persister.ClassPersister которая будет реализовывать персистенцию, через, например, вызов хранимых процедур, сериализацию в файловую струкуру или LDAP. Смотри для примера net.sf.hibernate.test.CustomPersister (реализует "персистентность" в Hashtable).

Учтите что установки dynamic-update и dynamic-insert не наследуются подклассами должны быть повторно установлены в элеметах <subclass> или <joined-subclass>. Эти установки могут увеличить производительность в некоторых случаях, но так же возможно и уменьшение производительности в иных случаях. Используйте их рассудительно.

Использование select-before-update обычно уменьшает производительность. Используется, чтобы избежать лишних вызовов тригеров на update.

Если вы включили dynamic-update, у вас есть выбор стратегии оптимистической блокировки (optimistic locking):

  • version проверять столбец версии/даты

  • all проверять все столбцы

  • dirty проверять измененные столбцы

  • none не использовать оптимистичесую блокировку

Мы очень настойчиво рекомендуем использовать стобцы версии/даты для оптимистической блокировки в Hibernate. Эта стратегия обеспечивает оптимальную производительность и это единственная стратегия обеспечивающая корректное отслеживание изменений, которые производились все контекста сессии (при использовании Session.update()). Помните, что свойство версии или даты никогда не должны быть null, в противном случае, независимо от стратегии unsaved-value, экземляры будут опознаны как транзитный (transient).

5.1.4. id

Замеппленые классы должны декларировать столбец первичного ключа в таблице базы данных. Большинство классов также должны описывать собственные свойства в стиле JavaBeans, включая уникальный идентификатор сущности. Элемент <id> в mapping-файле определят отображение этого уникального поля на столбец таблицы, выступающий в роли основного ключа (primary key).

<id
name="propertyName"(1)
type="typename"(2)
column="column_name" (3)
unsaved-value="any|none|null|id_value" (4)
access="field|property|ClassName"> (5)

<generator class="generatorClass"/>
</id>
(1)

name (необязательный): Наименование свойства идентификатора.

(2)

type (необязательный): Имя определяющее Hibernate-тип свойства.

(3)

column (необязательно - по умолчанию имя свойства): Название колонки основного ключа.

(4)

unsaved-value (необязательно - по умолчанию null): Значени свойства идентификатора, которое обзначает, что экземпляр новый (в терминах персистентного хранилища). Отличает данный экземпляр от транзитных экземпляров, которые были загружены или сохранены в предыдущей версии.

(5)

access (необязательный - по умолчанию property): Эту стратегию Hibernate будет использовать для доступа к данному свойству объекта.

Если атрибут name не указан, предполагается, что класс не имеет свойства идентификатора.

Атрибут unsaved-value важен! Если свойство идентификатор вашего класса по умолчанию не null, вы должны установить атрибут "unsaved-value" в соответствующее значение.

Существует альтернативное объявление <composite-id> для доступа к унаследованным (legacy) данным c композитными ключами. Мы настойчиво не рекомендуем использовать композитные ключи в других случаях.

5.1.4.1. generator

Обязательный дочерний элемент <generator>'а определяет Java класс используемый для генерации уникальных идентификаторов экземпляров песистентных классов. При необходимости используется элемент <param> для передачи параметров инициализации или конфигурирации экземпляра генератора.

<id name="id" type="long" column="uid" unsaved-value="0">
<generator class="net.sf.hibernate.id.TableHiLoGenerator">
<param name="table">uid_table</param>
<param name="column">next_hi_value_column</param>
</generator>
</id>

Все генераторы реализуют интерфейс net.sf.hibernate.id.IdentifierGenerator. Это очень простой интерфейс; многие приложения могут использовать свою специальную реализацию генератора. Несмотря на это, Hibernate включает в себя множество встроенных генераторов. Ниже идут краткие наименования (ярлыки) для встроенных генераторов:

increment

генерирует идентификаторы типа long, short или int, уникальные только когда другие процессы не добавляют данные в ту же таблицу. Не использовать в кластере.

identity

Поддерживает identity колонки в in DB2, MySQL, MS SQL Server, Sybase и HypersonicSQL. Тип возвращаемого идентификатора long, short или int.

sequence

Использует последовательность (sequence) в DB2, PostgreSQL, Oracle, SAP DB, McKoi или generator в Interbase. Тип возвращаемого идентификатора long, short или int.

hilo

использует hi/lo алгоритм для эффективной генерации идентификаторов которые имеют тип long, short или int, требуют наименования таблицы и столбца (по умолчанию hibernate_unique_key и next_hi соответсвенно), как источник значений hi. Алгоритм hi/lo генерирует идентификаторы которые кникальный только для отдельный баз данных. Не используйте этот генератор для соединений через JTA или пользовательских соединений.

seqhilo

использует алгоритм hi/lo для генерации идентификаторов типа long, short или int, с использованием последовательности (sequence) базы данных.

uuid.hex

Использует 128-битный UUID алгоритм для генерации строковых идентификаторов, уникальных в пределах сети (изспользуется IP-адрес). UUID - строка длинной в 32 символа, содержащая шеснадцатеричное представление числа.

uuid.string

использует тот же UUID алгоритм, однако строка при использовании этого генератора состоит из 16 (каких-то) ANSII символов. Не использовать с PostgreSQL.

native

выбирает identity, sequence или hilo, в зависимости от возможностей используемой базы данных.

assigned

предоставляет приложению возможность самостоятельно задать идентификатор объекта перед вызовом метода save().

foreign

используется идентификатор другого, ассоциированного объекта. Обычно используется в крньюкции с <one-to-one> оассоциацией по первичному ключу.

5.1.4.2. Алгоритм Hi/Lo

Генераторы hilo и seqhilo предоставляют две альтернативные реализации алгортма hi/lo, наиболее предпочтительного подхода для генерации идентификаторов. Первая реализация требует "специальной" таблицы в базе данных, для хранения следующего "hi" значения. Вторая реализация использует последовательность (Oracle-style), в базах данных, которые их поддерживают.

<id name="id" type="long" column="cat_id">
<generator class="hilo">
<param name="table">hi_value</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>

К сожалению вы не можете использовать hilo в случае поставки своего соединения (Connection) в Hibernate, так же невозможно его использование в конфигурации, когда Hiberante использует источник данных сервера приложений, управляемый JTA. Hiberante должен иметь возможность получать "hi" значение в новой тразакции. Стандартным подходом в EJB, является использование session stateless bean для реализации алгоритма hi/lo.

5.1.4.3. Алгоритм UUID

UUID содержат: IP адрес, время старта JVM (с точностью до четверти секунды), системное время и счетчик (уникальный в рамках JVM). Это лучшее что можно сделать не используя JNI, так как невозможно из Java кода получить MAC адрес или адрес в памяти.

Не пробуйте использовать uuid.string в PostgreSQL.

5.1.4.4. Последовательности и identity колонки

Вы можете использовать генератор ключей identity для баз данных с поддержкой indentity столбцов (DB2, MySQL, Sybase, MS SQL). Для баз данных поддерживающих последовательности можно использовать sequence стиль для генерации ключей. Обе эти стратегии требуют двух SQL запросов для вставки нового объекта в базу данных.

<id name="id" type="long" column="uid">
<generator class="sequence">
<param name="sequence">uid_sequence</param>
</generator>
</id>
<id name="id" type="long" column="uid" unsaved-value="0">
<generator class="identity"/>
</id>

Для разработки кроссплатформеннох приложений используйте стратегию native. Она будет использвать identity, sequence и hilo стратегии в зависимости от возможностей той базы данных с которой в данный момент времени работает Hibernate.

5.1.4.5. Задаваемые идентификаторы

Если вы хотите чтобы приложение само назначало идетнификаторы, вы можете использовать генератор assigned. Этот специальный генератор использует идетнификаторы которые устанавливаются приложением. Для этого приложение устанавливает идентификатор в соответсвующее свойство объекта. Будте очень внимательны при использовании этой возможности для установки ключей (в большинстве случаев это решение сигнализирует о плохом дизайне приложения).

Вследствие свойственной ему природы, сущности которые используют этот генератор, не могут быть сохранены через метод Session.saveOrUpdate(). Вместо этого вы должны явно указывать Hibernate, должен ли объект быть создан или обновлен вызовами соотвествующих методов объекта Session: save() или update().

5.1.5. composite-id

<composite-id
name="propertyName"
class="ClassName"
unsaved-value="any|none"
access="field|property|ClassName">

<key-property name="propertyName" type="typename" column="column_name"/>
<key-many-to-one name="propertyName class="ClassName" column="column_name"/>
......
</composite-id>

Для таблиц с композитными ключами вы можете отображать несколько свойств класса как свойства идентификации объекта. Элемент <composite-id> принимает отображения свойств при помощи дочерних элементов <key-property> и <key-many-to-one>.

<composite-id>
<key-property name="medicareNumber"/>
<key-property name="dependent"/>
</composite-id>

Ваш персистентный класс должен переопределять методы equals() и hashCode() для реализации эквивалентности композитных идентификаторов. Он так же должен реализовывать интерфейс Serializable.

К сожалению, возможность задавать составные идентификаторы подразумевает то, что персистентный объект и есть идентификатор. Нет возможности для удобной обработки, чем посредством самого объекта. Вы должны создать сущность персистентного класса самостоятельно и установить его идентифицирующее свойство перед тем как выполнить загрузку load() персистентного состояния ассоциированного с данным составным идентификатором. Мы опишем более подходящий способ, где составные идентификаторы реализованы отдельным классом в разделе Раздел 7.4, «Компоненты как составные идентификаторы». Атрибуты, которые описываются ниже, применимы только для альтернативного метода:

  • name (необязательно): свойство типа копонента, которое содержит составной идентификатор (см. следующий раздел).

  • class (опционально, по умолчанию тип свойства определяет через рефлексию): компонент данного класса используется как составной идентификатор (см. следующий раздел).

  • unsaved-value (опционально, по умолчанию none): если установлен в any, то это указывает на то, что транзитные сущности рассматриваются как новые.

5.1.6. discriminator

Элемент <discriminator> необходим для полиморфной персистентности, использующей стратегию отображения table-per-class-hierarchy. Данный элемент объявляет колонку-дискриминатор, по которой определяется соответствие записи в таблице конкретному классу в иерархии. Дискриминатор может иметь один из следующих типов: string, character, integer, byte, short, boolean, yes_no, true_false.

<discriminator
column="discriminator_column"(1)
type="discriminator_type"(2)
force="true|false" (3)
/>
(1)

column (необязательно, по умолчанию class) название колонки-дискриминатора.

(2)

type (необязательно, по умолчанию string) имя Hibernate-типа.

(3)

force (необязательно, по умолчанию false) "принуждает" Hibernate указывать все допустимые значения дискриминатора, даже если происходит извлечение всех сущностей корневого класса в иерархии. (Примечание переводчика: имеется в виду указание значений дескриминатора в генерируемом Hibernate SQL запросе. В общем случае, если выбираются все подклассы, в таком ограничении SQL запроса нет смысла.)

Соответствующие значения колонки дискриминатора для каждого класса задаются в атрибуте discriminator-value для элементов <class> и <subclass>.

Атрибут force полезен только в случае, если таблица содержит записи с дополнительными значениями дискриминатора, которые не отображаются в персистентном классе. Обычно данный атрибут не используется.

5.1.7. version (необязательно)

Элемент <version> отражает то, что таблица содержит записи с пометкой о версии. Это особенно полезно, если вы планируете использовать длинные транзакции (см. ниже).

<version
column="version_column"(1)
name="propertyName"(2)
type="typename"(3)
access="field|property|ClassName"(4)
unsaved-value="null|negative|undefined"(5)
/>
(1)

column (необязательно, по умолчанию берется имя свойства): имя колонки, которая хранит номера версий.

(2)

name: Имя свойства персистентного класса.

(3)

type (необязательно, по умолчанию integer): тип свойства версии.

(4)

access (необязательно, по умолчанию property): стратегия, которую должен использовать Hibernate для доступа к значению свойства.

(5)

unsaved-value (необязательно, по умолчанию undefined): Значение свойства версии, которое указывает на то, что сущность еще не сохранена (unsaved). Не путайте несохраненные сущности от транзитных, которые были сохранены или загружены в предыдущей сессии. (undefined указывает на то, что будет использовано значение идентификатора.)

Номера версий могут быть типа long, integer, short, timestamp либо calendar.

5.1.8. timestamp (необязательно)

Элемент <timestamp> указывает на то, что таблица содержит записи промаркированные временной меткой. Этот элемент выступает как альтернатива маркерам версии. Временные метки по определению менее безопасная реализация оптимистической блокировки. Тем не менее, иногда приложение использует временные метки для других целей

<timestamp
column="timestamp_column" (1)
name="propertyName" (2)
access="field|property|ClassName" (3)
unsaved-value="null|undefined"(4)
/>
(1)

column (необязательно, по умолчанию используется имя свойства): имя колонки, которая содержит временную метку.

(2)

name: Имя в стиле JavaBeans типа Date или Timestamp свойства персистентного класса.

(3)

access (необязательно, по умолчанию property): стратегия, которую должен использовать Hibernate для доступа к значению свойства.

(4)

unsaved-value (необязательно, - по умолчанию null): Значение свойства времени, которое указывает на то, что сущность еще не сохранена (unsaved). Не путайте несохраненные сущности от транзитных, которые были сохранены или загружены в предыдущей сессии. (undefined указывает на то, что будет использовано значение идентификатора.)

Примечание: элемент <timestamp> эквивалентен элементу <version type="timestamp">.

5.1.9. property

Элемент <property> объявляет персистентное, в стиле JavaBeans, свойство класса.

<property
name="propertyName" (1)
column="column_name"(2)
type="typename" (3)
update="true|false" (4)
insert="true|false" (4)
formula="arbitrary SQL expression"(5)
access="field|property|ClassName" (6)
/>
(1)

name: Имя свойства, начинается с буквы в нижнем регистре.

(2)

column (необязательно, по умолчанию подставляется название свойства): имя соответствующей колонки в таблице базы данных.

(3)

type (необязательно): название Hibernate-типа.

(4)

update, insert (необязательно, по умолчанию true) : указывает на то, что соответствующая колонка должна включаться в SQL-выражения UPDATE и/или INSERT. Установка обоих свойств в false позволяет задавать значение этого свойства либо из другого свойства, которое отображено в той же колонке/колонках, либо посредством триггера, либо другим приложением.

(5)

formula (необязательно): SQL-выражение, которое вычисляет значение свойства. Вычисляемые поля не должны отображаться в колонку таблиц базы данных.

(6)

access (необязательно, по умолчанию property): стратегия, которую Hibernate должен использовать для доступа к значению свойства.

значение свойства type может быть одним из следующих:

  1. Имя базового типа Hibernate (например, integer, string, character, date, timestamp, float, binary, serializable, object, blob).

  2. Имя Java-класса (например, int, float, char, java.lang.String, java.util.Date, java.lang.Integer, java.sql.Clob).

  3. Имя производного от PersistentEnum класса (например, eg.Color).

  4. Имя сериализуемого Java-класса.

  5. Имя пользовательского класса (например, com.illflow.type.MyCustomType).

Если вы не указываете значение свойства type, Hibernate будет использовать рефлексию для указанного свойства для подбора соответствующего Hibernate типа. Hibernate попытается определить имя класса возвращаемого свойства методом get() используя правила 2, 3, 4 в этом порядке. Тем не менее, этого не всегда бывает достаточно. В некоторых случаях вам все же необходимо указать атрибут type. (Например для различия между Hibernate.DATE и Hibernate.TIMESTAMP, либо для указания пользовательского типа.)

Атрибут access позволяет управлять задавать Hibernate метод доступа к полю во время исполнения. По умолчанию Hibernate вызывает методы get/set для доступа к полю. Если вы задаете access="field", то Hibernate будет обходить методы get/set и обращаться к полю напрямую, используя рефлексию. Вы можете указать вашу собственную стратегию для доступа указав класс, который реализует интерфейс net.sf.hibernate.property.PropertyAccessor.

5.1.10. many-to-one

Обычная связь с другим персистентным классов объявляется используя элемент many-to-one. В реляционных терминах это ассоциация многих к одному. В действительности это просто ссылка на объект.

<many-to-one
name="propertyName"(1)
column="column_name" (2)
class="ClassName"(3)
cascade="all|none|save-update|delete"(4)
outer-join="true|false|auto" (5)
update="true|false"(6)
insert="true|false"(6)
property-ref="propertyNameFromAssociatedClass" (7)
access="field|property|ClassName"(8)
/>
(1)

name: Имя свойства.

(2)

column (необязательно): Имя колонки.

(3)

class (необязательно - по умолчанию тип поля определяется через рефлексию): Имя ассоциированного класса.

(4)

cascade (необязательно): Определяет, какая операция будет выполняться каскадом от родительского объекта к ассоциированному.

(5)

outer-join (необязательно - по умолчанию auto): Задействует извлечение ассоциированных объектов, используя объединения outer-join если опция hibernate.use_outer_join конфигурационного файла включена.

(6)

update, insert (необязательно - по умолчанию true) определяет то, что отображаемые колонки будут включены в SQL-запросы UPDATE и/или INSERT. Установка обоих свойств в false позволяет задавать значение этого свойства либо из другого свойства, которое отображено в той же колонке/колонках, либо посредством триггера, либо другим приложением.

(7)

property-ref: (необязательно) Имя ключевого свойства ассоциированного класса. По этому свойству будет происходить связывание (join). Если не указано, то используется первичный ключ ассоциированного класса.

(8)

access (необязательно - по умолчанию property): Стратегия, которую использует Hibernate для доступа к значению данного поля.

Атрибут cascade может принимать следующие значения: all, save-update, delete, none. Установка значения отличного от none повлечет определенные операции над ассоциированным (дочерним) объектом. См ниже "Жизненный цикл объектов".

Атрибут outer-join может принимать три следующих значения:

  • auto (по умолчанию) извлекает ассоциированные объекты используя outer join если ассоциированный класс не имеет прокси.

  • true Всегда извлекать ассоциированные объекты используя outer join.

  • false Никогда не извлекать ассоциированные объекты используя outer join.

Типичное объявление ассоциации many-to-one выглядит так

<many-to-one name="product" class="Product" column="PRODUCT_ID"/>

Атрибут property-ref использоваться только для связи c унаследованными данными, когда внешний ключ ссылается на уникальное значение ассоциированной таблицы отличной от первичного ключа. Это опасное реляционное решение. Например, возможно, что класс Product имеет уникальный последовательный номер, который не является первичным ключем. (Атрибут unique конролирует герерацию DDL Hibernate'ом. Генерация производится при помощи утилиты SchemaExport.)

<property name="serialNumber" unique="true" type="string" column="SERIAL_NUMBER"/>

Отображение для OrderItem может использовать:

<many-to-one name="product" property-ref="serialNumber" column="PRODUCT_SERIAL_NUMBER"/>

В действительности, так делать крайне не рекомендуется.

5.1.11. one-to-one

Ассоциация "один к одному" с другим персистентным классом можно объявить, используя элемент one-to-one.

<one-to-one
name="propertyName"(1)
class="ClassName"(2)
cascade="all|none|save-update|delete"(3)
constrained="true|false" (4)
outer-join="true|false|auto" (5)
property-ref="propertyNameFromAssociatedClass" (6)
access="field|property|ClassName"(7)
/>
(1)

name: Имя свойства.

(2)

class (необязательно - по умолчанию определяется рефлексией исходя из типа поля): Имя ассоциированного класса.

(3)

cascade (необязательно) определяет какая операция будет выполняться каскадом от родительского объекта к ассоциированному.

(4)

constrained (необязательно) определяет то, что внешний ключ, ссылающийся на таблицу ассоциированного класса, ограничен первичным ключом этой таблицы. Эта опция влияет на порядок, в котором выполняются каскадные операции save() и delete() (а так же используется утилитой экспортирующей схему - schema export tool).

(5)

outer-join (необязательно - по умолчанию auto): Задействует извлечение ассоциированных объектов, используя объединения outer-join если опция hibernate.use_outer_join конфигурационного файла включена.

(6)

property-ref: (необязательно) Имя свойства ассоциированного класса, которое входит в первичный ключ данного класса. Если не указано, то используется первичный ключ ассоциированного класса.

(7)

access (необязательно, по умолчанию property): Стратегия, которую должен использовать Hibernate для доступа к данному полю.

Существует два вида ассоциаций "один к одному":

  • связь по первичному ключу

  • связь по уникальному внешнему ключу

Для организации ассоциации по первичному ключу нет надобности в дополнительных столбцах; если две записи связаны такой ассоциацией, то это значит, что две записи в двух таблицах имеют одно и то же значение первичного ключа. Следовательно, если вы хотите ассоциировать два объекта, чтобы они были связаны по первичному ключу, то вы должны убедиться в том, что их идентификаторам присвоены одинаковые значения!

Для ассоциации по первичному ключу добавьте следующее отображение для классов Employee и Person соответственно.

<one-to-one name="person" class="Person"/>
<one-to-one name="employee" class="Employee" constrained="true"/>

Теперь мы должны убедиться в том, что первичные ключи связанных записей в таблицах идентичны. Мы используем специальный генератор Hibernate foreign:

<class name="person" table="PERSON">
<id name="id" column="PERSON_ID">
<generator class="foreign">
<param name="property">employee</param>
</generator>
</id>
...
<one-to-one name="employee"
class="Employee"
constrained="true"/>
</class>

Сохраняемому экземпляру класса Person присваивается тоже значение первичного ключа, которое присвоено экземпляру класса Employee на который ссылается свойство employee класса Person.

Как альтернативный вариант описания связи "один к одному" от Employee к Person, через уникальный внешний ключ можно использовать следующую запись:

<many-to-one name="person" class="Person" column="PERSON_ID" unique="true"/>

Эта ассоциация может быть двунаправленной после добавления следующего выражения к маппингу класса Person:

<one-to-one name"employee" class="Employee" property-ref="person"/>

5.1.12. component, dynamic-component

Элемент <component> отображает поля вложенного объекта на колонки таблицы родительского класса. Компоненты могут в свою очередь определять свои собственные свойства, компоненты или коллекции. Смотрите "Компоненты" ниже.

<component 
name="propertyName" (1)
class="className" (2)
insert="true|false" (3)
upate="true|false"(4)
access="field|property|ClassName">(5)

<property ...../>
<many-to-one .... />
........
</component>
(1)

name: Наименование свойства (ссылающегося на компонентный объект).

(2)

class (необязательно - по умолчанию тип компонента определяется используя рефлексию): Наименование класса компонента.

(3)

insert: Если установлен в true, то отображаемые поля компонента участвуют в SQL-запросах INSERT.

(4)

update: Если установлен в true, то отображаемые поля компонента участвуют в SQL-запросах UPDATE.

(5)

access (необязательно - по умолчанию property): Стратегия, которую должен использовать Hibernate при доступе к этому компоненту через родительский объект.

Вложенные тэги <property> отображают поля компонента в колонки таблицы.

Элемент <component> допускает вложенный элемент <parent> который отображает свойство компонента как обратную ссылку на родительский объект.

Элемент <dynamic-component> позволяет использовать Map как компонент, в котором имена полей соответствуют ключам Map'а.

5.1.13. subclass

И наконец, полиморфная персистентность требует объявления каждого подкласса базового класса. Для (рекомендованной) стратегии отображения table-per-class-hierarchy используется элемент <subclass>.

<subclass
name="ClassName"(1)
discriminator-value="discriminator_value" (2)
proxy="ProxyInterface"(3)
lazy="true|false" (4)
dynamic-update="true|false"
dynamic-insert="true|false">

<property .... />
.....
</subclass>
(1)

name: Полное имя класса для подкласса.

(2)

discriminator-value (необязательно - по умолчанию имя класса): Значение, которое отличает индивидуальный подкласс.

(3)

proxy (необязательно): Определяет класс либо интерфейс, используемый в качестве прокси для отложенной инициализации отображаемых объектов данного типа.

(4)

lazy (необязательно): Установка lazy="true" сокращенная запись эквивалентная указанию имени данного класса в роли собственного proxy.

Каждый подкласс должен объявлять свои собственные персистентные поля и подклассы. Допускается наследование свойств <version> и <id> от базового класса. Каждый подкласс в иерархии должен определять уникальное значение discriminator-value. Если это значение не указано, то в качестве дискриминатора используется полное имя класса.

5.1.14. joined-subclass

В качестве альтернативы, подкласс, объекты которого хранятся в отдельной таблице (стратегия отображения table-per-subclass), объявляется используя элемент <joined-subclass>.

<joined-subclass
name="ClassName"(1)
proxy="ProxyInterface"(2)
lazy="true|false" (3)
dynamic-update="true|false"
dynamic-insert="true|false">

<key .... >

<property .... />
.....
</joined-subclass>
(1)

name: Полное имя подкласса.

(2)

proxy (необязательно): Определяет класс либо интерфейс, используемый в качестве прокси для отложенной инициализации отображаемых объектов данного типа.

(3)

lazy (необязательно): Установка lazy="true" сокращенная запись эквивалентная указанию имени данного класса в роли собственного proxy.

Для данной стратегии отображения не требуется указывать колонку discriminator. Однако, каждый подкласс должен объявлять колонку таблицы, которая содержит идентификатор отображаемый элементом <key>. Маппинг приведенный в начале раздела может быть переписан следующим образом:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping package="eg">

<class name="Cat" table="CATS">
<id name="id" column="uid" type="long">
<generator class="hilo"/>
</id>
<property name="birthdate" type="date"/>
<property name="color" not-null="true"/>
<property name="sex" not-null="true"/>
<property name="weight"/>
<many-to-one name="mate"/>
<set name="kittens">
<key column="MOTHER"/>
<one-to-many class="Cat"/>
</set>
<joined-subclass name="DomesticCat" table="DOMESTIC_CATS">
	<key column="CAT"/>
<property name="name" type="string"/>
</joined-subclass>
</class>

<class name="eg.Dog">
<!-- Здесь некое отображение для класса Dog -->
</class>

</hibernate-mapping>

5.1.15. map, set, list, bag

Коллекции рассматриваются позже.

5.1.16. import

Предположим ваше приложение имеет два персистентных класса с одним названием, но вы не желаете указывать полностью определенное имя классов в запросах Hibernate. Классы могут быть импортированы явно вместо использования опции auto-import="true". Вы можете импортировать классы и интерфейсы предварительно присвоив им короткие псевдонимы.

<import class="java.lang.Object" rename="Universe"/>
<import
class="ClassName"(1)
rename="ShortName" (2)
/>
(1)

class: Полное имя Java класса.

(2)

rename (необязательно - по умолчанию имя класса, без указания пакета): Псевдоним класса, имя, которое может использоваться в запросах.

5.2. Типы Hibernate

5.2.1. Сущности и значения

Для понимания поведения языковых объектов Java по отношению к персистентным сервисам мы должны разбить первые на две группы:

Сущности, существующие независимо от других объектов содержащих ссылки на эти сущности. Они отличаются от обычной модели Java, когда объекты, на которые нет ссылок, удаляются сборщиком мусора. Сущности должны явно сохраняться и удаляться (за исключением тех случаев, когда сохранение и удаление происходит каскадом по направлению ссылок от родительских объектов к подчиненным). Эта модель отличается от модели ODMG, где объект считается персистентным, если он доступен через другой, уже персистентный, объект. Модель Hibernate лучше отвечает требованиям к использованию объектов приложения в больших системах. Сущности поддерживают циклические и разделяемые ссылки. Также они могут быть весионными.

Персистентное состояние состоит из ссылок на другие сущности и экземляры типов-значения. Значения это примитивы, коллекции, компоненты и другие неизменяемые объекты. В отличии от сущностей экземляры типов значений (в частности коллекции и компоненты) сохраняются и удаляются по доступности. Поскольку объекты типов-значений (и примитивы) сохраняются и удаляются вместе с содержащей их сущностью они не могут иметь независимую версионность. Значения не имеют так же независимой идентификатоции и, таким образом, не могут быть разделены между двумя сущностями или коллекциями.

Все типы в Hibernate, за исключением коллекций, поддерживают семантику null-указателей.

До этого момента мы использовали термин "персистентный класс" для ссылки на сущности. Мы будем продолжать делать это. Строго говоря, не все определенные пользователем классы с персистентным состоянием являются сущностями. Например, компонент - это определенный пользователем класс с семантикой типа-значения (коспоненты, являются частью сущностей их содержащих, и считаются полями этих сущностей).

5.2.2. Базовые типы-значения

Базовые типы могут быть грубо разделены следующим образом

integer, long, short, float, double, character, byte, boolean, yes_no, true_false

Мапинги примитивных Java-типов либо классов оберток на соответсвующие (зависимые от поставщика) SQL-типы колонок таблиц. boolean, yes_no и true_false являются альтернативными обозначениями для Java-типов boolean или java.lang.Boolean.

string

Отображение типа java.lang.String в VARCHAR (либо Oracle VARCHAR2).

date, time, timestamp

Отображение типа java.util.Date и его подклассов в в SQL-типы DATE, TIME и TIMESTAMP (либо эквивалентные).

calendar, calendar_date

Отображение типа java.util.Calendar в SQL-типы TIMESTAMP и DATE (либо эквивалентные).

big_decimal

Отображение типа java.math.BigDecimal в NUMERIC (или Oracle NUMBER).

locale, timezone, currency

Отображение типа java.util.Locale, java.util.TimeZone и java.util.Currency в VARCHAR (или Oracle VARCHAR2). Экземпляры Locale и Currency отображаются в их ISO коды. Экземпляры TimeZone отображаются в их идентификаторы (ID).

class

Отображение типа java.lang.Class в VARCHAR (или Oracle VARCHAR2). Class отображается как его полное имя.

binary

Отображает массивы байтов в соответствующий бинарный SQL-тип.

text

Отображает длинные строки Java в SQL CLOB либо TEXT.

serializable

Отображает сериализуемые Java-типы в соответствующие бинарные SQL-типы. Вы так же можете обозначить Hibernate-типом serializable имя сериализуемого Java-класса либо интерфейса, который не является базовым типом и не реализует интерфейс PersistentEnum.

clob, blob

Отображение типа JDBC классов java.sql.Clob и java.sql.Blob. Эти типы могут быть неудобными для некоторых приложений, так как объекты типов blob и clob не могут использоваться вне транзакций. (К тому же, драйвера поддерживают эти типы не полностью и неодинаково.)

Уникальные идентификаторы сущностей и коллекций могут быть любого базового типа за исключением binary, blob и clob. (Составные идентификаторы так же допускаются, смотри ниже.)

Базовые типы-значения описываются константами объявленными в net.sf.hibernate.Hibernate. Например, Hibernate.STRING представляет тип string.

5.2.3. Персистентные перечисляемые типы (enum)

Перечисляемый тип является базовой идиомой Java когда класс имеет константное (небольшое) количество неизменяемых экземляров (прим. переводчика в Java 5 это введено на уровне языка, в более ранних версиях для этого применялся специальных паттерн). Вы можете создавать персистентные перечисляемые типы реализуя интерфейс net.sf.hibernate.PersistentEnum, и определяя операции toInt() и fromInt():

package eg;
import net.sf.hibernate.PersistentEnum;

public class Color implements PersistentEnum {
private final int code;
private Color(int code) {
this.code = code;
}
public static final Color TABBY = new Color(0);
public static final Color GINGER = new Color(1);
public static final Color BLACK = new Color(2);

public int toInt() { return code; }

public static Color fromInt(int code) {
switch (code) {
case 0: return TABBY;
case 1: return GINGER;
case 2: return BLACK;
default: throw new RuntimeException("Unknown color code");
}
}
}

Имя Hibernate-типа - это просто имя перечисляемого класса, в данном случае eg.Color.

5.2.4. Пользовательские типы-значения

Для разработчиков относительно просто создать свои типы-значения. Например, вы можете захотеть сохранять свойства типа java.lang.BigInteger в колонки типа VARCHAR. Hibernate не предоставляет встроенного типа для этого. Но определение пользовательских типов не огранивается отображением свойств (либо элементов коллекций) в единичный столбец таблицы. Таким образом, например, вы можете иметь свойство getName()/setName() типа java.lang.String которое хранится в колонках FIRST_NAME, INITIAL, SURNAME.

Для реализации пользовательского типа, реализуйте один из интерфесов net.sf.hibernate.UserType либо net.sf.hibernate.CompositeUserType и объявите свойство, используя полное имя класса вашей реализации типа. Просмотрите net.sf.hibernate.test.DoubleStringType для уточнения доступных возможностей.

<property name="twoStrings" type="net.sf.hibernate.test.DoubleStringType">
<column name="first_string"/>
<column name="second_string"/>
</property>

Примечание: используйте тэги <column> для отображения свойства в несколько колонок.

Хотя богатство встроенных Hibernate-типов и поддержка компонентов подразумевает то, что нужда в использовании пользовательских типов возникает достаточно редко, все же считается хорошей практикой использование последних в качестве (не сущностных) классов для типов, часто используемых в вашем приложении. Например, класс MonetoryAmount хороший кандидат для CompositeUserType, хотя он может отображаться как компонент. Главная мотивация это абстракция. С пользователскими типами, ваш документ маппинга будет более усточивым к возможным изменениям в будущем в случае если вы измените представление денежного типа.

5.2.5. Отображение Any типа

Есть еще один тип для отображения свойств. Элемент отображения <any> объявляет полиморфную ассоциацию для классов из нескольких таблиц. Этот тип отображения всегда требует более одной колонки. Первая колонка содержит тип ассоциированной сущности. Остальные колонки содержат идентификатор. Невозможно пределить внешний ключ для данного типа ассоциации, таким образом, это отображение обычно не используется для полиморфных ассоциаций. Вы должны использовать подобное отображение только в особых случаях (например, запись разнотипных данных, обращение к данным пользовательской сессии).

<any name="anyEntity" id-type="long" meta-type="eg.custom.Class2TablenameType">
<column name="table_name"/>
<column name="id"/>
</any>

Атрибут meta-type позволяет приложению задать пользовательский тип, который отображает значения колонок базы данных в персистентные классы, свойства-идентификаторы которых имеют тип, определеный в id-type. Если мета-тип (meta-type) возвращает сущности java.lang.Class, то больше ничего не требуется. В остальных случаях, когда это базовый тип, такой как string или character вы должны определить соответствие значений классам.

<any name="anyEntity" id-type="long" meta-type="string">
<meta-value value="TBL_ANIMAL" class="Animal"/>
<meta-value value="TBL_HUMAN" class="Human"/>
<meta-value value="TBL_ALIEN" class="Alien"/>
<column name="table_name"/>
<column name="id"/>
</any>
<any
name="propertyName"(1)
id-type="idtypename" (2)
meta-type="metatypename" (3)
cascade="none|all|save-update" (4)
access="field|property|ClassName"(5)
>
<meta-value ... />
<meta-value ... />
.....
<column .... />
<column .... />
.....
</any>
(1)

name: Имя свойства.

(2)

id-type: Тип идентификатора.

(3)

meta-type (необязательно - по умолчанию class): тип, который отображает java.lang.Class в одну колонку базы данных либо, в качестве альтернативы, тип, который разрешен для отображения дискриминатора.

(4)

cascade (необязательно - по умолчанию none): тип каскадной операции.

(5)

access (необязательно - по умолчанию property): Стратегия, которую должен использовать Hibernate для доступа к значению свойства.

Старое свойство object, которое занимает отдельное место в Hibernate 1.2, все еще поддерживается, но объявлено полу-устаревшим.

5.3. SQL-идентификаторы в кавычках

Вы можете принудить Hibernate заключать идентификаторы в кавычки в SQL выражениях. Hibernate будет следовать правилам заключения в кавычки согласно установленного диалекта SQL (обычно двойные кавычки, но скобки для SQL Server и обратные кавычки для MySQL).

<class name="LineItem" table="`Line Item`">
<id name="id" column="`Item Id`"/><generator class="assigned"/></id>
<property name="itemNumber" column="`Item #`"/>
...
</class>

5.4. Отдельные файлы отображения

Можно объявлять отображения subclass и joined-subclass в отдельных документах, прямо внутри элемента hibernate-mapping. Это позволяет расширять иерархию классов добавлением нового файла отображения. При таком подходе вы должны указать атрибут extends в отображении подкласса, содержащий имя предварительно замапленного суперкласса. Использование данной возможности делает важным порядок перечисления документов отображений.

<hibernate-mapping>
<subclass name="eg.subclass.DomesticCat" extends="eg.Cat" discriminator-value="D">
 <property name="name" type="string"/>
</subclass>
</hibernate-mapping>