❓ Какие существуют типы связей в JPA?
В предыдущей статье, мы поговорили о типах связей в реляционных БД. Давайте рассмотрим их реализации в JPA, на примере нашей доменной модели — Библиотеки, где у нас есть две сущности — Author и Book
Начнем с One to One, который можно реализовать двумя способами:
1⃣ Через аннотацию MapsId [этот вариант мы рассмотрим в отдельной статье]
2⃣ Через аннотацию OneToOne:
// Book.class
@OneToOne
// Annotation @JoinColumn(name = "author_id", nullable = false, unique = true) can be skipped,
// because of default name mapping
private Author author;
❓ А что в классе Author?
Представим, что мы вновь работаем только с SQL. Тогда, зная id сущности Author, мы можем получить как самого автора, так и его книгу. [автора по id, а книгу по author_id].
Теперь, возвращаемся к JPA. Если у нас нету ссылки на класс Book в классе Author, то по-умолчанию, самого автора получить мы можем, а вот его книгу — нет. Чтобы исправить это, нам нужно указать связь и в классе Author:
// Author.class
/*
* One Author to One Book
* Bidirectional relationship
* Owner of the relationship is Book [because books table contains foreign key to authors table],
* linked by 'author' field in Book class
*/
@OneToOne(mappedBy = "author")
private Book book;
Таким образом, в JPA выделяют два типа связей:
1⃣ Unidirectional — когда мы взаимодействуем только с owning side [тот, кто имеет foreign key, в нашем примере — Book.class]
2⃣ Bidirectional — когда нам необходимо через unowned side [тот, на кого ссылаются через foreign key, в нашем примере — Author.class], получить доступ к owning side [Book.class]. Для этого существует параметр mappedBy, в котором указывается поле из owning side [Book.class], которое ссылается через foreign key на unowned side [Author.class].
При использовании Bidirectional связи, появляются дополнительные проблемы, которые необходимо учитывать, например:
❌ В методах equals, hashCode, toString, а также при сериализации можно получить java.lang.StackOverflowError
❌ Если мы меняем только unowned side, без изменения owning side, то автоматически такие изменения не будут синхронизорованы с БД
❌ Для write операций необходимо поддерживать Bidirectional setter'ы, чтобы данные оставались консистентными в БД
Unidirectional пример доступен на 🐙Github
Bidirectional пример доступен на 🐙Github
One to Many и Many to One:
// Book.class
@ManyToOne
// Annotation @JoinColumn(name = "author_id", nullable = false) can be skipped,
// because of default name mapping
private Author author;
// Author.class
/*
* One Author to Many Books
* Bidirectional relationship
* Owner of the relationship is Book [because books table contains foreign key to authors table],
* linked by 'author' field in Book class
*/
@OneToMany(mappedBy = "author")
private Set books;
Hibernate не советует использовать аннотацию OneToMany без ManyToOne, о чем упоминается в документации
Unidirectional пример доступен на 🐙Github
Bidirectional пример доступен на 🐙Github
Many to Many:
// Book.class
@ManyToMany
@JoinTable(
name = "books_authors",
joinColumns = @JoinColumn(name = "book_id", nullable = false),
inverseJoinColumns = @JoinColumn(name = "author_id", nullable = false)
)
private Set authors;
// Author.class
/*
*
* Many Authors to Many Books
* Bidirectional relationship
* Owner of the relationship is Book,
* linked by 'authors' field in Book class. If required, Author can be owner side
*/
@ManyToMany(mappedBy = "authors")
private Set books;
Если возникает необходимость добавить дополнительные колонки в таблицу со связями и использовать их, то придется создавать дополнительную Entity. Об этом случае хорошо написано в документации
Unidirectional пример доступен на 🐙Github
Bidirectional пример доступен на 🐙Github
#sandbox_spring_data_jpa #relationships
➿ Меню
➿ Подпишись: @developer_sandbox
В предыдущей статье, мы поговорили о типах связей в реляционных БД. Давайте рассмотрим их реализации в JPA, на примере нашей доменной модели — Библиотеки, где у нас есть две сущности — Author и Book
Начнем с One to One, который можно реализовать двумя способами:
1⃣ Через аннотацию MapsId [этот вариант мы рассмотрим в отдельной статье]
2⃣ Через аннотацию OneToOne:
// Book.class
@OneToOne
// Annotation @JoinColumn(name = "author_id", nullable = false, unique = true) can be skipped,
// because of default name mapping
private Author author;
❓ А что в классе Author?
Представим, что мы вновь работаем только с SQL. Тогда, зная id сущности Author, мы можем получить как самого автора, так и его книгу. [автора по id, а книгу по author_id].
Теперь, возвращаемся к JPA. Если у нас нету ссылки на класс Book в классе Author, то по-умолчанию, самого автора получить мы можем, а вот его книгу — нет. Чтобы исправить это, нам нужно указать связь и в классе Author:
// Author.class
/*
* One Author to One Book
* Bidirectional relationship
* Owner of the relationship is Book [because books table contains foreign key to authors table],
* linked by 'author' field in Book class
*/
@OneToOne(mappedBy = "author")
private Book book;
Таким образом, в JPA выделяют два типа связей:
1⃣ Unidirectional — когда мы взаимодействуем только с owning side [тот, кто имеет foreign key, в нашем примере — Book.class]
2⃣ Bidirectional — когда нам необходимо через unowned side [тот, на кого ссылаются через foreign key, в нашем примере — Author.class], получить доступ к owning side [Book.class]. Для этого существует параметр mappedBy, в котором указывается поле из owning side [Book.class], которое ссылается через foreign key на unowned side [Author.class].
При использовании Bidirectional связи, появляются дополнительные проблемы, которые необходимо учитывать, например:
❌ В методах equals, hashCode, toString, а также при сериализации можно получить java.lang.StackOverflowError
❌ Если мы меняем только unowned side, без изменения owning side, то автоматически такие изменения не будут синхронизорованы с БД
❌ Для write операций необходимо поддерживать Bidirectional setter'ы, чтобы данные оставались консистентными в БД
Unidirectional пример доступен на 🐙Github
Bidirectional пример доступен на 🐙Github
One to Many и Many to One:
// Book.class
@ManyToOne
// Annotation @JoinColumn(name = "author_id", nullable = false) can be skipped,
// because of default name mapping
private Author author;
// Author.class
/*
* One Author to Many Books
* Bidirectional relationship
* Owner of the relationship is Book [because books table contains foreign key to authors table],
* linked by 'author' field in Book class
*/
@OneToMany(mappedBy = "author")
private Set books;
Hibernate не советует использовать аннотацию OneToMany без ManyToOne, о чем упоминается в документации
Unidirectional пример доступен на 🐙Github
Bidirectional пример доступен на 🐙Github
Many to Many:
// Book.class
@ManyToMany
@JoinTable(
name = "books_authors",
joinColumns = @JoinColumn(name = "book_id", nullable = false),
inverseJoinColumns = @JoinColumn(name = "author_id", nullable = false)
)
private Set authors;
// Author.class
/*
*
* Many Authors to Many Books
* Bidirectional relationship
* Owner of the relationship is Book,
* linked by 'authors' field in Book class. If required, Author can be owner side
*/
@ManyToMany(mappedBy = "authors")
private Set books;
Если возникает необходимость добавить дополнительные колонки в таблицу со связями и использовать их, то придется создавать дополнительную Entity. Об этом случае хорошо написано в документации
Unidirectional пример доступен на 🐙Github
Bidirectional пример доступен на 🐙Github
#sandbox_spring_data_jpa #relationships
➿ Меню
➿ Подпишись: @developer_sandbox