Топ-100Настройка связей в Hibernate приложении - CodOrbits
LogoCodOrbits

Раздел: Hibernate

Познакомьтесь с Hibernate — ORM-фреймворком Java для удобной работы с базами данных через объекты.

Все разделы
Иконка Hibernate

Настройка связей в Hibernate приложении

Last updated: 11 мая 2025 г.

В базе данных обычно существуют связанные таблицы.

Все стандартные виды связей (1-к-1, 1-ко-Многим, Многие-Ко-Многим) можно реализовать и между java классами тех таблиц, которые связаны между собой в базе.

Объекты этих связанных классов, очевидно, тоже могут быть связанными. Связанные объекты связанных классов, это связанные строки связанных таблиц.

Благодаря этому, можно сделать так, чтобы при добавлении в таблицу одной строки через объект, одновременно добавлялась в другую таблицу другая строка, благодаря тому, что объекты этих двух строк связаны в программе. То есть в БД добавятся связанные строки.

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

Также помимо одновременного удаления и добавления есть и другие каскадные операции, которые будут рассмотрены в конце урока.

Давайте с помощью SQL запросов создадим две связанные таблицы – таблицу author и таблицу author_info. В одной таблице хранятся авторы, в другой информация о них.

Между этими таблицами будет тип связи 1-к-1, то есть одной строке таблицы author, соответствует одна строка в таблице author_info. То есть вся информация о конкретном авторе храниться в одной строке другой таблицы.  

Выполним эти SQL запросы:

Теперь создадим класс только что созданной таблицы author_info. В нем пока ничего нового.

1package HibernateApps;
2
3import javax.persistence.CascadeType;
4@Entity
5//это класс с таблицы author_info
6@Table(name = "author_info")
7public class AuthorInfo {
8    @Id
9    @GeneratedValue(
10        strategy=GenerationType.IDENTITY)
11    @Column(name="id")
12    private int id;
13    @Column(name="books_written_number")
14    private int booksWritten;
15    @Column(name="country")
16    private String country;
17    public AuthorInfo() {}
18    public AuthorInfo(int booksWritten,
19                      String country) {
20        this.booksWritten = booksWritten;
21        this.country = country;
22    }
23    public int getId() {
24        return id;
25    }
26    public void setId(int id) {
27        this.id = id;
28    }
29    public int getBooksWritten() {
30        return booksWritten;
31    }
32    public void setBooksWritten(
33        int booksWritten) {
34        this.booksWritten = booksWritten;
35    }
36    public String getCountry() {
37        return country;
38    }
39    public void setCountry(String country) {
40        this.country = country;
41    }
42    @Override
43    public String toString() {
44        return "AuthorInfo [id=" + id +
45               ", booksWritten=" 
46               + booksWritten +
47               ", country="
48               + country + "]";
49    }
50}

Теперь создадим класс только что созданной таблицы author. В нем мы связываем классы связью 1-к-1.

1package HibernateApps;
2
3import java.util.List;
4import javax.persistence.Entity;
5import javax.persistence.GeneratedValue;
6import javax.persistence.GenerationType;
7import javax.persistence.Id;
8import javax.persistence.Column;
9import javax.persistence.Table;
10import javax.persistence.OneToOne;
11import javax.persistence.JoinColumn;
12import javax.persistence.CascadeType;
13
14//это класс с таблицы author
15@Entity
16@Table(name = "author")
17public class Author {
18
19    @Id
20    @GeneratedValue(strategy = GenerationType.IDENTITY)
21    @Column(name = "id")
22    private int id;
23
24    @Column(name = "name")
25    private String authorName;
26
27    //Помним что в БД у нас настроена связь 1-к-1
28    //между таблицами author и author_info.
29    //Поэтому мы можем настроить связь мужду
30    //классами этих таблиц – Author и AuthorInfo.
31    //Точнее говоря настраивается
32    //связь между объектом текущего класса
33    //Author и объектом AuthorInfo класса
34    //AuthorInfo, который можно увидеть ниже.
35    //Тоесть связь настраивается между двумя
36    //объектами связанных строк связных таблиц.
37    //Аннотацией @OneToOne настраивается
38    //этот вид связи
39
40    //Кратко о том что такое cascade
41    //В конце урока. Пока оставим CascadeType.ALL
42    @OneToOne(cascade = CascadeType.ALL)
43    //Указываем каким атрибутом таблица данного
44    //класса связана с таблицей связанного.
45    @JoinColumn(name = "author_info_id")
46    //Ниже объект связанной таблицы
47    private AuthorInfo authorInfo;
48
49    public Author() {
50    }
51
52    public Author(String authorName) {
53        this.authorName = authorName;
54    }
55
56    public int getId() {
57        return id;
58    }
59
60    public void setId(int id) {
61        this.id = id;
62    }
63
64    public String getAuthorName() {
65        return authorName;
66    }
67
68    public void setAuthorName(String authorName) {
69        this.authorName = authorName;
70    }
71
72    public AuthorInfo getAuthorInfo() {
73        return authorInfo;
74    }
75
76    public void setAuthorInfo(AuthorInfo authorInfo) {
77        this.authorInfo = authorInfo;
78    }
79
80
81    @Override
82    public String toString() {
83        return "Author [id=" + id + ", "
84                + "authorName=" + authorName + ", "
85                + "authorInfo="
86                + authorInfo + "]";
87    }
88}

