1. Въведение
В този урок ще разгледаме различните начини за създаване на едно към едно съпоставяне в JPA.
Ще ни трябва основно разбиране за рамката за хибернация, така че, моля, разгледайте нашето Ръководство за хибернация 5 с пролет за допълнителен фон.
2. Описание
Да предположим, че изграждаме система за управление на потребителите и нашият шеф ни моли да съхраняваме пощенски адрес за всеки потребител. Потребителят ще има един пощенски адрес, а пощенският адрес ще има само един потребител, свързан с него.
Това е пример за връзка едно към едно, в случая между потребителски и адресни обекти.
Нека да видим как можем да приложим това в следващите раздели.
3. Използване на външен ключ
3.1. Моделиране с външен ключ
Нека да разгледаме следната ER диаграма, която представлява съпоставяне едно към едно на базата на външен ключ:

В този пример колоната address_id в потребители е външният ключ към адреса .
3.2. Прилагане с външен ключ в JPA
Първо, нека да създадем потребителския клас и да го анотираме по подходящ начин:
@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "address_id", referencedColumnName = "id") private Address address; // ... getters and setters }
Обърнете внимание, че поставяме анотацията @OneToOne в полето на съответния обект, Адрес .
Също така трябва да поставим анотацията @JoinColumn, за да конфигурираме името на колоната в потребителската таблица, която се свързва с първичния ключ в таблицата с адреси . Ако не предоставим име, Hibernate ще следва някои правила, за да избере такова по подразбиране.
И накрая, имайте предвид в следващия обект, че няма да използваме анотацията @JoinColumn там. Това е така, защото се нуждаем само от собствената страна на връзката с външния ключ. Просто казано, който притежава колоната с външен ключ, получава анотацията @JoinColumn .
Обектът Address се оказва малко по-прост:
@Entity @Table(name = "address") public class Address { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "address") private User user; //... getters and setters }
Също така трябва да поставим и анотацията @OneToOne тук. Това е така, защото това е двупосочна връзка. Адресната страна на връзката се нарича непритежаваща страна.
4. Използване на споделен първичен ключ
4.1. Моделиране със споделен първичен ключ
В тази стратегия, вместо да създаваме нова колона address_id, ще маркираме първичния ключколона (user_id) на адресната таблица като външен ключ към таблицата потребители :

Оптимизирахме пространството за съхранение, като използвахме факта, че тези обекти имат индивидуална връзка между тях.
4.2. Внедряване със споделен първичен ключ в JPA
Забележете, че нашите дефиниции се променят само леко:
@Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "user", cascade = CascadeType.ALL) @PrimaryKeyJoinColumn private Address address; //... getters and setters }
@Entity @Table(name = "address") public class Address { @Id @Column(name = "user_id") private Long id; //... @OneToOne @MapsId @JoinColumn(name = "user_id") private User user; //... getters and setters }
В mappedBy атрибут сега се премества към потребителя класа, тъй като външния ключ е вече присъства в адрес масата. Добавили сме и на @PrimaryKeyJoinColumn пояснението, което показва, че първичния ключ на потребителя структура се използва като външен ключ стойност за съответстващия му Адрес субект .
Все още трябва да дефинираме поле @Id в класа Address, но имайте предвид, че това се позовава на колоната user_id и то вече не използва анотацията @GeneratedValue . Също така, на терена, че позоваванията на потребителя , ние добавихме на @MapsId пояснението, което показва, че основните ключови стойности ще бъдат копирани от потребителя предприятието.
5. Използване на таблица за присъединяване
Съпоставянията едно към едно могат да бъдат два вида - незадължителни и задължителни . Досега сме виждали само задължителни връзки.
Сега нека да видим да си представим, че нашите служители се свързват с работна станция. Това е едно към едно, но понякога служителят може да няма работна станция и обратно.
5.1. Моделиране с таблица за присъединяване
Стратегиите, които обсъждахме досега, ни принуждават да поставяме нулеви стойности в колоната за обработка на незадължителни връзки .
Обикновено мислим за връзки много към много, когато обмисляме таблица за присъединяване, но в този случай използването на таблица за присъединяване може да ни помогне да премахнем тези нулеви стойности:

Сега, когато имаме връзка, ще правим запис в таблицата emp_workstation и ще избягваме nullsсъвсем.
5.2. Внедряване с таблица за присъединяване в JPA
Първият ни пример използва @JoinColumn. Този път ще използваме @JoinTable :
@Entity @Table(name = "employee") public class Employee { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(cascade = CascadeType.ALL) @JoinTable(name = "emp_workstation", joinColumns = { @JoinColumn(name = "employee_id", referencedColumnName = "id") }, inverseJoinColumns = { @JoinColumn(name = "workstation_id", referencedColumnName = "id") }) private WorkStation workStation; //... getters and setters }
@Entity @Table(name = "workstation") public class WorkStation { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; //... @OneToOne(mappedBy = "workStation") private Employee employee; //... getters and setters }
@ JoinTable инструктира Hibernate да използва стратегията за присъединяване на масата, като същевременно поддържа връзката.
Също така служителят е собственик на тази връзка, тъй като избрахме да използваме анотацията на таблицата за присъединяване върху нея.
6. Заключение
В този урок научихме различни начини за поддържане на индивидуална асоциация в JPA и Hibernate и кога да ги използваме.
Изходният код на този урок може да бъде намерен в GitHub.