Ceci est une ancienne révision du document !
Relations JPA
Les Types de Relations
@OneToMany / @ManyToOne
Cas d'usage : Un auteur a plusieurs livres, un livre a un seul auteur.
Configuration Unidirectionnelle (❌ Rarement recommandée)
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany
@JoinColumn(name = "author_id") // Crée une FK dans Book
private List<Book> books = new ArrayList<>();
}
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Pas de référence à Author
}
Problème : Génère des UPDATE supplémentaires !
Lancés automatiquement par Hibernate pour mettre à jour la clé étrangère.
Configuration Bidirectionnelle (✅ Recommandée)
@Entity
public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(
mappedBy = "author", // Référence au champ dans Book
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY // Par défaut
)
private List<Book> books = new ArrayList<>();
// Méthodes helper pour synchroniser les deux côtés
public void addBook(Book book) {
books.add(book);
book.setAuthor(this);
}
public void removeBook(Book book) {
books.remove(book);
book.setAuthor(null);
}
}
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(
fetch = FetchType.LAZY, // Recommandé
optional = false // NOT NULL en base
)
@JoinColumn(name = "author_id") // Nom de la FK
private Author author;
}
Le côté @ManyToOne est TOUJOURS le propriétaire (owner) de la relation.
Toutes les modifications portant sur cette relation devront se faire côté owner.
@ManyToMany
Cas d'usage : Un étudiant suit plusieurs cours, un cours a plusieurs étudiants.
Configuration Unidirectionnelle
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// Pas de référence à Student
}
Configuration Bidirectionnelle (✅ Recommandée)
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(
cascade = {CascadeType.PERSIST, CascadeType.MERGE},
fetch = FetchType.LAZY
)
@JoinTable(
name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id")
)
private Set<Course> courses = new HashSet<>();
public void enrollCourse(Course course) {
courses.add(course);
course.getStudents().add(this);
}
public void unenrollCourse(Course course) {
courses.remove(course);
course.getStudents().remove(this);
}
}
@Entity
public class Course {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToMany(mappedBy = "courses")
private Set<Student> students = new HashSet<>();
}
ManyToMany avec attributs
Ce n'est pas vraiment une ManyToMany…
Avec EmbeddedId
@Entity
public class Enrollment {
@EmbeddedId
private EnrollmentId id;
@ManyToOne
@MapsId("studentId")
private Student student;
@ManyToOne
@MapsId("courseId")
private Course course;
private LocalDate enrollmentDate;
private Integer grade;
}
@Embeddable
public class EnrollmentId implements Serializable {
private Long studentId;
private Long courseId;
// equals() et hashCode()
}
@Entity
public class Student {
@OneToMany(mappedBy = "student", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Enrollment> enrollments = new HashSet<>();
}
@Entity
public class Course {
@OneToMany(mappedBy = "course", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Enrollment> enrollments = new HashSet<>();
}
Sans , mais avec Id supplémentaire
@Entity
@Table(
name = "enrollment",
indexes = {
@Index(
name = "idx_enrollment_pk",
columnList = "student_id, course_id",
unique = true
)
}
)
public class Enrollment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id; // ← Clé primaire auto-générée simple
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "student_id", nullable = false)
private Student student;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "course_id", nullable = false)
private Course course;
private Integer grade;
private LocalDate enrollmentDate;
// Constructeurs
// Getters/Setters
// equals et hashCode
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Enrollment)) return false;
Enrollment that = (Enrollment) o;
return id != null && id.equals(that.getId());
}
@Override
public int hashCode() {
return getClass().hashCode();
}
@Override
public String toString() {
return "Enrollment{" +
"id=" + id +
", student=" + (student != null ? student.getName() : "null") +
", course=" + (course != null ? course.getName() : "null") +
", grade=" + grade +
", enrollmentDate=" + enrollmentDate +
'}';
}
}