Работа со связанными классами. Каскадная PERSIST операция.

Теперь давайте создадим два объекта, свяжем их и добавим их в БД.

1package HibernateApps;
2
3import java.util.List;
4
5public class HibernateApp {
6
7  public static void main(String[] args) {
8    //Делаем так чтобы sessionFactory анализировал
9    //классы Author и AuthorInfo.
10    SessionFactory sessionFactory = new Configuration()
11      .configure("hibernate.cfg.xml")
12      .addAnnotatedClass(Author.class)
13      .addAnnotatedClass(AuthorInfo.class)
14      .buildSessionFactory();
15
16    Session session = sessionFactory.getCurrentSession();
17
18    try {
19      //Ниже создаются два объекта созданных классов.
20      Author author = new Author("JJ Rouling");
21      AuthorInfo authorInfo = new AuthorInfo(
22        1, "Great Britain");
23      //Через сеттер связываем два созданных объекта
24      author.setAuthorInfo(authorInfo);
25
26      session.beginTransaction();
27
28      //Ниже при добавлении в таблицу author одной
29      //строки через объект author, добавляется
30      //в таблицу author_info другая строка,
31      //благодаря тому что объекты этих двух
32      //строк мы только что связали.
33      //Это называется каскадная PERSIST операция,
34      //тоесть когда сохраняется один объект в базу
35      //сохраняется и связанный с ним.
36      session.save(author);
37
38      session.getTransaction().commit();
39
40    } catch (Exception e) {
41      session.getTransaction().rollback();
42      e.printStackTrace();
43    } finally {
44      session.close();
45    }
46  }
47
48}

Давайте запустим нашу программу.

Как видим, произошло два запроса на вставку, хотя метод save мы вызывали только над одним объектом actor. Это доказывает что каскадная PERSIST операция сработала и произошел insert не только объекта author в таблицу author, а и связанного с ним объекта authorinfo в таблицу author_info.

Как видим в обе таблицы успешно добавились объекты.


Работа со связанными классами. Каскадная REMOVE операция.

Теперь продемонстрируем каскадную REMOVE операцию.

1package HibernateApps;
2
3import java.util.List;
4
5public class HibernateApp {
6
7  public static void main(String[] args) {
8    //Делаем так чтобы sessionFactory анализировал
9    //классы Author и AuthorInfo.
10    SessionFactory sessionFactory = new Configuration()
11      .configure("hibernate.cfg.xml")
12      .addAnnotatedClass(Author.class)
13      .addAnnotatedClass(AuthorInfo.class)
14      .buildSessionFactory();
15
16    Session session = sessionFactory.getCurrentSession();
17
18    try {
19      session.beginTransaction();
20
21      //Давайте извлечем из таблицы actor строку
22      //которую мы только что туда добавили.
23      Author author = session.get(Author.class, 1);
24      //Вместе с извлеченной строкой из таблицы author
25      //извлекается и связанная с ним строка из
26      //таблицы author_info. Тоесть в объекте author
27      //строки из таблицы author
28      //уже будет присутствовать объект связанной
29      //с этой строкой строки из таблицы author_info.
30      System.out.println(author);
31
32      //Ниже при удалении одной строки
33      //из таблицы author через объект author,
34      //одновременно удаляется и связанная с этой
35      //строкой строка из таблицы author_info,
36      //опять же благодаря тому,
37      //что объекты этих двух строк связаны в программе.
38      //Это называется каскадная REMOVE операция,
39      //тоесть когда удаляется один объект в базе
40      //удаляется и связанный с ним из базы.
41      session.delete(author);
42
43
44      session.getTransaction().commit();
45
46    } catch (Exception e) {
47      session.getTransaction().rollback();
48      e.printStackTrace();
49    } finally {
50      session.close();
51    }
52  }
53
54}

