Перейти к основному содержимому

Учебное пособие по аннотациям Hibernate One to Many

· 7 мин. чтения

1. Введение

В этом кратком руководстве по Hibernate мы рассмотрим пример сопоставления « один ко многим » с использованием аннотаций JPA, альтернативы XML.

Мы также узнаем, что такое двунаправленные отношения, как они могут создавать несоответствия и как может помочь идея владения.

2. Описание

Проще говоря, сопоставление « один ко многим » означает, что одна строка в таблице сопоставляется с несколькими строками в другой таблице. ``

Давайте посмотрим на следующую диаграмму отношений объектов, чтобы увидеть ассоциацию « один ко многим »:

./1dd5a06c9ea44e0590913124e87e3e16.png

В этом примере мы реализуем систему корзин, в которой у нас есть таблица для каждой корзины и другая таблица для каждого товара. В одной корзине может быть много товаров, поэтому здесь у нас есть отображение « один ко многим ».

На уровне базы данных это работает так: у нас есть cart_id в качестве первичного ключа в таблице cart , а также cart_id в качестве внешнего ключа в items .

Мы делаем это в коде с помощью @OneToMany .

Давайте сопоставим класс Cart с коллекцией объектов Item таким образом, чтобы отразить взаимосвязь в базе данных:

public class Cart {

//...

@OneToMany(mappedBy="cart")
private Set<Item> items;

//...
}

