1. Введение
В этом кратком руководстве мы кратко рассмотрим, как аннотацию @ManyToMany
можно использовать для указания этого типа отношений в Hibernate.
2. Типичный пример
Давайте начнем с простой диаграммы отношений сущностей, которая показывает связь «многие ко многим» между двумя сущностями , сотрудником
и проектом:
В этом сценарии любой конкретный сотрудник
может быть назначен на несколько проектов, и над проектом
может работать несколько сотрудников, что приводит к ассоциации «многие ко многим» между ними.
У нас есть таблица сотрудников с employee_id
в
качестве первичного ключа и таблица проекта с
project_id
в качестве первичного ключа. Здесь требуется таблица соединений employee_project для соединения обеих сторон.
3. Настройка базы данных
Предположим, у нас есть уже созданная база данных с именем spring_hibernate_many_to_many.
Нам также необходимо создать таблицы employee
и project
вместе с таблицей соединения employee_project
с employee_id
и project_id
в качестве внешних ключей:
CREATE TABLE `employee` (
`employee_id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(50) DEFAULT NULL,
`last_name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`employee_id`)
) ENGINE=InnoDB AUTO_INCREMENT=17 DEFAULT CHARSET=utf8;
CREATE TABLE `project` (
`project_id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(50) DEFAULT NULL,
PRIMARY KEY (`project_id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;
CREATE TABLE `employee_project` (
`employee_id` int(11) NOT NULL,
`project_id` int(11) NOT NULL,
PRIMARY KEY (`employee_id`,`project_id`),
KEY `project_id` (`project_id`),
CONSTRAINT `employee_project_ibfk_1`
FOREIGN KEY (`employee_id`) REFERENCES `employee` (`employee_id`),
CONSTRAINT `employee_project_ibfk_2`
FOREIGN KEY (`project_id`) REFERENCES `project` (`project_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
После настройки базы данных следующим шагом будет подготовка зависимостей Maven и конфигурации Hibernate. Для получения информации об этом, пожалуйста, обратитесь к статье Руководство по Hibernate4 с Spring
4. Классы моделей
Классы модели Employee
и Project
должны быть созданы с аннотациями JPA:
@Entity
@Table(name = "Employee")
public class Employee {
// ...
@ManyToMany(cascade = { CascadeType.ALL })
@JoinTable(
name = "Employee_Project",
joinColumns = { @JoinColumn(name = "employee_id") },
inverseJoinColumns = { @JoinColumn(name = "project_id") }
)
Set<Project> projects = new HashSet<>();
// standard constructor/getters/setters
}
@Entity
@Table(name = "Project")
public class Project {
// ...
@ManyToMany(mappedBy = "projects")
private Set<Employee> employees = new HashSet<>();
// standard constructors/getters/setters
}
Как мы видим, и класс Employee , и классы
Project
ссылаются друг на друга, а это означает, что связь между ними является двунаправленной.
Чтобы отобразить ассоциацию «многие ко многим», мы используем аннотации @ManyToMany
, @JoinTable
и @JoinColumn
. Давайте посмотрим на них поближе.
Аннотация @ManyToMany
используется в обоих классах для создания отношения «многие ко многим» между сущностями.
Эта ассоциация имеет две стороны, т.е. владеющую сторону и обратную сторону. В нашем примере стороной-владельцем является Employee
, поэтому таблица соединения указывается на стороне-владельце с помощью аннотации @JoinTable
в классе Employee .
@JoinTable используется для определения таблицы соединений/связей .
В данном случае это Employee_Project.
Аннотация @JoinColumn
используется для указания столбца соединения/связывания с основной таблицей. Здесь столбец соединения — employee_id
, а project_id
— обратный столбец соединения, поскольку Project
находится на обратной стороне отношения.
В классе Project атрибут
mappedBy
используется в аннотации @ManyToMany
, чтобы указать, что коллекция сотрудников
сопоставляется с коллекцией проектов
на стороне владельца.
5. Исполнение
Чтобы увидеть аннотацию «многие ко многим» в действии, мы можем написать следующий тест JUnit:
public class HibernateManyToManyAnnotationMainIntegrationTest {
private static SessionFactory sessionFactory;
private Session session;
//...
@Test
public void givenSession_whenRead_thenReturnsMtoMdata() {
prepareData();
@SuppressWarnings("unchecked")
List<Employee> employeeList = session.createQuery("FROM Employee").list();
@SuppressWarnings("unchecked")
List<Project> projectList = session.createQuery("FROM Project").list();
assertNotNull(employeeList);
assertNotNull(projectList);
assertEquals(2, employeeList.size());
assertEquals(2, projectList.size());
for(Employee employee : employeeList) {
assertNotNull(employee.getProjects());
assertEquals(2, employee.getProjects().size());
}
for(Project project : projectList) {
assertNotNull(project.getEmployees());
assertEquals(2, project.getEmployees().size());
}
}
private void prepareData() {
String[] employeeData = { "Peter Oven", "Allan Norman" };
String[] projectData = { "IT Project", "Networking Project" };
Set<Project> projects = new HashSet<Project>();
for (String proj : projectData) {
projects.add(new Project(proj));
}
for (String emp : employeeData) {
Employee employee = new Employee(emp.split(" ")[0], emp.split(" ")[1]);
employee.setProjects(projects);
for (Project proj : projects) {
proj.getEmployees().add(employee);
}
session.persist(employee);
}
}
//...
}
Мы можем видеть отношение «многие ко многим» между двумя сущностями, созданными в базе данных: таблицами employee
, project
и employee_project
с образцами данных, представляющими отношения.
6. Заключение
В этом руководстве мы увидели, как создавать сопоставления с использованием аннотаций Hibernate «многие ко многим», что является более удобным аналогом по сравнению с созданием файлов сопоставления XML.
Исходный код этого руководства можно найти на GitHub .