Давайте запустим нашу программу.

Как видим, в объекте author присутствует объект authorInfo, который соответствует связанной строке из таблицы author_info.

Далее можно увидеть, что произошло два запроса на удаление, хотя метод delete мы вызывали только над одним объектом actor. Это доказывает, что каскадная REMOVE операция сработала и произошел delete не только объекта author в таблице author, а и связанного с ним объекта в таблице author_info.

Связанные строки успешно удалились из обеих таблиц.


О каскадных операциях

Только что мы рассмотрели две каскадные операции PERSIST(добавление связанных объектов в БД), REMOVE(удаление связанных объектов из БД).

Но есть и другие – MERGE, DETACH и REFRESH.

Детали их работы и настройки покажем на примере.

1package HibernateApps;
2
3import java.util.List;
4
5@Entity
6//это класс с таблицы author
7@Table(name = "author")
8public class Author {
9
10    @Id
11    @GeneratedValue(
12            strategy = GenerationType.IDENTITY
13    )
14    @Column(name="id")
15    private int id;
16
17    @Column(name="name")
18    private String authorName;
19    //CascadeType.ALL – все каскадные операции разрешены. 
20    //Подробное пояснение типов cascade приведено ниже
21    @OneToOne(cascade={CascadeType.ALL})
22    @JoinColumn(name="author_info_id")
23    private AuthorInfo authorInfo;
24
25    public Author() {
26    }
27
28    public Author(String authorName) {
29        this.authorName = authorName;
30    }
31
32    public int getId() {
33        return id;
34    }
35
36    public void setId(int id) {
37        this.id = id;
38    }
39
40    public String getAuthorName() {
41        return authorName;
42    }
43
44    public void setAuthorName(String authorName) {
45        this.authorName = authorName;
46    }
47
48    public AuthorInfo getAuthorInfo() {
49        return authorInfo;
50    }
51
52    public void setAuthorInfo(AuthorInfo authorInfo) {
53        this.authorInfo = authorInfo;
54    }
55
56    @Override
57    public String toString() {
58        return "Author [id=" + id + ", "
59                + "authorName=" + authorName
60                + ", authorInfo="
61                + authorInfo + "]";
62    }
63}

CascadeType.ALL значит, что все каскадные операции разрешены. Вместо CascadeType.ALL можно было написать так: cascade={CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH} и это было бы то же самое что CascadeType.ALL.

Любой из каскадирований можно убирать или добавлять в этих скобочках, то есть если убрать CascadeType.REMOVE, то каскадная операция REMOVE работать больше не будет со связанными объектами Author и AuthorInfo. При удалении объекта Author из БД, связанный с ним объект AuthorInfo из БД удаляться не будет.

Кратко рассмотрим каждую каскадную операцию на примере связи 1-к-1 объектов Author и AuthorInfo.

DETACH – если объект класса Author внезапно перестает принадлежать сессии, тогда и связанный с ним объект класса AuthorInfo тоже перестает ей принадлежать. Это можно сделать с помощью функции session.detach().

MERGE – если через объект класса Author происходит обновление строки в таблице author, то тогда через объект класса AuthorInfo, который связан с этим объектом Author, происходит и обновление связанной с этой строкой строки в таблице author_info. Это можно сделать с помощью функции session.save().

PERSIST – если через объект класса Author происходит сохранение новой строки в таблице author, то тогда через объект класса AuthorInfo, который связан с этим объектом Author, тоже происходит сохранение новой строки в таблицу author_info. Это можно сделать с помощью функции session.save().

REMOVE – если через объект класса Author происходит удаление строки в таблице author, то тогда через объект класса AuthorInfo, который связан с этим объектом Author, происходит и удаление связанной с этой строкой строки в таблице author_info. Это можно сделать с помощью функции session.delete().

REFRESH – если в программе происходит обновление объекта класса Author (имеется в виду перезвлечение строки таблицы в объект. Тоесть, происходит обновление объекта в программе, не в БД), то происходит и обновление (перевзвлечение) объекта, который связан с объектом класса AuthorInfo. Это можно сделать с помощью функции session.refresh().

Search Icon

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


Дополнительные материалы

Additional Material Icon

Следующие уроки

Двусторонняя связь между сущностями в Hibernate

20
мин.

Similar Articles Icon
Divider

Настройка связи Один-ко-Многим в Hibernate

20
мин.

Similar Articles Icon
Divider

Типы извлечения данных в Hibernate (fetch types)

19
мин.

Similar Articles Icon