Мы также можем добавить ссылку на корзину в каждом элементе , используя @ManyToOne , что сделает эту связь двунаправленной . Двунаправленность означает, что мы можем получить доступ к товарам из корзин , а также к тележкам из товаров . [](https://docs.jboss.org/hibernate/orm/4.1/manual/en-US/html/ch07.html#collections-bidirectional) ** ``**

Свойство mappedBy — это то, что мы используем, чтобы сообщить Hibernate, какую переменную мы используем для представления родительского класса в нашем дочернем классе.

Следующие технологии и библиотеки используются для разработки примера приложения Hibernate, реализующего ассоциацию « один ко многим »:

  • JDK 1.8 или новее
  • Спящий режим 5
  • Maven 3 или более поздней версии
  • База данных Н2

3. Настройка

3.1. Настройка базы данных

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

3.2. Зависимости Maven

Начнем с добавления зависимостей драйверов Hibernate и H2 в наш файл pom.xml . Зависимость Hibernate использует ведение журнала JBoss и автоматически добавляется в качестве транзитивных зависимостей:

  • Гибернация версии 5.6.7.Финальная
  • Драйвер H2 версии 2.1.212

Пожалуйста, посетите центральный репозиторий Maven для получения последних версий Hibernate и зависимостей H2 .

3.3. Hibernate SessionFactory

Далее давайте создадим Hibernate SessionFactory для нашего взаимодействия с базой данных:

public static SessionFactory getSessionFactory() {

ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder()
.applySettings(dbSettings())
.build();

Metadata metadata = new MetadataSources(serviceRegistry)
.addAnnotatedClass(Cart.class)
// other domain classes
.buildMetadata();

return metadata.buildSessionFactory();
}

private static Map<String, String> dbSettings() {
// return Hibernate settings
}

4. Модели

Конфигурации, связанные с сопоставлением, будут выполняться с использованием аннотаций JPA в классах моделей:

@Entity
@Table(name="CART")
public class Cart {

//...

@OneToMany(mappedBy="cart")
private Set<Item> items;

// getters and setters
}

Обратите внимание, что аннотация @OneToMany используется для определения свойства в классе Item , которое будет использоваться для сопоставления переменной mappedBy . Вот почему у нас есть свойство с именем « cart » в классе Item :

@Entity
@Table(name="ITEMS")
public class Item {

//...
@ManyToOne
@JoinColumn(name="cart_id", nullable=false)
private Cart cart;

public Item() {}

// getters and setters
}

Также важно отметить, что аннотация @ManyToOne связана с переменной класса Cart . Аннотация @JoinColumn ссылается на сопоставленный столбец.

5. В действии

В тестовой программе мы создаем класс с методом main () для получения Hibernate Session и сохраняем объекты модели в базу данных, реализуя ассоциацию « один ко многим »:

sessionFactory = HibernateAnnotationUtil.getSessionFactory();
session = sessionFactory.getCurrentSession();
System.out.println("Session created");

tx = session.beginTransaction();

session.save(cart);
session.save(item1);
session.save(item2);

tx.commit();
System.out.println("Cart ID=" + cart.getId());
System.out.println("item1 ID=" + item1.getId()
+ ", Foreign Key Cart ID=" + item.getCart().getId());
System.out.println("item2 ID=" + item2.getId()
+ ", Foreign Key Cart ID=" + item.getCart().getId());

Это результат нашей тестовой программы:

Session created
Hibernate: insert into CART values ()
Hibernate: insert into ITEMS (cart_id)
values (?)
Hibernate: insert into ITEMS (cart_id)
values (?)
Cart ID=7
item1 ID=11, Foreign Key Cart ID=7
item2 ID=12, Foreign Key Cart ID=7
Closing SessionFactory

6. Аннотация @ManyToOne

Как мы видели в разделе 2, мы можем указать отношение « многие к одному », используя аннотацию @ManyToOne . Сопоставление « многие к одному » означает, что многие экземпляры этой сущности сопоставляются с одним экземпляром другой сущности — много товаров в одной корзине .

Аннотация @ManyToOne также позволяет нам создавать двунаправленные отношения. Мы подробно рассмотрим это в следующих нескольких подразделах.

6.1. Несоответствия и право собственности

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

Однако в нашем случае связь является двунаправленной, что может привести к несогласованности.

Давайте представим ситуацию, когда разработчик хочет добавить item1 в экземпляр cart1 и item2 в экземпляр cart2 , но совершает ошибку, из-за которой ссылки между cart2 и item2 становятся несогласованными:

Cart cart1 = new Cart();
Cart cart2 = new Cart();

Item item1 = new Item(cart1);
Item item2 = new Item(cart2);
Set<Item> itemsSet = new HashSet<Item>();
itemsSet.add(item1);
itemsSet.add(item2);
cart1.setItems(itemsSet); // wrong!

Как показано выше, item2 ссылается на корзину2, тогда как cart2 не ссылается на item2, и это плохо .

Как Hibernate должен сохранить item2 в базу данных? Будет ли внешний ключ item2 ссылаться на корзину1 или корзину2 ?

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

6.2. Предмет как сторона-владелец

Как указано в спецификации JPA в разделе 2.9, рекомендуется помечать сторону « многие к одному » как сторону-владельца.

Другими словами, I tem будет стороной-владельцем, а Cart — обратной стороной, что мы и сделали ранее.

Так как же мы этого добились?

Включив атрибут mappedBy в класс Cart , мы помечаем его как обратную сторону.

В то же время мы также аннотируем Item. поле cart с @ManyToOne , что делает Item стороной-владельцем.

Возвращаясь к нашему примеру с «несогласованностью», теперь Hibernate знает, что ссылка на item2 важнее, и сохранит ссылку на item2 в базе данных.

Проверяем результат:

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=2

Хотя в нашем фрагменте корзина ссылается на item2 , ссылка item2 на корзину2 сохраняется в базе данных.

6.3. Корзина как сторона-владелец

Также можно пометить сторону « один ко многим » как сторону-владельца, а сторону « многие к одному » — как обратную сторону.

Хотя это не рекомендуется, давайте попробуем.

В приведенном ниже фрагменте кода показана реализация стороны « один ко многим » в качестве стороны-владельца:

public class ItemOIO {

// ...
@ManyToOne
@JoinColumn(name = "cart_id", insertable = false, updatable = false)
private CartOIO cart;
//..
}

public class CartOIO {

//..
@OneToMany
@JoinColumn(name = "cart_id") // we need to duplicate the physical information
private Set<ItemOIO> items;
//..
}

Обратите внимание, как мы удалили элемент mappedBy и установили @JoinColumn « многие к одному» как вставляемое и обновляемое значение false . ``

Если мы запустим тот же код, результат будет противоположным:

item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=1

Как показано выше, теперь item2 принадлежит корзине.

7. Заключение

Мы видели, как легко реализовать связь « один ко многим » с Hibernate ORM и базой данных H2, используя аннотации JPA.

Кроме того, мы узнали о двунаправленных отношениях и о том, как реализовать понятие стороны-владельца.

Исходный код в этой статье можно найти на GitHub .