Hibernate упрощает преобразование наследования

Изучение трех упрощенных (для выполнения) стратегий для отображения классовых иерархий.

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

Xavier Coulon, E-business IT Специалист, IBM Business Consulting Services

Author photoXavier Coulon пришел в IBM France как IT-специалист шесть лет назад и начал с ERP-консультирования по различным платформам. Последние два года работает над крупными J2EE-проектами, включая открытые источники основы, такие как Struts и Hibernate. С ним можно связаться по адресу: xavier.coulon@fr.ibm.com.



Christian Brousseau, J2EE Консультант

Author photoChristian Brousseau, канадец, немногим более десяти лет работающий над программным обеспечением. Начинал с многочисленных Windows-разработок (C++, Visual Basic, Microsoft Foundation Classes, ActiveX), перешел на Java-проекты, когда вышла первая версия. Его компетентность как J2EE-консультанта дала ему отличную возможность переехать во Францию, где он помогал проектировать, разрабатывать и разворачивать множество J2EE-проектов по систематизации предприятий. С ним можно связаться по адресу: cbrous@fr.ibm.com.



14.12.2004

Введение

У Hibernate есть ряд преимуществ перед другими подобными подходами к объектно-реляционному управлению (JDO, компоненты управления данными, внутренняя разработка программ и т.д.): это доступная исходная программа, достигшая высокой степени зрелости, она широко используется и активно обсуждается.

Чтобы интегрировать пакет Hibernate в существующий Java-проект, нужно пройти следующие этапы:

  • Загрузить последнюю версию основы Hibernate из the Hibernate Web site. (см. Resources)
  • Скопировать необходимые Hibernate-библиотеки (JAR-файлы) в свои CLASSPATH приложения.
  • Создать файлы XML-конфигурации, которые будут использоваться для преобразования ваших Java-объектов в таблицы базы данных. (Мы опишем этот процесс в данной статье).
  • Скопировать файлы XML-конфигурации в свои CLASSPATH приложения.

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

Язык запросов пакета Hibernate (HQL)
Hibernate предусматривает язык запросов, который называется Hibernate Query Language, схожий с SQL. Тем из вас, кто предпочитает старые добрые SQL-запросы, Hibernate все еще дает возможность использовать их. Мы в своих примерах будем использовать исключительно HQL.

HQL достаточно прост в обращении. Вы найдете все знакомые ключевые слова, известные вам по SQL, такие как: SELECT, FROM и WHERE. HQL отличается от SQL тем, что вы не пишете запросы прямо в вашей модели данных (т.е. в таблицах, столбцах и т.д.), а в Java-объектах, используя их свойства и взаимосвязи.

Листинг 1 иллюстрирует основной пример. Этот HQL-код восстанавливает все таблицы Individual, где firstName - "John".

Листинг 1. Основной HQL-запрос
SELECT * FROM eg.hibernate.mapping.dataobject.Individual WHERE firstName = "John"

Вы можете обратиться к ссылке HQL на Hibernate Website, если хотите больше узнать о синтаксисе HQL (см. Resources).

Файлы XML-конфигурации
Суть функциональности Hibernate - файлы XML-конфигурации. Эти файлы должны находиться в вашем CLASSPATH приложении. Мы расположили их в справочнике нашего пакета с кодами-образцами (пакет можно загрузить из Resources).

Первый файл, который мы рассмотрим - это hibernate.ctg.xml. Он содержит информацию, соответствующую вашему источнику данных (DB URL, название схемы, имя пользователя, пароль и т.д.), и отсылает к другим файлам конфигурации, которые будут содержать необходимую вам информацию о преобразовании.

Сохраненные XML-файлы позволяют преобразовывать Java-классы независимо от таблиц базы данных. Далее мы подробнее рассмотрим эти файлы, но сейчас важно отметить, что названия файлов создаются по образцу ClassName.hbm.xml.


Наглядный пример

