Как связать Entity в JPA?

В этом уроке речь пойдет о связях между таблицами базы данных на уровне Entity.

Теоретический материал

Что такое связь между Entity?

Entity — это сущность которая является отображением в базе данных. Связь между Entity — это зависимость одной сущности от другой. Очень часто используются они в построении больших БД.

Теперь рассмотрим какие есть виды связей

Допустим есть сущность A и сущность B как сделать между ними различные типы связей:

1. OneToOne — один к одному

На рисунку выше видно что есть две сущности Автор и Книга. Так как сущности имеют связь OneToOne, то с сущности Книга можно получить Автора этой книги, а с сущности Автор можно получить его книгу.

2. OneToMany — один ко многому

3. ManyToOne — много к одному

OneToMany и ManyToOne обычно применяет вместе, что это дает?

Со стороны Автора мы сможем получить список книг которые он написал — это связь OneToMany, а со стороны Книги мы можем получить автора который написал данную  книгу — это связь ManyToOne.

4. ManyToMany — много ко многому

В этой связи со стороны Автора мы можем получить все книги автора, а со стороны Книги мы можем получить список авторов, которые написали книгу.

Реализация

Теперь реализуем все выше рассмотренные виды связей.

Для дальнейших примеров нам нужно реализовать сущность Автор и Книга.

Создаем класс Author.java, который будет отображать сущность Автор.

package com.devcolibri.admin.entity;

import javax.persistence.*;

@Entity
@Table(name = "author")
public class Author {

    @Id
    private Integer id;

    @Column(name = "name")
    private String name;

    @Column(name = "last_name")
    private String lastName;

    public Author() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

Создаем класс Book.java, который будет отображать сущность Книга.

package com.devcolibri.admin.entity;

import javax.persistence.*;

@Entity
@Table(name = "book")
public class Book {

    @Id
    Integer id;

    @Column(name = "name")
    private String name;

    public Book() {
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Обязательно должны быть Getter & Setter для того чтобы JPA мог инициализировать данные поля.

OneToOne

В сущность Author добавляем следующее:

@OneToOne(optional = false)
@JoinColumn(name="book_id", unique = true, nullable = false, updatable = false)
private Book book;

public Book getBook() {
    return book;
}

public void setBook(Book book) {
    this.book = book;
}

а в сущность Book:

@OneToOne(optional = false, mappedBy="book")
public Author author;

public Author getAuthor() {
    return author;
}

public void setAuthor(Author author) {
    this.author = author;
}

Обратите внимание на mappedBy, он указывает на имя атрибута сущности Author для связи с ним.

OneToMany и ManyToOne

В сущность Author добавляем следующее:

@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JoinColumn(name = "book_id", nullable = false)
private Book book;

public Book getBook() {
    return book;
}

public void setBook(Book book) {
    this.book = book;
}

а в сущность Book:

@OneToMany(fetch = FetchType.EAGER, mappedBy = "book")
private Set<Author> users;

public Set<Author> getUsers() {
    return users;
}

public void setUsers(Set<Author> users) {
    this.users = users;
}

ManyToMany

В сущность Author добавляем следующее:

@ManyToMany
@JoinTable(name="author_book",
        joinColumns = @JoinColumn(name="author_id", referencedColumnName="id"),
        inverseJoinColumns = @JoinColumn(name="book_id", referencedColumnName="id")
)
private Set<Book> books;

public Set<Book> getBooks() {
    return books;
}

public void setBooks(Set<Book> books) {
    this.books = books;
}

а в сущность Book:

@ManyToMany(fetch = FetchType.EAGER, mappedBy = "books")
private Set<Author> users;

public Set<Author> getUsers() {
    return users;
}

public void setUsers(Set<Author> users) {
    this.users = users;
}

При такой связи будет получена новая промежуточная таблица:

CascadeType

Определяет набор каскадных операций, которые распространяются на соответствующие сущности:

cascade=ALL — на все операции

cascade={PERSIST, MERGE, REMOVE, REFRESH, DETACH} — на определенные операции, те что есть в перечислении будут учитываться.

@ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JoinColumn(name = "book_id", nullable = false)
private Book book;

В зависимости от cascade будет либо учитываться связь между сущностями для выполнения операции, либо нет.

FetchType

Определение стратегии для извлечения данных из базы данных.

EAGER — стратегия, которая предусматривает получение полной связи между сущностями, и последующих обращениях к связям не будет выполнять запрос на получение данных, так как данные изначально были получены полностью.

@OneToMany(fetch = FetchType.EAGER, mappedBy = "book")
private Set<Author> users;

LAZY — стратегия, которая не предусматривает получение полной связи сущностей, и при первом обращении к связи будет выполнятся запрос на получение данных с БД.

@OneToMany(fetch = FetchType.LAZY, mappedBy = "book")
private Set<Author> users;
Урок создан: 05 августа 2013 | Просмотров: 37413 | Автор: Александр Барчук | Правила перепечатки


Добавить комментарий

Добавить комментарий

Ваш e-mail не будет опубликован.

Комментарии:

  • 07 ноября 2013 в 14:16

    Sts

    Здорово конечно. Смущают поля вроде book_id в которых непонятно что хранится и в какой таблице они находятся.

  • 14 декабря 2013 в 13:31

    rimma

    смущает еще следующее (возможно, просто не вникла в суть я)
    раздел OneToMany и ManyToOne:
    1) Set users — users ? почему пользователи(читатели), если речь идет об авторах
    2) в коде сущности Book та же строка: private Set users, в коде сущности Author: private Book book;. так в данном примере у книги много авторов, и автор пишет одну книгу, или все-таки это ошибка и должно быть наоборот?

