Бесплатный курс по Java: от основ до продвинутого уровня
Настройка связей в 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().
Важно помнить, что по умолчанию cascade не задан, то есть он ничего не содержит. Поэтому нужно явно устанавливать нужный тип cascade между таблицами.
Настройте связь Один-ко-Многим в Hibernate с помощью @OneToMany. Узнайте, как работать с каскадными операциями и управлять связанными сущностями в Java приложениях.