Це дуже поширене питання, тому ця відповідь заснована на цій статті, яку я написав у своєму блозі.
Один до багатьох
Співвідношення таблиці «один до багатьох» виглядає так:
У системі реляційних баз даних взаємозв'язок таблиці один на багато пов'язує дві таблиці на основі Foreign Key
стовпця у дочірній частині, який посилається Primary Key
на батьківський рядок таблиці.
На діаграмі таблиці вище, post_id
стовпець у post_comment
таблиці має Foreign Key
відношення до стовпця post
ідентифікатора Primary Key
таблиці:
ALTER TABLE
post_comment
ADD CONSTRAINT
fk_post_comment_post_id
FOREIGN KEY (post_id) REFERENCES post
@ManyToOne анотація
Найкращий спосіб зіставити співвідношення таблиці «один до багатьох» - це використання @ManyToOne
анотації.
У нашому випадку дочірнє об'єднання PostComment
відображає post_id
стовпчик "Зовнішній ключ" за допомогою @ManyToOne
анотації:
@Entity(name = "PostComment")
@Table(name = "post_comment")
public class PostComment {
@Id
@GeneratedValue
private Long id;
private String review;
@ManyToOne(fetch = FetchType.LAZY)
private Post post;
}
Використання @OneToMany
примітки JPA
Тільки тому, що у вас є можливість використовувати @OneToMany
анотацію, це не означає, що це має бути варіантом за замовчуванням для кожного " для багатьох" співвідношення бази даних . Проблема з колекціями полягає в тому, що ми можемо ними користуватися лише тоді, коли кількість дочірніх записів досить обмежена.
Найкращий спосіб відобразити @OneToMany
асоціацію - покладатися на @ManyToOne
сторону для поширення всіх змін стану сутності:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@OneToMany(
mappedBy = "post",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();
//Constructors, getters and setters removed for brevity
public void addComment(PostComment comment) {
comments.add(comment);
comment.setPost(this);
}
public void removeComment(PostComment comment) {
comments.remove(comment);
comment.setPost(null);
}
}
У материнської сутності Post
є два корисні методи (наприклад, addComment
та removeComment
), які використовуються для синхронізації обох сторін двонаправленого об'єднання. Ви завжди повинні надавати ці методи кожного разу, коли працюєте з двосторонньою асоціацією, оскільки, в іншому випадку, ви ризикуєте дуже тонкими проблемами поширення держави .
Односторонньої @OneToMany
асоціації слід уникати, оскільки вона менш ефективна, ніж використання @ManyToOne
або двостороння @OneToMany
асоціація.
Щоб отримати докладнішу інформацію про найкращий спосіб зіставити @OneToMany
стосунки з JPA та Hibernate, перегляньте цю статтю .
Один до одного
Співвідношення таблиці «один на один» виглядає так:
У системі реляційних баз даних взаємозв'язок таблиці один на один пов'язує дві таблиці на основі Primary Key
стовпця у дочірній, що також є Foreign Key
посиланням Primary Key
на батьківський рядок таблиці.
Тому можна сказати, що дочірня таблиця ділиться Primary Key
з батьківською таблицею.
На діаграмі таблиці вище, id
стовпець у post_details
таблиці також має Foreign Key
відношення до стовпця post
таблиці id
Primary Key
:
ALTER TABLE
post_details
ADD CONSTRAINT
fk_post_details_id
FOREIGN KEY (id) REFERENCES post
Використання JPA @OneToOne
з @MapsId
примітками
Найкращий спосіб зіставити @OneToOne
відносини - це використовувати @MapsId
. Таким чином, вам навіть не потрібна двонаправлена асоціація, оскільки ви завжди можете отримати PostDetails
об'єкт, використовуючи Post
ідентифікатор сутності.
Відображення виглядає приблизно так:
[код мови = "java"] @Entity (name = "PostDetails") @Table (name = "post_details") публічний клас PostDetails {
@Id
private Long id;
@Column(name = "created_on")
private Date createdOn;
@Column(name = "created_by")
private String createdBy;
@OneToOne(fetch = FetchType.LAZY)
@MapsId
@JoinColumn(name = "id")
private Post post;
public PostDetails() {}
public PostDetails(String createdBy) {
createdOn = new Date();
this.createdBy = createdBy;
}
//Getters and setters omitted for brevity
} [/ код]
Таким чином, id
властивість виконує функції "Первинний ключ" та "Зовнішній ключ". Ви помітите, що @Id
стовпець більше не використовує @GeneratedValue
анотацію, оскільки ідентифікатор заповнений ідентифікатором post
асоціації.
Щоб отримати докладнішу інформацію про найкращий спосіб зіставити @OneToOne
стосунки з JPA та Hibernate, перегляньте цю статтю .
Багато-до-багатьох
Співвідношення таблиці «багато до багатьох» виглядає так:
У реляційній системі баз даних багато-багато-багато табличних зв’язків пов'язують дві батьківські таблиці через дочірню таблицю, яка містить два Foreign Key
стовпці, що посилаються на Primary Key
стовпці двох батьківських таблиць.
На діаграмі таблиці вище, post_id
стовпець у post_tag
таблиці також має Foreign Key
зв'язок із стовпцем post
ідентифікатора Primary Key
таблиці:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_post_id
FOREIGN KEY (post_id) REFERENCES post
І, tag_id
стовпець у post_tag
таблиці має Foreign Key
відношення до стовпця tag
ідентифікатора Primary Key
таблиці:
ALTER TABLE
post_tag
ADD CONSTRAINT
fk_post_tag_tag_id
FOREIGN KEY (tag_id) REFERENCES tag
Використання @ManyToMany
карти JPA
Ось як можна зіставити many-to-many
взаємозв’язок таблиці з JPA та Hibernate:
@Entity(name = "Post")
@Table(name = "post")
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
@JoinTable(name = "post_tag",
joinColumns = @JoinColumn(name = "post_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id")
)
private Set<Tag> tags = new HashSet<>();
//Getters and setters ommitted for brevity
public void addTag(Tag tag) {
tags.add(tag);
tag.getPosts().add(this);
}
public void removeTag(Tag tag) {
tags.remove(tag);
tag.getPosts().remove(this);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Post)) return false;
return id != null && id.equals(((Post) o).getId());
}
@Override
public int hashCode() {
return 31;
}
}
@Entity(name = "Tag")
@Table(name = "tag")
public class Tag {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String name;
@ManyToMany(mappedBy = "tags")
private Set<Post> posts = new HashSet<>();
//Getters and setters ommitted for brevity
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Tag tag = (Tag) o;
return Objects.equals(name, tag.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
tags
Об'єднання в Post
сутності тільки визначає PERSIST
і MERGE
каскадні типів. Як пояснено в цій статті , REMOVE
перехід стану сутності не має жодного сенсу для @ManyToMany
асоціації JPA, оскільки це може спровокувати вилучення ланцюга, що врешті-решт знищить обидві сторони асоціації.
- Як пояснено в цій статті , методи додавання / видалення утиліти є обов'язковими, якщо ви використовуєте двосторонні асоціації, щоб ви могли переконатися, що обидві сторони асоціації синхронізовані.
- Суб'єкт
Post
господарювання використовує ідентифікатор сутності для рівності, оскільки йому не вистачає жодного унікального бізнес-ключа. Як пояснено в цій статті , ви можете використовувати ідентифікатор сутності для рівності, якщо ви переконаєтесь, що він залишається послідовним для всіх переходів стану сутності .
- Суб'єкт
Tag
господарювання має унікальний бізнес-ключ, який позначений @NaturalId
анотацією, що стосується сплячого режиму . У цьому випадку унікальний бізнес-ключ є найкращим кандидатом на перевірку рівності .
mappedBy
Атрибут posts
асоціації в Tag
позначках сутностей , що в цьому двобічної зв'язку, то Post
підприємство має асоціацію. Це потрібно, оскільки лише одна сторона може володіти відносинами, а зміни передаються лише до бази даних саме з цієї сторони.
- Переважним
Set
є те, що використання методу List
з @ManyToMany
менш ефективним.
Щоб отримати докладнішу інформацію про найкращий спосіб зіставити @ManyToMany
стосунки з JPA та Hibernate, перегляньте цю статтю .