Hibernate — пример отображения один ко многим One-To-Many и Many-To-One
Большинство баз данных содержат в себе зависимости вида «один ко многим» и «многие ко многим». Здесь будет рассмотрен пример отображения «один ко многим» и показана реализация извлечения данных из таблицы с помощью Java кода и аннотаций.
Update:
На сайте есть еще одна статья — реализация связи один-ко-многим в Java с помощью Hibernate. В процессе работы над другими статьями получилось две разных. Поэтому для тех, кому будет что-то непонятно здесь, могут обратиться в другой, почти такой же пример.
Используемые технологии:
Hibernate 5.0.1.Final
Maven 3.2.5
IntelliJ IDEA 14
MySQL 5.6.25
1. Описание задачи
Рассмотреть основные настройки для реализации зависимости один ко многим (One-to-Many). Получить данные из таблицы с помощью простого Java кода.
2. Структура проекта
Проект основан на предыдущих статьях и использует тот же код и таблицы. Для понимания того, что здесь используется и откуда берутся данные рекомендую сначала прочесть (там же есть код таблиц и базы данных):
Hibernate – быстрый старт. Пример приложения Hello World
Hibernate – примеры Criteria. Обзор Hibernate Criteria API
Используемая база данных выглядит так:
Таблица с контактами:
И детали номера для определенного контакта:
3. Настройка сущностей для маппинга данных
Итак, для этого примера нам требуется настроить две сущности для таблицы контактов и таблицы с деталями о телефонных номерах этих контактов для получения данных с помощью Hibernate. Новый код для связи один ко многим будет выделен относительно предыдущего примера.
3.1. Отображение один ко многим
Ассоциация один ко многим в нашем случае означает, что каждый контакт может иметь один или несколько телефонных номеров.
1 2 3 4 5 6 7 8 9 10 11 |
/* EXAMPLE One To Many */ private Set<ContactTelDetailEntity> contactTelDetails = new HashSet<ContactTelDetailEntity>(); @OneToMany(mappedBy = "contact", cascade = CascadeType.ALL, orphanRemoval = true) public Set<ContactTelDetailEntity> getContactTelDetails() { return this.contactTelDetails; } public void setContactTelDetails(Set<ContactTelDetailEntity> contactTelDetails) { this.contactTelDetails = contactTelDetails; } |
Нажмите чтобы посмотреть весь код класса контактов:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
package ru.javastudy.hibernate.dao; import javax.persistence.*; import java.io.Serializable; import java.util.Date; import java.util.HashSet; import java.util.Set; /** * Created by Nick on 05.09.2015. */ @Entity @Table(name = "contact", schema = "", catalog = "javastudy") public class ContactEntity implements Serializable{ private int id; private String firstName; private String lastName; private Date birthDate; private int version; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) //generated DataBase auto_increment when insert value @Column(name = "id", nullable = false, insertable = true, updatable = true) public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "first_name", nullable = false, insertable = true, updatable = true, length = 60) public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Basic @Column(name = "last_name", nullable = false, insertable = true, updatable = true, length = 40) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } //NOTE THIS! @Temporal(TemporalType.DATE) //in table uses java.sql.Date, we use java.util.Date. @Column(name = "birth_date", nullable = true, insertable = true, updatable = true) public java.util.Date getBirthDate() { return birthDate; } public void setBirthDate(Date birthDate) { this.birthDate = birthDate; } //NOTE THIS! @Version //используем механизм оптимистичной блокировки. @Column(name = "version", nullable = false, insertable = true, updatable = true) public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } /* * EXAMPLE One To Many */ private Set<ContactTelDetailEntity> contactTelDetails = new HashSet<ContactTelDetailEntity>(); @OneToMany(mappedBy = "contact", cascade = CascadeType.ALL, orphanRemoval = true) public Set<ContactTelDetailEntity> getContactTelDetails() { return this.contactTelDetails; } public void setContactTelDetails(Set<ContactTelDetailEntity> contactTelDetails) { this.contactTelDetails = contactTelDetails; } public void addContactTelDetail(ContactTelDetailEntity contactTelDetail) { contactTelDetail.setContact(this); getContactTelDetails().add(contactTelDetail); } public void removeContactTelDetail(ContactTelDetailEntity contactTelDetail) { getContactTelDetails().remove(contactTelDetail); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ContactEntity that = (ContactEntity) o; if (id != that.id) return false; if (version != that.version) return false; if (firstName != null ? !firstName.equals(that.firstName) : that.firstName != null) return false; if (lastName != null ? !lastName.equals(that.lastName) : that.lastName != null) return false; if (birthDate != null ? !birthDate.equals(that.birthDate) : that.birthDate != null) return false; return true; } @Override public int hashCode() { int result = id; result = 31 * result + (firstName != null ? firstName.hashCode() : 0); result = 31 * result + (lastName != null ? lastName.hashCode() : 0); result = 31 * result + (birthDate != null ? birthDate.hashCode() : 0); result = 31 * result + version; return result; } @Override public String toString() { return "ContactEntity{" + "id=" + id + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + ", birthDate=" + birthDate + ", version=" + version + '}'; } } |
Метод извлечения атрибута contactTelDetails аннотирован с помощью аннотации @OneToMany, указывающей на наличие отношения “один ко многим” с классом ContactTelDetailEntity. Этой аннотации передается несколько атрибутов. Атрибут mappedBy задает свойство в классе ContactTelDetailEntity, которое предоставляет ассоциацию (т.е. связано с определением внешнего ключа в таблице CONTACT_TEL_DETAIL). Атрибут cascade означает, что операция обновления должна распространяться на дочерние записи. Атрибут orphanRemoval указывает, что после обновления деталей телефонных номеров контакта записи, которые больше не существуют в наборе, должны быть удалены из базы данных.
Теперь класс ContactTelDetailEntity, в котором настроено отображение ассоциации:
1 2 3 4 5 6 7 8 9 10 11 12 |
/* EXAMPLE Many To One */ private ContactEntity contact; @ManyToOne @JoinColumn(name = "contact_id") public ContactEntity getContact() { return this.contact; } public void setContact(ContactEntity contact) { this.contact = contact; } |
Весь код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
package ru.javastudy.hibernate.dao; import javax.persistence.*; /** * Created by Nick on 05.09.2015. */ @Entity @Table(name = "contact_tel_detail", schema = "", catalog = "javastudy") public class ContactTelDetailEntity { private int id; private String telType; private String telNumber; private int version; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, insertable = true, updatable = true) public int getId() { return id; } public void setId(int id) { this.id = id; } @Basic @Column(name = "tel_type", nullable = false, insertable = true, updatable = true, length = 20) public String getTelType() { return telType; } public void setTelType(String telType) { this.telType = telType; } @Basic @Column(name = "tel_number", nullable = false, insertable = true, updatable = true, length = 20) public String getTelNumber() { return telNumber; } public void setTelNumber(String telNumber) { this.telNumber = telNumber; } //NOTE THIS! @Version @Column(name = "version", nullable = false, insertable = true, updatable = true) public int getVersion() { return version; } public void setVersion(int version) { this.version = version; } /* * EXAMPLE Many To One */ private ContactEntity contact; @ManyToOne @JoinColumn(name = "contact_id") public ContactEntity getContact() { return this.contact; } public void setContact(ContactEntity contact) { this.contact = contact; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; ContactTelDetailEntity that = (ContactTelDetailEntity) o; if (id != that.id) return false; if (version != that.version) return false; if (telType != null ? !telType.equals(that.telType) : that.telType != null) return false; if (telNumber != null ? !telNumber.equals(that.telNumber) : that.telNumber != null) return false; return true; } @Override public int hashCode() { int result = id; result = 31 * result + (telType != null ? telType.hashCode() : 0); result = 31 * result + (telNumber != null ? telNumber.hashCode() : 0); result = 31 * result + version; return result; } @Override public String toString() { return "ContactTelDetailEntity{" + "id=" + id + ", contact id: " + getContact().getId() + ", telType='" + telType + '\'' + ", telNumber='" + telNumber + '\'' + ", version=" + version + ", contact=" + contact + '}'; } } |
Аннотация @ManyToOne (для нескольких телефонов есть один контакт) задает другую сторону ассоциации с ContactEntity. Так же был переопределен метод toString() с добавлением getContact().getId(), чтобы легче было просматривать результат.
4. Извлечение данных из таблицы
Был создан простенький интерфейс ContactDao с методами с понятными названиями
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public interface ContactDAO { // Найти все контакты. public List<ContactEntity> findAll(); // Найти все контакты с заданным телефоном и хобби. public List<ContactEntity> findAllWithDetail(); // Найти контакт со всеми деталями по идентификатору. public ContactEntity findById(Long id); // Вставить или обновить контакт. public ContactEntity save(ContactEntity contact); // Удалить контакт. public void delete(ContactEntity contact); } |
Его реализация ContactDaoImpl (Только один метод задействован)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public class ContactDAOImpl implements ContactDAO { private Session session; public List<ContactEntity> findAll() { return session.createQuery("from ContactEntity c").list(); } public List<ContactEntity> findAllWithDetail() { return null; } public ContactEntity findById(Long id) { return null; } public ContactEntity save(ContactEntity contact) { return null; } public void delete(ContactEntity contact) { } public void setSession(Session session) { this.session = session; } public Session getSession() { return session; } } |
в методе поиска всех контактов используется простой запрос HQL, который выводит все найденные записи из таблицы ContactEntity.
5. Извлечение контактов из базы данных
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class AppMain { @PersistenceUnit static EntityManager emf; public static void main(String[] args) { System.out.println("Hibernate tutorial start"); Session session = HibernateSessionFactory.getSessionFactory().openSession(); ContactDAOImpl contactDAO = new ContactDAOImpl(); contactDAO.setSession(session); Transaction tx = session.beginTransaction(); List<ContactEntity> contacts = contactDAO.findAll(); for (ContactEntity contact : contacts) { System.out.println(contact); } listContactsWithDetail(contacts); tx.commit(); session.close(); } |
Вызываем метод findAll у экземпляра contactDAO и полученный список выводим в консоль:
1 2 3 4 5 6 |
ContactEntity{id=3, firstName='Name0', lastName='LastName0', birthDate=2015-07-08, version=0} ContactEntity{id=8, firstName='Name1 ', lastName='LastName1', birthDate=2014-09-01, version=0} ContactEntity{id=10, firstName='Name2', lastName='LastName2', birthDate=2014-05-01, version=0} ContactEntity{id=11, firstName='Name3', lastName='LastName3', birthDate=2016-09-02, version=0} ContactEntity{id=12, firstName='Name4', lastName='LastName4', birthDate=2015-11-01, version=0} ContactEntity{id=13, firstName='Name5', lastName='LastName5', birthDate=2012-09-07, version=0} |
5.1. Извлечение данных с использованием связи Один ко Многим (One-to-Many)
Для получения контактов и их телефонных номеров используется метод listContactsWithDetail()
1 2 3 4 5 6 7 8 9 10 11 |
private static void listContactsWithDetail(List<ContactEntity> contacts) { System.out.println("Contact with detail info"); for (ContactEntity contact : contacts) { System.out.println(contact); if (contact.getContactTelDetails() != null) { for (ContactTelDetailEntity detailedContact : contact.getContactTelDetails()) { System.out.println(detailedContact); } } } } |
Получаем для нашей базы данных следующий результат
1 2 3 4 5 6 7 8 9 |
Contact with detail info ContactEntity{id=3, firstName='Name0', lastName='LastName0', birthDate=2015-07-08, version=0} ContactTelDetailEntity{id=3, contact id: 3, telType='Рабочий', telNumber='555-44-33', version=0, contact=ContactEntity{id=3, firstName='Name0', lastName='LastName0', birthDate=2015-07-08, version=0}} ContactTelDetailEntity{id=1, contact id: 3, telType='Домашний', telNumber='8-800-200-600', version=0, contact=ContactEntity{id=3, firstName='Name0', lastName='LastName0', birthDate=2015-07-08, version=0}} ContactEntity{id=8, firstName='Name1 ', lastName='LastName1', birthDate=2014-09-01, version=0} ContactEntity{id=10, firstName='Name2', lastName='LastName2', birthDate=2014-05-01, version=0} ContactEntity{id=11, firstName='Name3', lastName='LastName3', birthDate=2016-09-02, version=0} ContactEntity{id=12, firstName='Name4', lastName='LastName4', birthDate=2015-11-01, version=0} ContactEntity{id=13, firstName='Name5', lastName='LastName5', birthDate=2012-09-07, version=0} |
Т.к. в базе данных только два номера для контакта с id=3, то мы получаем соответствующий результат.
Ссылки для скачивания
Hibernate OneToMany — исходники Java
SQLHibernate OneToMany — MySQL база данных с заполненными таблицами
11One thought on “Hibernate — пример отображения один ко многим One-To-Many и Many-To-One”
Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.
Привет! Создайте ссылочный ключ в таблице дочерней сущности, если отношение однонаправленное; в противном случае, используйте отношение «многие-к-одному» для создания двунаправленного отношения.