← Назад к вопросам

Как сделать отношение один к многим

2.0 Middle🔥 121 комментариев
#Другое

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Отношение один-ко-многим (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=REMOVEorphanRemoval=true
Удаление родителяУдаляет потомковУдаляет потомков
Удаление потомка из спискаНичегоУдаляет потомка
Отсоединение потомкаНичегоУдаляет потомка

Отношение один-ко-многим — фундамент большинства приложений, поэтому важно правильно его настроить.