1. введение
В этом кратком учебнике по гибернации мы рассмотрим пример сопоставления один ко многим с использованием аннотаций JPA, альтернативного XML.
Мы также узнаем, что такое двунаправленные отношения, как они могут создавать несоответствия и как идея собственности может помочь.
Дальнейшее чтение:
Весенняя загрузка с гибернацией
Обзор идентификаторов в Hibernate/JPA
2. Описание
Проще говоря, сопоставление один ко многим означает, что одна строка в таблице сопоставляется с несколькими строками в другой таблице.
Давайте посмотрим на следующую диаграмму отношений сущностей, чтобы увидеть ассоциацию один ко многим :
В этом примере мы реализуем систему корзины, в которой у нас есть таблица для каждой корзины и еще одна таблица для каждого товара. В одной корзине может быть много товаров, поэтому здесь у нас есть сопоставление один ко многим .
Как это работает на уровне базы данных, у нас есть cart_id в качестве первичного ключа в таблице cart , а также cart_id в качестве внешнего ключа в items .
Мы делаем это в коде с помощью @OneToMany .
Давайте сопоставим класс Cart с объектом Items таким образом, чтобы он отражал отношения в базе данных:
public class Cart { //... @OneToMany(mappedBy="cart") private Setitems; //... }
Мы также можем добавить ссылку на Корзину в Элементы используя @ManyToOne , делая это двунаправленным отношением. Двунаправленный означает, что мы можем получить доступ к предметам из тележек , а также тележкам из предметов .
Свойство mappedBy – это то, что мы используем, чтобы сообщить Hibernate, какую переменную мы используем для представления родительского класса в нашем дочернем классе.
Следующие технологии и библиотеки используются для разработки примера приложения Hibernate, реализующего ассоциацию один ко многим :
- JDK 1.8 или более поздняя версия
- Спящий режим 5
- Maven 3 или более поздняя версия
- База данных H2
3. Настройка
3.1. Настройка базы данных
Ниже приведен наш скрипт базы данных для таблиц Cart и Items . Мы используем ограничение внешнего ключа для сопоставления один ко многим :
CREATE TABLE `Cart` ( `cart_id` int(11) unsigned NOT NULL AUTO_INCREMENT, PRIMARY KEY (`cart_id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; CREATE TABLE `Items` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `cart_id` int(11) unsigned NOT NULL, PRIMARY KEY (`id`), KEY `cart_id` (`cart_id`), CONSTRAINT `items_ibfk_1` FOREIGN KEY (`cart_id`) REFERENCES `Cart` (`cart_id`) ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8;
Наша настройка базы данных готова, поэтому давайте перейдем к созданию примера проекта Hibernate.
3.2. Зависимости Maven
Затем мы добавим зависимости драйверов Hibernate и H2 в ваш pom.xml файл. Зависимость Hibernate использует ведение журнала JBoss, и она автоматически добавляется в качестве транзитивных зависимостей:
- Гибернация версии 5 .2.7.Финал
- Драйвер H2 версии 1 .4.197
Пожалуйста, посетите центральный репозиторий Maven для получения последних версий Hibernate и зависимостей H2 .
3.3. Настройка режима гибернации
Вот конфигурация гибернации:
org.h2.Driver jdbc:h2:mem:spring_hibernate_one_to_many sa org.hibernate.dialect.H2Dialect thread true
3.4. Класс Util аннотации Hibernate
С помощью класса Hibernate Annotation Util нам просто нужно сослаться на новый файл конфигурации Hibernate:
private static SessionFactory sessionFactory; private SessionFactory buildSessionFactory() { ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder(). configure("hibernate-annotation.cfg.xml").build(); Metadata metadata = new MetadataSources(serviceRegistry).getMetadataBuilder().build(); SessionFactory sessionFactory = metadata.getSessionFactoryBuilder().build(); return sessionFactory; } public SessionFactory getSessionFactory() { if(sessionFactory == null) sessionFactory = buildSessionFactory(); return sessionFactory; }
4. Модели
Конфигурации, связанные с отображением, будут выполняться с использованием аннотаций JPA в классах моделей:
@Entity @Table(name="CART") public class Cart { //... @OneToMany(mappedBy="cart") private Setitems; // getters and setters }
Обратите внимание, что аннотация @OneToMany используется для определения свойства в Элементы класса, которые будут использоваться для сопоставления переменной mappedBy . Вот почему у нас есть свойство с именем ” cart ” в классе Items :
@Entity @Table(name="ITEMS") public class Items { //... @ManyToOne @JoinColumn(name="cart_id", nullable=false) private Cart cart; public Items() {} // getters and setters }
Также важно отметить, что аннотация @ManyToOne связана с переменной класса Cart . @JoinColumn аннотация ссылается на сопоставленный столбец.
5. В действии
В тестовой программе мы создаем класс с методом main () для получения сеанса гибернации и сохранения объектов модели в базе данных, реализующей ассоциацию один ко многим :
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. Несоответствия и Право собственности
Теперь, если Корзина ссылка Пункты , но Элементы в свою очередь не ссылались на диаграмму , наши отношения были бы однонаправленными . Объекты также будут иметь естественную согласованность.
В нашем случае, однако, отношения являются двунаправленными, что приводит к возможности несогласованности.
Давайте представим ситуацию , когда разработчик хочет добавить item1 в корзину и пункт 2 в корзину 2 , но делает ошибку, так что ссылки между cart2 и item2 становятся несовместимыми:
Cart cart1 = new Cart(); Cart cart2 = new Cart(); Items item1 = new Items(cart1); Items item2 = new Items(cart2); SetitemsSet = new HashSet (); itemsSet.add(item1); itemsSet.add(item2); cart1.setItems(itemsSet); // wrong!
Как показано выше, пункт 2 ссылки корзина 2, в то время как диаграмма 2 не ссылается пункт 2, и это плохо .
Как Hibernate должен сохранять item2 в базе данных? Будет item2 ссылка на внешний ключ card1 или card2 ?
Мы разрешаем эту двусмысленность, используя идею стороны-владельца отношений; ссылки, принадлежащие стороне-владельцу, имеют приоритет и сохраняются в базе данных.
6.2. Предметы в качестве Стороны-владельца
Как указано в спецификации JPA в разделе 2.9, хорошей практикой является пометка многие к одному сторона в качестве стороны-владельца.
Другими словами, I tems будет стороной владельца и Cart обратной стороной, что именно то, что мы делали ранее.
Так как же мы этого добились?
Путем включения mappedBy атрибут в Тележка класс, мы отмечаем его как обратную сторону.
В то же время мы также комментируем Предметы. корзина поле с @ManyToOne , изготовление Предметы сторона собственника.
Возвращаясь к нашему примеру “несогласованности”, теперь Hibernate знает, что ссылка item 2 более важна и сохранит ссылку item2 в базе данных.
Давайте проверим результат:
item1 ID=1, Foreign Key Cart ID=1 item2 ID=2, Foreign Key Cart ID=2
Хотя корзина ссылки пункт 2 в нашем фрагменте, пункт 2 ссылка на корзину 2 сохраняется в базе данных.
6.3. Тележка как Сторона-владелец
Также можно пометить сторону один ко многим как сторону владельца, а сторону много к одному как обратную сторону.
Хотя это не рекомендуется, давайте попробуем.
В приведенном ниже фрагменте кода показана реализация one-to-many side в качестве стороны-владельца:
public class ItemsOIO { // ... @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 Setitems; //.. }
Обратите внимание, как мы удалили элемент mappedBy и установили many-to-one @JoinColumn как вставляемый и обновляемый в false .
Если мы запустим один и тот же код, результат будет противоположным:
item1 ID=1, Foreign Key Cart ID=1 item2 ID=2, Foreign Key Cart ID=1
Как показано выше, теперь пункт 2 принадлежит корзине.
7. Заключение
Мы видели, как легко реализовать связь один ко многим с базой данных Hibernate ORM и H2 с помощью аннотаций JPA.
Кроме того, мы узнали о двунаправленных отношениях и о том, как реализовать понятие стороны-владельца.
Исходный код в этой статье можно найти на GitHub .