Взаимоотношения „един към един” в JPA

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.