В этой статье мы рассмотрим основной пример, который показывает, как Hibernate работает и претворяет в жизнь три различные стратегии, с помощью которых вы можете использовать Hibernate для объектно-реляционного преобразования. Наше пробное приложение будет использоваться страховой компанией, которая должна сохранить легальные записи с авторскими правами, что и гарантируется покупателям. Мы предоставляем полный исходный текст программы для этой статьи (см. Resources); эта исходная программа предусматривает основные функции, с помощью которых можно построить законченное приложение, как, например, Web- или Swing-приложения.

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

В нашем приложении авторские права представлены классом Right. Right может быть либо Lease (аренда), либо Property (собственность). Right - собственность покупателя. Для обозначения нашего покупателя мы используем обобщенный класс Person. Person может быть либо Individual (личный), либо Corporation (корпоративный). Конечно же, страховая компания должна знать Estate (собственность), к которой эти Rights приписываются. Estate - очень обобщенный элемент, согласитесь. Поэтому мы будем использовать классы Land (земля) и Building (здание), чтобы предоставить нашим разработчикам более понятные термины для работы.

Опираясь на этот абзац, можно разработать модель класса, изображенную на Схеме 1:

Схема 1. Полная модель класса
Схема 1. Полная модель класса

Наша модель базы данных была смоделирована, чтобы описать три различные стратегии, которые мы обсудим в этой статье. Для иерархии Right мы используем одну таблицу (TB_RIGHT) и преобразуем в нужный класс, используя столбец DISCRIMINATOR. Для иерархии Person мы используем так называемую супер-таблицу (TB_PERSON), которая имеет те же ID, что и две другие таблицы (TB_CORPORATION и TB_INDIVIDUAL). Третья иерархия (Estate) использует две различные таблицы (TB_BUILDING и ТB_LAND), связанные внешним ключом, определяемым сочетанием двух столбцов (REF_ESTATE_ID и REF_ESTATE_TYPE).

Схема 2 показывает модель базы данных:

Схема 2. Полная модель базы данных
Схема 2. Полная модель базы данных

Службы интеграции

Установка базы данных
Hibernate поддерживает большое разнообразие RDBMS, каждый из которых может работать по нашему образцу. Тем не менее, код-образец и текст этой статьи приспособлены к HSQLDB (см. Resources), полнофункциональной реляционной базе данных, полностью написанной на языке Java. В SQL-справочнике вы найдете файл под названием datamodel.sql. Этот SQL-текст создает модель данных, используемых в нашем образце.

Установка проекта Java.
Также всегда можно построить и выполнить код-образец, используя командную строку; можно рассмотреть возможность установки проекта в IDE для лучшей интеграции. В рамках пакета кода-образца, вы обнаружите следующие указатели:

  • сonfig, который содержит файлы XML-конфигураций всех образцов (преобразование, Log4J и т.д.)
  • data, который содержит файлы конфигураций, используемые HSQLDB. Также можно встретить командный файл под названием startHSQLDB.bat, который можно использовать для запуска базы данных.
  • src, содержит исходный текст всех образцов.

Удостоверьтесь, что скопированы необходимые библиотеки Java и файлы XML-конфигурации в ваш CLASSPATH приложения. Коду требуется только Hibernate- и HSQLDB-библиотеки для правильного транслирования и выполнения. Вы можете загрузить эти пакеты из секции Resources.


Стратегия 1: Одна таблица на подкласс (Persons)

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

Сейчас нам нужно преобразовать нашу объектную иерархию. Мы имеем три таблицы (TB_PERSON, TB_INDIVIDUAL и TB_CORPORATION). Как мы отметили выше, у них у всех есть столбец с именем ID в качестве первоначального ключа. Необязательно иметь общий столбец с таким названием, но это считается хорошей практикой - а также значительно облегчает чтение генерированных SQL-запросов.

В файле XML-преобразования, показанном в Листинге 2, вы можете заметить, что два конкретных класса определены как <joined-subclass> внутри преобразовательного определения Person. Элемент XML <id> преобразован в первоначальный ключ для таблицы верхнего уровня TB_PERSON, в то время как элементы <key> (из каждого подкласса) преобразованы в соответствующие первоначальные ключи таблиц TB_INDIVIDUAL и TB_CORPORATION.

