Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Отношение один-ко-многим (One-to-Many) в JPA
Отношение один-ко-многим — это классическая связь, когда одна сущность может быть связана с несколькими другими сущностями. Например, одна компания имеет много сотрудников.
Базовая структура
1. Сущность на стороне один (родитель)
@Entity
@Table(name = "company")
public class Company {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String name;
@OneToMany(mappedBy = "company",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY)
private List<Employee> employees = new ArrayList<>();
}
2. Сущность на стороне многим (потомок)
@Entity
@Table(name = "employee")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "company_id", nullable = false)
private Company company;
}
База данных
CREATE TABLE company (
id UUID PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
CREATE TABLE employee (
id UUID PRIMARY KEY,
name VARCHAR(255) NOT NULL,
company_id UUID NOT NULL REFERENCES company(id) ON DELETE CASCADE
);
Параметры @OneToMany
mappedBy — указывает на поле в другом классе, которое владеет связью:
@OneToMany(mappedBy = "company")
cascade — определяет, какие операции распространяются на потомков:
CascadeType.PERSIST— сохранение компании сохраняет сотрудниковCascadeType.MERGE— обновление компании обновляет сотрудниковCascadeType.REMOVE— удаление компании удаляет сотрудниковCascadeType.ALL— все операции
orphanRemoval — удалять потомков, если они удалены из списка:
@OneToMany(mappedBy = "company", orphanRemoval = true)
fetch — стратегия загрузки:
FetchType.EAGER— загружать сразуFetchType.LAZY— загружать по запросу (рекомендуется)
Полный пример: Университет с курсами
@Entity
@Table(name = "university")
public class University {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String name;
private String city;
@OneToMany(mappedBy = "university",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY)
private List<Course> courses = new ArrayList<>();
public void addCourse(Course course) {
courses.add(course);
course.setUniversity(this);
}
public void removeCourse(Course course) {
courses.remove(course);
course.setUniversity(null);
}
}
@Entity
@Table(name = "course")
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
private UUID id;
private String title;
private int credits;
@ManyToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "university_id")
private University university;
}
Использование в коде
Сохранение
University uni = new University();
uni.setName("MIT");
uni.setCity("Boston");
Course course1 = new Course();
course1.setTitle("Java Advanced");
course1.setCredits(3);
uni.addCourse(course1);
Course course2 = new Course();
course2.setTitle("Spring Boot");
course2.setCredits(4);
uni.addCourse(course2);
universityRepository.save(uni);
// Все курсы сохраняются автоматически благодаря cascade
Загрузка и использование
University uni = universityRepository.findById(id).orElseThrow();
List<Course> courses = uni.getCourses(); // Загружаются при обращении (LAZY)
for (Course course : courses) {
System.out.println(course.getTitle());
}
Удаление потомка
University uni = universityRepository.findById(id).orElseThrow();
Course course = uni.getCourses().get(0);
uni.removeCourse(course);
universityRepository.save(uni);
// Курс удаляется из БД благодаря orphanRemoval = true
Лучшие практики
1. Всегда инициализируй коллекции
private List<Employee> employees = new ArrayList<>();
2. Добавляй helper методы
public void addEmployee(Employee emp) {
employees.add(emp);
emp.setCompany(this);
}
public void removeEmployee(Employee emp) {
employees.remove(emp);
emp.setCompany(null);
}
3. Используй FetchType.LAZY по умолчанию Это избегает N+1 проблемы и улучшает производительность.
4. Используй orphanRemoval для зависимых сущностей Если потомок не может существовать без родителя, удаляй его автоматически.
5. Будь осторожен с CascadeType.REMOVE
// Это опасно — удаление компании удалит всех сотрудников!
@OneToMany(mappedBy = "company", cascade = CascadeType.REMOVE)
6. Используй JPQL для больших наборов данных
@Query("SELECT e FROM Employee e WHERE e.company.id = :companyId")
List<Employee> findByCompanyId(@Param("companyId") UUID companyId);
Различие между cascade и orphanRemoval
| Операция | cascade=REMOVE | orphanRemoval=true |
|---|---|---|
| Удаление родителя | Удаляет потомков | Удаляет потомков |
| Удаление потомка из списка | Ничего | Удаляет потомка |
| Отсоединение потомка | Ничего | Удаляет потомка |
Отношение один-ко-многим — фундамент большинства приложений, поэтому важно правильно его настроить.