Hibernate – пример отображения один к одному One-To-One
Рассмотрим как реализовать отношение один-к-одному с помощью Hibernate.
Используемые технологии:
Hibernate 5.0.1.Final
Maven 3.2.5
IntelliJ IDEA 14
MySQL 5.6.25
1. Описание задачи
Связь один-к-одному используется, когда необходимо отобразить разную по сути информацию, которую удобно хранить в разных таблицах, но жестко связанную. Т.е. одной записи из таблицы 1 соответствует одна запись в таблице 2.
2. Структура проекта
Имеются две таблицы employee и address. Для каждого сотрудника может быть указан только один адрес и каждый адрес соответствует только одному сотруднику. Настройки Hibernate, MySQL описаны в первой статье Hibernate – быстрый старт. Пример приложения Hello World.
Схема таблиц для этой статьи показана ниже. Мы будем использовать только две таблицы: address и employee.
3. Описание сущностей
Сущность EmployeeEntity:
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 |
package ru.javastudy.hibernate.dao.entities; import javax.persistence.*; @Entity @Table(name = "employee", schema = "", catalog = "relationship") public class EmployeeEntity { private int id; private String firstName; private String lastName; private int addressId; private AddressEntity address; @OneToOne @JoinColumn(name = "fk_ad_id", referencedColumnName = "ad_id") public AddressEntity getAddress() { return this.address; } public void setAddress(AddressEntity address) { this.address = address; } @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 = "firstName", nullable = true, insertable = true, updatable = true, length = 200) public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Basic @Column(name = "lastName", nullable = true, insertable = true, updatable = true, length = 200) public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Column(name = "fk_ad_id", nullable = false, insertable = false, updatable = false) public int getAddressId() { return addressId; } public void setAddressId(int addressId) { this.addressId = addressId; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; EmployeeEntity that = (EmployeeEntity) o; if (id != that.id) 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; 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); return result; } @Override public String toString() { return "EmployeeEntity{" + "id=" + id + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\'' + '}'; } } |
Сущность AddressEntity:
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 |
package ru.javastudy.hibernate.dao.entities; import javax.persistence.*; @Entity @Table(name = "address", schema = "", catalog = "relationship") public class AddressEntity { private int adId; private String street; private String city; private EmployeeEntity employee; @OneToOne(cascade = CascadeType.ALL, mappedBy = "address") public EmployeeEntity getEmployee() { return employee; } public void setEmployee(EmployeeEntity employee) { employee.setAddress(this); this.employee = employee; } @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "ad_id", nullable = false, insertable = true, updatable = true) public int getAdId() { return adId; } public void setAdId(int adId) { this.adId = adId; } @Basic @Column(name = "street", nullable = true, insertable = true, updatable = true, length = 200) public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } @Basic @Column(name = "city", nullable = true, insertable = true, updatable = true, length = 200) public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; AddressEntity that = (AddressEntity) o; if (adId != that.adId) return false; if (street != null ? !street.equals(that.street) : that.street != null) return false; if (city != null ? !city.equals(that.city) : that.city != null) return false; return true; } @Override public int hashCode() { int result = adId; result = 31 * result + (street != null ? street.hashCode() : 0); result = 31 * result + (city != null ? city.hashCode() : 0); return result; } @Override public String toString() { return "AddressEntity{" + "adId=" + adId + ", street='" + street + '\'' + ", city='" + city + '\'' + '}'; } } |
В начале каждой сущности указаны методы для связи двух таблиц.
Здесь нужно обратить внимание на использование атрибута mappedBy и аннотации @JoinColumn. Код будет работать независимо от того в какой из сущностей будет указан @OneToOne(mappedBy = «»), а в какой @JoinColumn(name = «», referencedColumnName = «»), но есть рекомендация использовать @JoinColumn в той сущности, которая имеет физическую информацию, а не ссылку foreign key (в примере один-ко-многим это легче для понимания, чем в один-к-одному, где связь и так прямая). В этом примере так же необходимо указать для addressId значения insertable = false, updatable = false. Для лучшего понимания этих аннотаций рекомендую попробовать поменять местами указание mappedBy и @JoinColumn и посмотреть какие изменения потребуется внести.
4. Методы доступа к БД
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package ru.javastudy.hibernate.dao.implementations; import org.hibernate.Query; import org.hibernate.Session; import ru.javastudy.hibernate.dao.entities.AddressEntity; import ru.javastudy.hibernate.dao.entities.EmployeeEntity; import ru.javastudy.hibernate.dao.interfaces.EmployeeDAO; import java.util.List; public class EmployeeDAOImpl implements EmployeeDAO { private Session session; public List<EmployeeEntity> findAll() { return session.createQuery("from EmployeeEntity c").list(); } public AddressEntity findAddressById(int id) { Query query = session.createQuery("from AddressEntity a where a.id = :id"); query.setParameter("id", id); return (AddressEntity) query.uniqueResult() ; } } |
Здесь два простеньких запроса для поиска всех записей в таблицы employee, а так же извлечение из таблицы address записи по внешнему ключу из employee, указывающему связь между сотрудником и адресом.
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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package ru.javastudy.hibernate.main; import org.hibernate.Session; import org.hibernate.Transaction; import ru.javastudy.hibernate.dao.entities.AddressEntity; import ru.javastudy.hibernate.dao.entities.EmployeeEntity; import ru.javastudy.hibernate.dao.implementations.EmployeeDAOImpl; import ru.javastudy.hibernate.utils.HibernateSessionFactory; import javax.persistence.EntityManager; import javax.persistence.PersistenceUnit; import java.util.List; public class AppMain { @PersistenceUnit static EntityManager emf; public static void main(String[] args) { System.out.println("Hibernate tutorial start"); Session session = HibernateSessionFactory.getSessionFactory().openSession(); EmployeeDAOImpl employeeDAO = new EmployeeDAOImpl(); employeeDAO.setSession(session); Transaction tx = session.beginTransaction(); EmployeeEntity employee = new EmployeeEntity(); employee.setFirstName("FirstName"); employee.setLastName("LasettName"); AddressEntity address = new AddressEntity(); address.setCity("Moscow"); address.setStreet("Lenina"); address.setEmployee(employee); session.save(address); List<EmployeeEntity> employeeList = employeeDAO.findAll(); for (EmployeeEntity empl : employeeList) { System.out.println("Found entity: " + empl); System.out.println("Address from employee entity: " + empl.getAddress().getAdId()); System.out.println("Address from database before transaction complete: " +employeeDAO.findAddressById(empl.getAddressId()) ); } tx.commit(); session.close(); } } |
Результат:
1 2 3 4 5 6 7 8 9 |
Found entity: EmployeeEntity{id=60, firstName='FirstName', lastName='LasettName'} Address from employee entity: 46 Address from database before transaction complete: AddressEntity{adId=46, street='Lenina', city='Moscow'} Found entity: EmployeeEntity{id=61, firstName='FirstName', lastName='LasettName'} Address from employee entity: 48 Address from database before transaction complete: AddressEntity{adId=48, street='Lenina', city='Moscow'} Found entity: EmployeeEntity{id=62, firstName='FirstName', lastName='LasettName'} Address from employee entity: 49 Address from database before transaction complete: null |
Исходные коды
Hibernate OneToOne — исходники
HibernateOneToOne SQL — база данных из статьи
7