Поскольку вы сказали, что изучаете и не пометили ни одного другого тега, кроме hibernate
, например spring
или spring-boot
, позвольте мне go подробно рассказать о том, как выполнить свою работу.
Давайте рассмотрим пример таблицы Cart and 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;
Если вы используете springboot, вы можете пропустить шаг настройки, так как он предварительно настроен только с одной зависимостью.
Зависимости Maven
Затем мы добавим зависимости драйвера Hibernate и H2 в наш файл pom. xml. Зависимость Hibernate использует протоколирование JBoss и автоматически добавляется в качестве транзитивных зависимостей:
- Версия Hibernate 5.2.7.Final
- H2 версия драйвера 1.4.197
Пожалуйста, посетите центральный репозиторий Maven для получения последних версий Hibernate и зависимостей H2.
Конфигурация Hibernate
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.driver_class">org.h2.Driver</property>
<property name="hibernate.connection.password"></property>
<property name="hibernate.connection.url">
jdbc:h2:mem:spring_hibernate_one_to_many</property>
<property name="hibernate.connection.username">sa</property>
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
<property name="hibernate.current_session_context_class">thread</property>
<property name="hibernate.show_sql">true</property>
</session-factory>
</hibernate-configuration>
HibernateAnnotationUtil Class
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;
}
Модели
Конфигурации, связанные с отображением, будут выполнены с использованием аннотаций JPA в классах моделей:
@Entity
@Table(name="CART")
public class Cart {
//...
@OneToMany(mappedBy="cart")
private Set<Items> items;
// getters and setters
}
Обратите внимание, что аннотация @OneToMany используется для определения свойства в классе Items, которое будет использоваться для сопоставления переменной 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
}
в действии
В тестовой программе мы создаем класс с Метод main () для получения сеанса Hibernate и сохранения объектов модели в базе данных, реализующий связь «один ко многим»:
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
Аннотация @ManyToOne
Как мы видели выше, мы можем указать отношение many-to-one
, используя аннотацию @ManyToOne
. Отображение many-to-one
означает, что многие экземпляры этого объекта сопоставлены одному экземпляру другого объекта - много элементов в одной корзине.
Аннотация @ ManyToOne позволяет также создавать двунаправленные отношения , Я расскажу об этом подробнее далее.
Несоответствия и право собственности
Теперь, если Корзина ссылается на Предметы, но Предметы, в свою очередь, не ссылаются на Корзину, наш отношения будут однонаправленными. Объекты также будут иметь естественную согласованность.
В нашем случае, однако, отношения являются двунаправленными, что создает возможность несогласованности.
Давайте представим ситуацию, когда разработчик хочет добавить item1 в корзину и item2 в cart2, но допускает ошибку, так что ссылки между cart2 и item2 становятся несовместимыми:
Cart cart1 = new Cart();
Cart cart2 = new Cart();
Items item1 = new Items(cart1);
Items item2 = new Items(cart2);
Set<Items> itemsSet = new HashSet<Items>();
itemsSet.add(item1);
itemsSet.add(item2);
cart1.setItems(itemsSet); // wrong!
Как показано выше, item2 ссылается на cart2, тогда как cart2 не ссылается на item2 - и это плохо.
Как Hibernate должен сохранить item2 в базу данных? Будет ли item2 foreign key
ссылаться на cart1 или cart2?
Мы разрешим эту неоднозначность, используя идею стороны-владельца - ссылки, принадлежащие стороне-владельцу, имеют приоритет и сохраняются в базе данных.
предметов как сторона-владелец это хорошая практика, чтобы пометить сторону "многие-к-одному" как сторону-хозяин.
Другими словами, предметы будут являться стороной-владельцем, а корзина - обратной. сторона, которая является именно тем, что мы сделали ранее.
Итак, как мы достигли этого?
Включая атрибут mappedBy в класс Cart, мы помечаем его как обратную сторону.
В то же время мы также аннотируем поле Items.cart с помощью @ManyToOne, делая Предметы стороной-владельцем.
Возвращаясь к нашему примеру «несоответствия», теперь Hibernate знает, что ссылка на item2 более важно и сохранит ссылку на item2 в базе данных.
Давайте проверим результат:
item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=2
Корзина как сторона-владелец
Это также можно отметить сторону «один ко многим» как сторону-хозяина, а сторону «многие-к-одному» как обратную сторону.
Хотя это не рекомендуемая практика, давайте go опередим ее и попробуем.
В приведенном ниже фрагменте кода показана реализация стороны один-ко-многим в качестве стороны-владельца:
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 Set<ItemsOIO> items;
//..
}
Обратите внимание на то, как мы удалили элемент mappedBy и установили @JoinColumn «многие-к-одному» как вставляемый и обновляемый на false.
Если мы запустим один и тот же код, результат будет противоположным:
item1 ID=1, Foreign Key Cart ID=1
item2 ID=2, Foreign Key Cart ID=1
Как показано выше, теперь item2 принадлежит корзине.
Надеюсь, этого было достаточно с точки зрения изучения.