Листинг 2. Person.hbm.xml
<?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>
  <class name="eg.hibernate.mapping.dataobject.Person"
   table="TB_PERSON" polymorphism="implicit">
    <id name="id" column="ID">
      <generator class="assigned"/>
    </id>
    <set name="rights" lazy="false">
     <key column="REF_PERSON_ID"/>
      <one-to-many class="eg.hibernate.mapping.dataobject.Right" />
    </set>
    <joined-subclass name="eg.hibernate.mapping.
    dataobject.Individual" table="TB_INDIVIDUAL">
      <key column="id"/>
      <property name="firstName" column=
      "FIRST_NAME" type="java.lang.String" />
      <property name="lastName" column=
      "LAST_NAME" type="java.lang.String" />
    </joined-subclass>
    <joined-subclass name=
    "eg.hibernate.mapping.dataobject.Corporation" table="TB_CORPORATION">
      <key column="id"/>
      <property name=
      "name" column="NAME" type="string" />
      <property name="registrationNumber" column
      ="REGISTRATION_NUMBER" type="string" />
    </joined-subclass>
  </class>
</hibernate-mapping>

Сохраняя новое свойство формы Individual, наш Java-код с Hibernate остается довольно простым, как показано в Листинге 3:

Листинг 3. Сохранение нового свойства таблицы Individual
public Object create(Object object) {
  Session session = null;
  try {
    session = sessionFactory.openSession();
    Transaction tx = session.beginTransaction();
    session.save(object);
    session.flush();
    tx.commit();
    ...
}

Hibernate в свою очередь генерирует два SQL-требования INSERT, показанных в Листинге 4. Это два требования только для одного save( ).

Листинг 4. SQL-запросы вставок
insert into TB_PERSON (ID) values (?)
insert into TB_INDIVIDUAL (FIRST_NAME, LAST_NAME, id) values (?, ?, ?)

Для доступа таблицы Individual из базы данных нужно просто определить имя класса в вашем HQL-запросе, как показано в Листинге 5.

Листинг 5. Активизация HQL-запроса
public Person findIndividual(Integer id) {
  ...
  session.find("select p from " +
   Individual.class.getName() + " as p where p.id = ?",
    new Object[] { id },
    new Type[] { Hibernate.INTEGER });	
  ...

Hibernate автоматически выполнит SQL JOIN для восстановления всей необходимой информации из обеих таблиц, как показано в Листинге 6:

Листинг 6. SQL-запрос SELECT для Individual
select individual0_.id as ID, 
individual0_.FIRST_NAME as FIRST_NAME55_, 
  individual0_.LAST_NAME as LAST_NAME55_ 
  from TB_INDIVIDUAL individual0_ 
  inner join TB_PERSON 
  individual0__1_ on individual0_.id=individual0__1_.ID 
  where (individual0_.id=? )

Запрос абстрактных классов

Hibernate выводит автоматически всю группу классов, когда запрашивается абстрактный класс. Например, если мы запрашиваем таблицу Person из базы данных, Hibernate выводит список объектов таблиц Individual и Corporation.

Однако когда не уточнен абстрактный класс, пакету Hibernate необходимо выполнить SQL JOIN, т.к. неизвестно, через какую таблицу нужно пройти. Вместе с прочими столбцами восстановленной таблицы, выведенной из HQL-запроса, будет также восстановлен и дополнительный динамический столбец. Столбец clazz используется пакетом Hibernate для определения и размещения восстановленного объекта. Мы называем это классовое распределение динамическим в противоположность тому методу, который мы будем использовать в нашей второй стратегии.

Листинг 7 показывает как запрашивается Person через id, в то время как Листинг 8 демонстрирует SQL-запрос, автоматически генерируемый Hibernate, включая перекрещивание таблиц.

Листинг 7. HQL-запрос в поисковом методе вызова
public Person find(Integer id) {
  ...
  session.find("select p from " 
  + Person.class.getName() + " as p where p.id = ?",
    new Object[] { id },
    new Type[] { Hibernate.INTEGER });	
  ...
}
Листинг 8. SQL-запрос SELECT для любого типа в таблице Person
select person0_.ID as ID0_,
  casewhen(person0__1_.id is not null, 1,  
  casewhen(person0__2_.id is not null, 2,  
  casewhen(person0_.ID is not null, 0, -1))) as clazz_0_, 
  person0__1_.FIRST_NAME as FIRST_NAME61_0_, 
  person0__1_.LAST_NAME as LAST_NAME61_0_, 
  person0__2_.NAME as NAME62_0_, 
  person0__2_.REGISTRATION_NUMBER as REGISTRA3_62_0_ 
  from TB_PERSON person0_ 
  left outer join TB_INDIVIDUAL person0__1_ on person0_.ID=person0__1_.id 
  left outer join TB_CORPORATION person0__2_ on person0_.ID=person0__2_.id 
  where person0_.ID=?

Стратегия 2: Одна таблица на классовую иерархию (Rights)

Для нашей иерархии Right мы используем одну таблицу (TB_RIGHT) для сохранения полной классовой иерархии. Вы заметите, что таблица TB_RIGHT имеет все необходимые столбцы для хранения всех свойств классовой иерархии таблицы Right. Значения сохраненных свойств будут затем сохранены в таблице, каждый неиспользованный столбец будет заполнен нулевым значением. (Т. к. в таблице много "дырок", мы часто называем ее "Швейцарский сыр").

В Схеме 3 вы увидите, что таблица TB_RIGHT включает дополнительный столбец для автоматического определения соответствующего класса и распределения согласно классу. Этот столбец преобразован с использованием XML-элемента <discriminator> из наших файлов преобразования.

Схема 3. Содержание таблицы TB_RIGHT
Схема 3. Содержание таблицы TB_RIGHT

Верх простоты

В очень больших проектах вы столкнетесь со сложной классовой иерархией, состоящей из нескольких уровней абстрактных классов. К счастью, вам не придется уточнять дискриминатор-значение (discriminator-value) для абстрактного класса. Вам всего лишь нужно определить его для конкретных классов, которые будет использовать Hibernate.

Так же как в файле преобразования Person в Листинге 2, так и в Листинге 9, мы преобразуем абстрактный класс (Right) и все его свойства. Для преобразования наших двух конкретных классов (Lease и Property) мы используем XML-признак <subclass>. Этот признак довольно прост; он требует свойство name, так же как признак class требует свойство discriminator-value. Hibernate использует последнее свойство для определения класса, с которым он должен работать.

Как вы заметили из диаграммы класса на Схеме 1, discriminator не является свойством ни одного класса Java. На самом деле он даже не преобразован. Это всего лишь технический столбец, помещенный между Hibernate и базой данных.

Листинг 9. Right.hbm.xml
<?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>
  <class name="eg.hibernate.mapping.
  dataobject.Right" table="TB_RIGHT" polymorphism="implicit">
    <id name="id" column="ID">
      <generator class="assigned"/>
    </id>
    <discriminator>
      <column name="DISCRIMINATOR"/>
    </discriminator>
    <property name="date" column="DATE"
     type="java.sql.Date" />
    <many-to-one name="person" class=
    "eg.hibernate.mapping.dataobject.Person" 
    column="REF_PERSON_ID"/>
    <any name="estate"
         meta-type="string"
         id-type="java.lang.Integer">
      <meta-value value="LND" class=
      "eg.hibernate.mapping.dataobject.Land"/>
      <meta-value value="BLD" class=
      "eg.hibernate.mapping.dataobject.Building"/>
      <column name="REF_ESTATE_TYPE"/>
      <column name="REF_ESTATE_ID"/>         
    </any>
    
    <subclass name="eg.hibernate.mapping.dataobject.
    Property" discriminator-value="PRO"/>
    
    <subclass name="eg.hibernate.mapping.
    dataobject.Lease" discriminator-value="LEA">
      <property name="duration" column=
      "DURATION" type="java.lang.Integer" />
     </subclass>
  </class>
</hibernate-mapping>

В файле преобразования Листинга 9 вы видите отношения "множество-единичность" между иерархиями Right и Person, которые (естественно) противоположны отношениям в иерархии Person ("единичность-множественность"). Обратите внимание также на отношения между иерархиями Right и Estate; позднее в этой статье мы рассмотрим и эти отношения.

Как и в первой стратегии, здесь Hibernate производить довольно эффективные SQL-операторы при доступе к базе данных. Когда мы запрашиваем конкретный класс, как показано в Листинге 10, Hibernate автоматически фильтрует с помощью средств discriminatorа - хорошее качество, т.к. это означает, что Hibernate читает только соответствующие указанному файлу столбцы.

Листинг 10. SQL-запрос конкретного класса
select property0_.ID as ID, property0_.DATE as DATE, 
	property0_.REF_PERSON_ID as 
	REF_PERS4_, property0_.
	REF_ESTATE_TYPE as REF_ESTA5_, 
	property0_.REF_ESTATE_ID as REF_ESTA6_ 
	from TB_RIGHT property0_ 
	where property0_.DISCRIMINATOR='PRO'

Все гораздо сложнее, когда мы запрашиваем абстрактный класс. Потому что Hibernate не знает, какой специфический класс необходим, ему приходится считывать каждый столбец (включая столбец дискриминатора), затем он определяет, какой класс нужно вычленить и, в конце концов, показывает его. Дискриминатор играет ту же роль, что и столбец clazz в нашей первой стратегии. Но это соответствие оказывается более статичным, в то время как имя класса произведено прямо от значения дискриминатора.

Листинг 11. SQL-запрос (абстрактных) классов Right
select right0_.ID as ID, 
	right0_.DISCRIMINATOR as DISCRIMI2_, 
	right0_.DATE as DATE, right0_.
	REF_PERSON_ID as REF_PERS4_, 
	right0_.REF_ESTATE_TYPE as
	 REF_ESTA5_, right0_.REF_ESTATE_ID as REF_ESTA6_, 
	right0_.DURATION as DURATION from TB_RIGHT right0_

Несовместимость стратегий

Как определено DTD (определение типа документа) преобразования Hibernate, первые две стратегии, описанные в данной статье, взаимоисключают друг друга. Это означает, что они не могут комбинироваться при преобразовании одной и той же иерархии.

Есть одно обстоятельство, касающееся этих двух стратегий: для того, чтобы они работали, нужно "обнулить" все незадействованные столбцы. Полученная таблица может оказаться сложной для работы, в то время как разработчики, как правило, полагаются на ограничение целостности в базе данных. (В конце концов, Lease со значением, сведенным к нулю, не вносит никакого смысла!)

Решением может быть использование ограничения целостности проверки уровня базы данных. В зависимости от свойств дискриминатора вы можете определять набор правил для выполнения, как показано в Листинге 12. Конечно, структура вашей базы данных должна поддерживать этот признак. Более того, т.к. это ограничение целостности должно быть выражено в одном виде для всех конкретных классов, в то же время с разрастанием иерархии его будет трудно поддерживать.

Листинг 12. Ограничение целостности базы данных
alter table TB_RIGHT 
	add constraint CHK_RIGHT check(	
		(discriminant ='DPP' 
		and date is null and duration is null)
	or	(discriminant ='DLM' and
	date is not null and duration is not null));

Стратегия 3: Одна таблица на конкретный класс (Estates)

Наша третья и последняя стратегия, возможно, самая изощренная из всех: одна таблица на конкретный класс и ни одной для абстрактного суперкласса Estate. Воспользуемся Hibernate для осуществления поддержки полиморфизма. В XML-файле преобразования в Листинге 13 вы увидите, что только два наших конкретных класса преобразованы (Building и Land):

Листинг 13. Estate.hbm.xml
<?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>
  <class name="eg.hibernate.mapping.
  dataobject.Land" table=
  "TB_LAND" polymorphism="implicit">
    <id name="id" column="ID">
      <generator class="assigned"/>
    </id>
    <property name="description" 
    column="DESCRIPTION" type="java.lang.String" />
    <property name="squareFeet" 
    column="SQUARE_FEET" type="java.lang.Double"/>
  </class>

  <class name="eg.hibernate.
  mapping.dataobject.Building" 
  table="TB_BUILDING" polymorphism="implicit">
    <id name="id" column="ID">
      <generator class="assigned"/>
    </id>
    <property name="
    description" column="DESCRIPTION" type="java.lang.String" />
    <property name="
    address" column="ADDRESS" type="java.lang.String"/>
  </class>
</hibernate-mapping>

Разделение ID-свойств между таблицами

Важно, чтобы не было идентичных ID-свойств, распределенных между двумя таблицами, которые преобразованы внутри одной и той же классовой иерархии. Если такое случается, Hibernate выводит несколько разных объектов на один и тот же ID. Это может оказаться проблемным для Hibernate - впрочем, как и для вас.

Если вы посмотрите на файл преобразования в Листинге 13, ваша первая реакция будет такой: "Это преобразование ничем не отличается от тех, которые я использую каждый день. Ничего особенного!" И вы будете правы. На самом деле для нашей третьей стратегии требуется только одно условие: нам нужно особенным образом установить свойства полиморфизма в имплицит.

Даже несмотря на то, что класс Estate невозможно найти нигде найти в вашем файле преобразования, он все еще существует в нашей классовой иерархии. А т.к. два наших преобразованных класса (Building и Land) производны от Estate, мы можем использовать этот абстрактный суперкласс в наших HQL-запросах, как показано в Листинге 14. Hibernate будет использовать интроспекцию для определения классов, расширяющих этот абстрактный класс так, что он может успешно выполнять соответствующие для каждого суперкласса SQL-запросы.

Листинг 14. HQL-запрос в поисковом методе вызова
public Estate find(Integer id) {
  ...
  List objects =
    session.find(
      "select e from " + Estate.class.getName() + " as e where e.id = ?",
        new Object[] { id },
        new Type[] { Hibernate.INTEGER });
  ...
}

Для обнаружения Estates с соответствующими ID, Hibernate нужно предоставить два запроса базе данных (Листинг 15).

Листинг 15. SQL-запросы
select land0_.ID as ID, land0_.DESCRIPTION as 
DESCRIPT2_, land0_.SQUARE_FEET as SQUARE_F3_ 
from TB_LAND land0_ where (land0_.ID=? )

select building0_.ID as ID, building0_.
DESCRIPTION as DESCRIPT2_, building0_.ADDRESS as ADDRESS 
from TB_BUILDING building0_ where (building0_.ID=? )

Как мы видели во второй стратегии, между классами Right и Estate существует отношение "множество-единичность". Это переводится примерно так: "Один Estate может относиться к многим Rights. Но каждый Right может ссылаться только на один Estate". В свете модели нашей базы данных не существует уникальной таблицы, которую мы можем использовать для создания ограничения внешнего ключа, как между TB_RIGHT и TB_PERSON. Для нас является практически невозможным создание внешнего ключа. К счастью, Hibernate обеспечивает нас очень мощным элементом XML-преобразования - признаком <any>, использование которого демонстрируется в Листинг16.

Листинг 16. XML-преобразование разных взаимоотношений
<any name="estate"
	meta-type="string"
	id-type="java.lang.Integer">
  <meta-value value="LND"
   class="eg.hibernate.mapping.dataobject.Land"/>
  <meta-value value="BLD" 
  class="eg.hibernate.mapping.dataobject.Building"/>
  <column name="REF_ESTATE_TYPE"/>
  <column name="REF_ESTATE_ID"/>          
</any>
<any name="estate"
	meta-type="string"
	id-type="java.lang.Integer">
  <meta-value value="LND" 
  class="eg.hibernate.mapping.dataobject.Land"/>
  <meta-value value="BLD" 
  class="eg.hibernate.mapping.dataobject.Building"/>
  <column name="REF_ESTATE_TYPE"/>
  <column name="REF_ESTATE_ID"/>         
</any>

Блокированный полиморфизм

Классы, полиморфическре обеспечение которых заблокировано (<class...polymorphism="explicit"...>), исключаются из нацеливания запросов любого из суперклассов.

Рассмотрим ближе эту новую часть преобразования. Наш виртуальный внешний код основан на двух столбцах из таблицы TB_RIGHT. Первый (REF_ESTATE_TYPE) содержит строку дискриминатора, которая будет использована для преобразования имени соответствующего класса. Второй (REF_ESTATE_ID) - это имя столбца из первичного ключа другой таблицы. Используя установки по умолчанию, Hibernate попытается сохранить преобразованные имена класса в первом столбце, который может оказаться неэффективным, но занимать при этом место в таблице (особенно если имена класса изменяются во время обновления кода). К счастью, Hibernate обеспечивает сообщение имен класса константам строки, используя XML-элемент <meta-value>. Эти константы служат той же цели, что и дискриминаторы, которые мы обсуждаем во второй стратегии. Опять же, этот признак включает только Hibernate и базу данных, и это не изменяет классовую иерархию.

Интеграция модели базы данных
Также стандартный SQL не допускает ограничителей ссылок одновременно с многочисленными таблицами в данный столбец; возможно присоединение процедуры, которая проверяет наличие данных в конечной таблице, получившей свойство дискриминатора, которое он считывает. Тем не менее, такой метод осуществления интеграции может оказаться очень сложным в применении и также сократить участие всей базы данных.

Полиморфизм

Нужно учитывать одну вещь при использовании встроенного полиморфизма Hibernate: вы можете по неосторожности вернуть гораздо больше информации; объясняется это тем, что все классы преобразованы признаком полиморфизма, установленным в имплицит. Листинг 17 иллюстрирует способ возвращения всей базы данных с использованием HQL-запроса, состоящего из двух слов.

Листинг 17. HQL-запрос
public List all() {
  ...
  List objects = session.find("from Object");
  ...
}

Довольно мощно, вам не кажется? Конечно, не многим из вас понадобится (или захочется, по крайней мере) возвращать всю базу данных одним HQL-запросом. Цель этого примера (нонсенс) лишь в том, чтобы показать возможности имплицитного полиморфизма. Вы можете использовать эти возможности, чтобы предотвратить бесполезное заполнение нужных ресурсов при отправке SQL-запросов в базу данных.

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

  • С помощью нашей первой стратегии (одна таблица на подкласс) Hibernate считывает многочисленные таблицы каждый раз, когда объект определяется и размещается. Эта операция может дать хорошие результаты, если ваши индексы четко определены, а иерархия не слишком разветвлена. Если, однако, это не так, вы можете столкнуться с проблемами в общем выполнении задачи.
  • Для второй стратегии (одна таблица на классовую иерархию) вам нужно четко определить целостность данных, используя проверяющие ограничители. Могут возникнуть трудности в использовании этой стратегии с постепенным увеличением количества столбцов. С другой стороны, вы в праве не использовать такие ограничители совсем, а позволить своему коду приложения выполнить собственную интеграцию базы данных.
  • Наша третья стратегия (одна таблица на конкретный класс) имеет некоторые ограничения в преобразовании; выделение модели данных не может использовать референтальную интеграцию; это значит, что вы не включили механизм реляционной базы данных в полную силу. "Плюс" то, что эта стратегия может свободно соединяться с двумя другими.

Независимо от того, какую стратегию вы выбираете, всегда помните, что вам не нужно удалять в процессе ваши Java-классы; это значит, что нет абсолютно никакой связи между вашими рабочими объектами и устойчивой основой. Это та степень гибкости, которая делает Hibernate таким популярным в объектно-реляционных проектах Java.


Загрузка

ОписаниеИмяРазмер
Образец кодаj-hibernate-source.zip---

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Технология Java
ArticleID=96477
ArticleTitle=Hibernate упрощает преобразование наследования
publish-date=12142004