[Программирование, Java] Односторонние и двусторонние отношения в Hibernate
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Всем нам хорошо известен ответ на вопрос, какими могут быть отношения между сущностями в Hibernate и JPA. Вариантов всего четыре:
- OneToOne - один к одному
- OneToMany - один ко многим
- ManyToOne - многие к одному
- ManyToMany - многие ко многим
Для каждого из отношений есть своя аннотация и, казалось бы, на этом можно закончить разговор, но все не так просто. Да и вообще, может ли быть что-то просто в Hibernate ;) Каждое из выше перечисленных отношений может быть односторонним (unidirectional) или двусторонним (bidirectional), и если не принимать это во внимание, то можно столкнуться с массой проблем и странностей.Для примера возьмем две простейшие сущности: пользователь и контакт. Очевидно, что каждый контакт связан с пользователем отношением многие к одному, а пользователь с контактами отношением один ко многим. Односторонние отношения Односторонним называется отношение, владельцем которого является только одна из двух сторон. Отсюда и название. Следует заметить, что при этом вторая сторона об этом отношении ничего не знает. HIbernate будет считать владельцем отношения ту сущность, в которой будет поставлена аннотация отношения. Давайте попробуем сделать владельцем отношения сторону контакта. При этом сущности будут выглядеть следующим образом.
@Entity
@Table(name = "contacts")
public class Contact {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String type;
@Column
private String data;
@ManyToOne
private User user;
// Конструктор по умолчанию, геттеры, сеттеры и т.д.
}
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String username;
// Конструктор по умолчанию, гетеры, сеттеры и т.д.
}
Если запустить этот код, то Hibernate создаст следующую структуру таблиц, которая выглядит для нас вполне привычно. Отношение между таблицами создается при помощи ссылочного поля user_id в таблице contacts.
create table contacts (
id bigint not null auto_increment,
data varchar(255),
type varchar(255),
user_id bigint,
primary key (id)
) engine=InnoDB;
create table users (
id bigint not null auto_increment,
email varchar(255),
password varchar(512) not null,
username varchar(128) not null,
primary key (id)
) engine=InnoDB
Но выбор сущности Contact в качестве стороны владельца отношений в данном случае не очень удачен. Очевидно, что нам чаще нужна информация обо всех контактах пользователя чем о том, какому пользователю принадлежит контакт. Попробуем сделать владельцем контакта сущность пользователя. Для этого убираем поле user из класса Contact и добавляем поле со списком контактов в класс User. Получаем следующий код.
@Entity
@Table(name = "contacts")
public class Contact {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String type;
@Column
private String data;
// Конструктор по умолчанию, геттеры, сеттеры и т.д.
}
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String username;
@OneToMany
private List<Contact> contacts;
// Конструктор по умолчанию, гетеры, сеттеры и т.д.
}
Теперь владельцем отношения является сущность пользователя, что более логично, но если запустить данный код и посмотреть на созданную Hibernate структуру таблиц, то мы столкнёмся с одной хорошо известной почти каждому кто использовал эту библиотеку проблемой.
create table contacts (
id bigint not null auto_increment,
data varchar(255),
type varchar(255),
primary key (id)
) engine=InnoDB;
create table users (
id bigint not null auto_increment,
email varchar(255),
password varchar(512) not null,
username varchar(128) not null,
primary key (id)
) engine=InnoDB;
create table users_contacts (
User_id bigint not null,
contacts_id bigint not null
) engine=InnoDB;
Чтобы связать сущности Hibernate создал дополнительную таблицу связи (join table) с именем users_contacts, хотя сущности вполне можно было бы связать через ссылочное поле в таблице contacts, как в предыдущем случае. Честно говоря, я не совсем понимаю, почему Hibernate поступает именно так. Буду рад, если кто-то поможет с этим разобраться в комментариях к статье. Проблему можно легко решить добавив аннотацию JoinColumn к полю contacts.
@OneToMany
@JoinColumn(name = "user_id")
private List<Contact> contacts;
При таких настройках связь будет проводиться при помощи колонки user_id в таблице contacts, а таблица связи создаваться не будет.Двусторонние отношенияУ двусторонних отношений помимо стороны - владельца (owning side) имеется ещё и противоположная сторона (inverse side). Т.е. обе стороны отношения обладают информацией о связи. Логично предположить, что из одностороннего отношения можно сделать двустороннее просто добавив поле и аннотацию в класс сущности противоположной стороны, но не все так просто. В чем именно тут проблема очень хорошо видно на примере отношения многие ко многим. Давайте создадим пример такого отношения между сущностями пользователя и роли этого пользователя.
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String username;
@ManyToMany
private List<Role> roles;
// Конструктор по умолчанию, гетеры, сеттеры и т.д.
}
@Entity
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String name;
@ManyToMany
private List<User> users;
// Конструктор по умолчанию, гетеры, сеттеры и т.д.
}
Запускаем код и смотрим на структуру таблиц. Помимо таблиц для пользователей и ролей Hibernate создаст две таблицы связи, хотя нам хватило бы и одной.
create table roles_users (
Role_id bigint not null,
users_id bigint not null
) engine=InnoDB;
create table users_roles (
User_id bigint not null,
roles_id bigint not null
) engine=InnoDB;
Дело в том, что вместо одного одностороннего отношения мы с вами сейчас создали два односторонних. Тоже самое произойдет и для отношения один ко многим. Чтобы Hibernate понял, что мы хотим создать именно одностороннее отношение нам нужно указать, какая из двух сторон является владельцем отношений, а какая сторона является обратной. Это делается при помощи атрибута mappedBy. Важно отметить, что указывается этот параметр в аннотации, которая находится на противоположной стороне отношения.Для отношения многие ко многим любая из сторон может быть владельцем. В случае с ролями и пользователями выберем сущность пользователя в качестве владельца. Для этого изменим описание поля users в классе Role следующим образом.
// значение атрибута mappedBy - имя поля связи в классе сущности-владельца отношений
@ManyToMany(mappedBy = "roles")
private List<User> users;
Теперь Hibernate создаст только одну таблицу связи users_roles.И напоследок давайте сделаем двусторонним отношение между пользователями и контактами. Следует отметить, что в отношении один ко многим стороной-владельцем может быть только сторона многих (many), поэтому атрибут mappedBy есть только в аннотации @OneToMany . В нашем случае владельцем отношения будет сторона контакта (класс Contact).
@Entity
@Table(name = "contacts")
public class Contact {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String type;
@Column
private String data;
@ManyToOne
private User user;
// Конструктор по умолчанию, геттеры, сеттеры и т.д.
}
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column
private String username;
@OneToMany(mappedBy = "user")
private List<Contact> contacts;
// Конструктор по умолчанию, гетеры, сеттеры и т.д.
}
Для такого кода Hibernate создаст привычную нам структуру из двух таблиц со ссылкой на пользователя в таблице контактов.На этом все на этот раз! Благодарю, что дочитали до конца и надеюсь, что статья была полезной! Разумеется, очень жду от вас обратную связь в виде голосов и комментариев! Возможно, будет продолжение ;
===========
Источник:
habr.com
===========
Похожие новости:
- [Python, Программирование] Как перестать беспокоиться и начать жить
- [Python, Программирование] 7 полезных расширений VS Code для Python-разработчиков (перевод)
- [.NET] .NET 5 + Source Generator = Javascript
- [Программирование, Assembler] Перевод числа в строку с помощью SIMD + FPU
- [Программирование, Kotlin, Старое железо] Пиксели, Excel, Kotlin и немного ностальгии…
- [Разработка веб-сайтов, JavaScript, TypeScript] TypeScript: Раскладываем tsconfig по полочкам. Часть 1
- [Программирование, Flutter] Работа с адаптивным программируемым интерфейсом APIs во Flutter (перевод)
- [Программирование, Разработка под Linux, Софт, IT-компании] Цветочные новости: разработчики ОС Fuchsia добавят поддержку запуска немодифицированных Linux-программ
- [Программирование микроконтроллеров, Разработка для интернета вещей, Разработка под Arduino, Компьютерное железо, DIY или Сделай сам] ESP32-C3: первое знакомство. Заменим ESP8266?
- [Системное администрирование, Программирование, Карьера в IT-индустрии, DevOps] Зачем IT-специалисты преподают на курсах и к чему готовиться, если решил стать спикером
Теги для поиска: #_programmirovanie (Программирование), #_java, #_java, #_hibernate, #_jpa, #_programmirovanie (
Программирование
), #_java
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 11:32
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Всем нам хорошо известен ответ на вопрос, какими могут быть отношения между сущностями в Hibernate и JPA. Вариантов всего четыре:
@Entity
@Table(name = "contacts") public class Contact { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String type; @Column private String data; @ManyToOne private User user; // Конструктор по умолчанию, геттеры, сеттеры и т.д. } @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String username; // Конструктор по умолчанию, гетеры, сеттеры и т.д. } create table contacts (
id bigint not null auto_increment, data varchar(255), type varchar(255), user_id bigint, primary key (id) ) engine=InnoDB; create table users ( id bigint not null auto_increment, email varchar(255), password varchar(512) not null, username varchar(128) not null, primary key (id) ) engine=InnoDB @Entity
@Table(name = "contacts") public class Contact { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String type; @Column private String data; // Конструктор по умолчанию, геттеры, сеттеры и т.д. } @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String username; @OneToMany private List<Contact> contacts; // Конструктор по умолчанию, гетеры, сеттеры и т.д. } create table contacts (
id bigint not null auto_increment, data varchar(255), type varchar(255), primary key (id) ) engine=InnoDB; create table users ( id bigint not null auto_increment, email varchar(255), password varchar(512) not null, username varchar(128) not null, primary key (id) ) engine=InnoDB; create table users_contacts ( User_id bigint not null, contacts_id bigint not null ) engine=InnoDB; @OneToMany
@JoinColumn(name = "user_id") private List<Contact> contacts; @Entity
@Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String username; @ManyToMany private List<Role> roles; // Конструктор по умолчанию, гетеры, сеттеры и т.д. } @Entity @Table(name = "roles") public class Role { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String name; @ManyToMany private List<User> users; // Конструктор по умолчанию, гетеры, сеттеры и т.д. } create table roles_users (
Role_id bigint not null, users_id bigint not null ) engine=InnoDB; create table users_roles ( User_id bigint not null, roles_id bigint not null ) engine=InnoDB; // значение атрибута mappedBy - имя поля связи в классе сущности-владельца отношений
@ManyToMany(mappedBy = "roles") private List<User> users; @Entity
@Table(name = "contacts") public class Contact { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String type; @Column private String data; @ManyToOne private User user; // Конструктор по умолчанию, геттеры, сеттеры и т.д. } @Entity @Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column private String username; @OneToMany(mappedBy = "user") private List<Contact> contacts; // Конструктор по умолчанию, гетеры, сеттеры и т.д. } =========== Источник: habr.com =========== Похожие новости:
Программирование ), #_java |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 11:32
Часовой пояс: UTC + 5