    • 14 декабря 2013 в 23:40

      Александр Барчук

      у одной книге может быть несколько авторов, Ну с именами переменых можно было бы и подумать :)

      • 15 декабря 2013 в 15:15

        rimma

        про то что у книги несколько авторов это несомненно, но в данном случае описывалась связь один-ко многим. Один — со стороны автора (судя по вступлению к статье и схеме связей), а в коде один- со стороны книги, это слегка путает неопытного читателя

  • 15 января 2014 в 09:49

    bumer7721

    Здравствуйте! Спасибо за такой мануал. Он мне здорово помог.
    Но здесь @ManyToMany(fetch = FetchType.EAGER, mappedBy = «book») у вас ошыбка, вместо «book» должно быть «books», иначе не работает.

  • 04 мая 2014 в 22:04

    Cooler

    Добрый вечер. Спасибо за статью. Но был бы рад увидеть продолжение в виде Дао класса, где мы добавляем поле в эти таблицы.
    В случае связи oneToMany, хочу добавить значения в таблицу автор и несколько книг. При этом у книг должен быть айди автора.
    Как это сделать? Как написать метод правильно? em.persist(author); добавить ведь только автора, есть ли метод у ентити менеджера, который закинет скопом в две таблицы данные, при этом с одной таблице передаст другой айди автора?

    Пс. из любопытсва, этот сайт вордпресс или сами писали?))

    • 05 мая 2014 в 00:17

      Александр Барчук

      Здравствуйте. Вы должны сохранить объект, em.merge он вернет вам тотже объект но уже с id дальше вы его проставляете set в тот объект который от него зависит и сохраняете, таким образом он ссылается на него. Ну а пачкой сохранить не совсем корректно :) Да на вордпресе, ну совсем скоро мы перезагрузимся, на новую версию которую мы пишем на Java EE.

      • 24 ноября 2014 в 17:50

        D

        Народ, кто нибудь объясните, неужели в EJB 3 нет возможности вывести список, содержащий поля нескольких связных таблиц? Я всё это читаю и у меня складывается впечатление что в простом примере с 3я таблицами 1) сотрудник(ид,фио и должность) 2) телефон(ид_сотрудника и номер) 3) паспорт(поля паспорта) при наличие сущностей имея задачей вывести перечень сотрудников с номерами телефонов и адресами прописки я должен дополнительно к сессионному бину создать ещё свой класс представление, содержащий все необходимые для вывода поля, затем итеративно заполнить вновь созданный список и только после этого могу его вывести на экран в фасаде?

  • 05 марта 2015 в 00:01

    Павел

    А не перепутали ли вы связи в диаграмме после слов «При такой связи будет получена новая промежуточная таблица:» ?

  • 24 ноября 2015 в 09:41

    Максим

    Добрый день! Спасибо большое за статью. Только Все равно абсолютно не ясно что за поле @JoinColumn(name = «book_id») ? Можно ли детально объяснить? Можно пример и схему таблиц? В Ентити books нет поля book_id, как его тогда установить? Спасибо

  • 29 ноября 2016 в 17:12

    Денис

    Результат запроса по указанной ссылке.
    Whitelabel Error Page

    This application has no explicit mapping for /error, so you are seeing this as a fallback.
    Tue Nov 29 10:11:51 EST 2016
    There was an unexpected error (type=Internal Server Error, status=500).
    I/O error on GET request for «http://localhost:9100/server/v1/hub/java»: Connection refused; nested exception is java.net.ConnectException: Connection refused

  • 08 октября 2017 в 15:37

    KK

    не переходит по ссылке, говорит 502 ошибка