День 2. Маппинг ассоциаций в hibernate

max аватар
83
Находится в разделах:

Предыдущие материалы

И так, мы сделали маппинг для одного класса. Давайте немного расширим его, добавив несколько ассоциаций между классами. Мы добавим людей в приложение и сохраним список событий, в котором они участвуют

Маппинг класса Person

И так,  класс Person выглядит следующим образом:

Создадим для него маппинг в src/main/resources/org/hibernate/tutorial/domain/Person.hbm.xml

И, наконец, добавим новый маппинг в конфигурацию Hibernate:

Теперь создадим ассоциацию между этими двумя сущностями. Люди (Persons) могут участвовать в событиях, а события могут иметь участников. 

Однонаправленная ассоциация, основанная на Set

Если мы добавим коллекцию событий в класс Person, то сможем иметь доступ к событиям для определенного человека без выполнения дополнительных запросов, а через Person#getEvents. Ассоциации с множественными значениями представлены в Hibernate коллекциями из Java Collection Framework; в нашем примере мы выбрали java.util.Set, поскольку коллекция не будет содержать дубликатов, а порядок элементов не критичен для наших примеров:

Перед тем, как задавать маппинг для этой ассоциации, рассмотрим эту связь с другой стороны. Мы можем хранить однонаправленность или создать еще одну коллекцию в классе Event, если захотим иметь возможность навигации по элементам с обоих сторон. Вы всегда можете выполнить явный запрос для получения определенного события.  Для доступа с обоих сторон, выберем ассоциацию many-to-many.  И так, маппинг:

Hibernate поддерживает широкий диапазон коллекций для маппинга, но set - наиболее часто используемый. Для ассоциаций many-to-many необходима таблица ассоциаций. Каждый рядок в такой таблице представляет связь между person и event. Имя таблицы задается с использованием атрибута table в элементе set. Имя колонки с идентификатором для стороны person определяется элементом key, а имя колонки со стороны event определяется атрибутом column элемента many-to-many. Также, необходимо указать хибернейту на класс объектов в коллекции.

Схема базы данных для маппингов выглядит так:

database schema

Работа с ассоциацией

Сейчас мы свяжем немного людей с событиями в новом методе класса EventManager:

После загрузки Person и Event, просто изменяем коллекцию с использованием стандартных методов коллекции. Фактически, мы работаем с объектами без намека на то, что эти операции переводятся в SQL. Как Вы заметили, нет явных вызовов update(), save(). Хибернейт автоматически обнаруживает тот факт. что коллекции были модифицированы и нуждаются в обновлении. Этот механизм называется automatic dirty checking. Мы можем также попробовать его в действии посредством изменения имени или даты в объекте. Пока они в персистентном состоянии, то есть привязаны к определенному объекту сессии org.hibernate.Session, хибернейт наблюдает за всеми изменениями и генерирует SQL. Процесс синхронизации состояния объектов в памяти с базой данных, обычно происходит в момент завершения работы сессии, обычно это называется flusing или сливание. В нашем коде,  сессия заканчивается комитом или откатом транзакции.

 

Мы можем загрузить  person или event различными методами. Можно также модифицировать объект вне org.hibernate.Session, когда он не находится в персистентном состоянии (если он до этого был уже персистентным, тогда его состояние называется detached).  

Посмотрим, как происходит модификация коллекции вне сессии:

Заменим предыдущий метод на вот этот:

Вызов метода update снова переводит объект в состояние персистивности, связывая его с новой единицей работы, поэтому любые модификации, проделанные над объектом, могут быть сохранены в базу данных. 

В этом примере не много пользы, но он показывает важную концепцию, которую можно применить в своем проекте. Закончим наше упражнение добавлением нового действия в main метод класса EventManager и вызовем его из командной строки. 

Если Вам необходимы идентификаторы person и event - то метод save() возвратит их.  Немного поменяем существующий else if в main методе:

также изменим метод createAndStoreEvent, чтобы он возвращал id.

 

Коллекции значений

Добавим коллекцию email адресов в класс Person. Она будет представлена как java.util.Set со значениями java.lang.String:

И добавим соответствующий маппинг в Person.hbm.xml:

Разница по сравнению с предыдущим маппингом состоит в использовании части element, которая сообщает хибернейту о том, что коллекция не содержит ссылок на другие сущности.  Атрибут table в элементе set определяет имя таблицы для коллекции. 

Атрибут column в элементе element определяет имя колонки, в которой будут храниться значения адресов. 

Вот обновленная схема:

updated schema

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

На этот раз, мы не использовали fetch запрос для инициализации коллекции. 

 

Двунаправленные ассоциации

Сейчас добавим маппинг для двунаправленной ассоциации (bi-directional association). Сделаем ассоциацию между person и event для работы с обоих сторон. Схема базы не меняется.

Сперва, добавим коллекцию участников в класс Event:

Теперь добавим маппинг в Event.hbm.xml:

Заметьте, что имена колонок в key и many-to-many поменялись местами в обоих документах. Наиболее важное дополнение здесь - это атрибут inverse="true" в элементе set

Оно сигнализирует о том, что хибернейт должен взять другую сторону, класс Person, когда ему необходимо найти информацию о связях.  Намного проще будет понять этот отстой один раз увидев двунаправленную связь между двумя сущностями.

 

Работа с двунаправленными связями

Во-первых, запомните, что Hibnernate не затрагивает симантику Java. Как мы создаем связь между классами Person  и Event в однонаправленном примере? Мы добавляем объект Event в коллекцию ссылок на события в классе Person. Если мы хотим сделать эту связь двунаправленной, необходимо сделать то же самое на стороне событий, добавив ссылку person на коллекцию в Event. Этот процесс установки связей на обоих сторонах является необходимым при работе с двунаправленными связями.


Конец
Джеймс Рассел Лоуелл более, чем уверен в том, что полезно время от времени ставить знак вопроса на вещах, которые тебе давно представляются несомненными.

хм…ну это памойму уже крайность…

max аватар

Что Вы имеете ввиду?

 

Дружище, когда используешь set в связи один-ко-многим или многие-ко-многим надо перекрывать методы equals и hashCode

max аватар

Благодарю за совет. Вы правы. 

Кстати, hibernate при старте сам выдаст жирный варнинг о том, что equals и hashCode не переопределены. 

Но, согласитесь, это не критично.

Отправить комментарий

CAPTCHA
Чтобы оставить комментарий, введите пожалуйста код, изображенный на картинке
Image CAPTCHA
Введите символы, изображенные на картинке