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

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

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

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

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

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

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

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

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

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

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

package HibernateApps; import javax.persistence.CascadeType; @Entity //это класс с таблицы author_info @Table(name = “author_info”) public class AuthorInfo { @Id @GeneratedValue( strategy=GenerationType.IDENTITY) @Column(name=”id”) private int id; @Column(name=”books_written_number”) private int booksWritten; @Column(name=”country”) private String country; public AuthorInfo() {} public AuthorInfo(int booksWritten, String country) { this.booksWritten = booksWritten; this.country = country; } public int getId() { return id; } public void setId(int id) { this.id = id; } public int getBooksWritten() { return booksWritten; } public void setBooksWritten( int booksWritten) { this.booksWritten = booksWritten; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } @Override public String toString() { return “AuthorInfo [id=” + id + “, booksWritten=” + booksWritten + “, country=” + country + “]”; } }

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

package HibernateApps; import java.util.List; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Column; import javax.persistence.Table; import javax.persistence.OneToOne; import javax.persistence.JoinColumn; import javax.persistence.CascadeType; //это класс с таблицы author @Entity @Table(name = “author”) public class Author { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = “id”) private int id; @Column(name = “name”) private String authorName; //Помним что в БД у нас настроена связь 1-к-1 //между таблицами author и author_info. //Поэтому мы можем настроить связь мужду //классами этих таблиц – Author и AuthorInfo. //Точнее говоря настраивается //связь между объектом текущего класса //Author и объектом AuthorInfo класса //AuthorInfo, который можно увидеть ниже. //Тоесть связь настраивается между двумя //объектами связанных строк связных таблиц. //Аннотацией @OneToOne настраивается //этот вид связи //Кратко о том что такое cascade //В конце урока. Пока оставим CascadeType.ALL @OneToOne(cascade = CascadeType.ALL) //Указываем каким атрибутом таблица данного //класса связана с таблицей связанного. @JoinColumn(name = “author_info_id”) //Ниже объект связанной таблицы private AuthorInfo authorInfo; public Author() { } public Author(String authorName) { this.authorName = authorName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthorName() { return authorName; } public void setAuthorName(String authorName) { this.authorName = authorName; } public AuthorInfo getAuthorInfo() { return authorInfo; } public void setAuthorInfo(AuthorInfo authorInfo) { this.authorInfo = authorInfo; } @Override public String toString() { return “Author [id=” + id + “, ” + “authorName=” + authorName + “, ” + “authorInfo=” + authorInfo + “]”; } }

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

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

package HibernateApps; import java.util.List; public class HibernateApp { public static void main(String[] args) { //Делаем так чтобы sessionFactory анализировал //классы Author и AuthorInfo. SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { //Ниже создаются два объекта созданных классов. Author author = new Author(“JJ Rouling”); AuthorInfo authorInfo = new AuthorInfo( 1, “Great Britain”); //Через сеттер связываем два созданных объекта author.setAuthorInfo(authorInfo); session.beginTransaction(); //Ниже при добавлении в таблицу author одной //строки через объект author, добавляется //в таблицу author_info другая строка, //благодаря тому что объекты этих двух //строк мы только что связали. //Это называется каскадная PERSIST операция, //тоесть когда сохраняется один объект в базу //сохраняется и связанный с ним. session.save(author); session.getTransaction().commit(); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

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

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

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


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

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

package HibernateApps; import java.util.List; public class HibernateApp { public static void main(String[] args) { //Делаем так чтобы sessionFactory анализировал //классы Author и AuthorInfo. SessionFactory sessionFactory = new Configuration() .configure(“hibernate.cfg.xml”) .addAnnotatedClass(Author.class) .addAnnotatedClass(AuthorInfo.class) .buildSessionFactory(); Session session = sessionFactory.getCurrentSession(); try { session.beginTransaction(); //Давайте извлечем из таблицы actor строку //которую мы только что туда добавили. Author author = session.get(Author.class, 1); //Вместе с извлеченной строкой из таблицы author //извлекается и связанная с ним строка из //таблицы author_info. Тоесть в объекте author //строки из таблицы author //уже будет присутствовать объект связанной //с этой строкой строки из таблицы author_info. System.out.println(author); //Ниже при удалении одной строки //из таблицы author через объект author, //одновременно удаляется и связанная с этой //строкой строка из таблицы author_info, //опять же благодаря тому, //что объекты этих двух строк связаны в программе. //Это называется каскадная REMOVE операция, //тоесть когда удаляется один объект в базе //удаляется и связанный с ним из базы. session.delete(author); session.getTransaction().commit(); } catch (Exception e) { session.getTransaction().rollback(); e.printStackTrace(); } finally { session.close(); } } }

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

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

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

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


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

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

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

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

package HibernateApps; import java.util.List; @Entity //это класс с таблицы author @Table(name = “author”) public class Author { @Id @GeneratedValue( strategy = GenerationType.IDENTITY ) @Column(name=”id”) private int id; @Column(name=”name”) private String authorName; //CascadeType.ALL – все каскадные операции разрешены. //Подробное пояснение типов cascade приведено ниже @OneToOne(cascade={CascadeType.ALL}) @JoinColumn(name=”author_info_id”) private AuthorInfo authorInfo; public Author() { } public Author(String authorName) { this.authorName = authorName; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getAuthorName() { return authorName; } public void setAuthorName(String authorName) { this.authorName = authorName; } public AuthorInfo getAuthorInfo() { return authorInfo; } public void setAuthorInfo(AuthorInfo authorInfo) { this.authorInfo = authorInfo; } @Override public String toString() { return “Author [id=” + id + “, ” + “authorName=” + authorName + “, authorInfo=” + authorInfo + “]”; } }

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 между таблицами.

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

Реализуйте двустороннюю связь между сущностями в Hibernate. Узнайте, как настроить взаимодействие между объектами с помощью аннотаций @OneToOne и mappedBy.
Time to read: 20

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

Настройте связь Один-ко-Многим в Hibernate с помощью @OneToMany. Узнайте, как работать с каскадными операциями и управлять связанными сущностями в Java приложениях.
Time to read: 20

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

Изучите разницу между LAZY и EAGER загрузкой в Hibernate. Узнайте, когда связанные сущности загружаются из БД и как это влияет на производительность приложений.
Time to read: 19