관계 Mapping
연관관계
JPA에서 연관관계는 영속 객체 간의 관계를 뜻한다
영속 객체인 엔티티 간의 연관 관계는 방향성을 가진다 (단방향, 양방향)
연관 관계는 관계의 다중성에 따라 1:1(일대일), 1:N(일대다), N:M(다대다) 관계로 구분한다
엔티티가 테이블과 매핑되는 것과 마찬가지로 엔티티 간의 관계는 테이블 간의 관계와 매핑된다
Student는 하나의 Major를 가진다
Major는 여러 Student에 속한다
테이블 관계 그대로 엔티티 정의
public class Student { //일부 코드
@Id
@GeneratedValue
private Long studentId;
private String name;
private String grade;
private Long majorId; // 테이블에 저장되는 형태 그대로 정의
}
// 전공 생성
Major major = new Major("Computer Science", "Engineering");
em.persist(major);
// 학생 생성
Student student = new Student("kim", "3");
student.setMajorId(major.getMajorId());
em.persist(student);
em.flush();
em.clear();
// 학생의 전공 찾기
Student foundStudent = em.find(Student.class, 1);
Major foundMajor = em.find(Major.class, foundStudent.getMajorId());
System.out.println(foundStudent);
System.out.println(foundMajor);
tx.commit();
테이블 연관관계 그대로 엔티티(필드 타입)를 구성
foundStudent로 찾아온 학생 정보에는 전공의 정보는 포함되지 않고 fk 값이 포함된다
학생의 전공을 찾기 위해서는 학생 객체의 majorId 필드를 이용해 DB를 한번 더 다녀와야 한다
majorId 필드를 Long 타입으로 지정하는 건 외래키를 단순히 값으로 처리하는 방식
즉 객체지향 설계에서 연관관계를 객체 간에 직접 맺어주는 것이 아니라 DB 값만 이용하는 방식으로 실제 객체들 간의 관계를 반영하지 못한다 (두 객체가 서로 연결되었다고 보기 어려움) 또한 지연로딩을 활용할 수 없음 (em.find())
Student 필드에 FK를 Major 객체로 지정
@ManyToOne// 관계구성
@JoinColumn(name = "MAJORID") // 외래키 지정
private Major major;
영속 객체 간의 관계는 방향성을 갖는다
Student 클래스가 Major 클래스를 참조하여 Student -> Major의 방향성을 갖게 된다
Student 객체는 참조하는 major를 통해 Major 인스턴스 객체에 접근할 수 있다
Student 클래스는 Major 클래스와 ManyToOne 관계가 된다 (N:1)
(반대 경우는 OneToMany)
// 전공 생성
Major major = new Major("Computer Science", "Engineering");
em.persist(major);
// 학생 생성
Student student = new Student("kim", "3");
student.setMajor(major);
em.persist(student);
em.flush();
em.clear();
// 학생의 전공 찾기
Student foundStudent = em.find(Student.class, 1);
System.out.println(foundStudent); // 학생 검색 시 major 에 대한 정보 함께 포함
지연로딩 설정
@ManyToOne(fetch = FetchType.LAZY)// 관계구성
@JoinColumn(name = "MAJORID") // 외래키 지정
private Major major;
// 전공 생성
Major major = new Major("Computer Science", "Engineering");
em.persist(major);
// 학생 생성
Student student = new Student("kim", "3");
student.setMajor(major);
em.persist(student);
em.flush();
em.clear();
// 학생의 전공 찾기
Student foundStudent = em.find(Student.class, 1);
foundStudent.getName(); // Student 에 대한 select 발생
System.out.println(foundStudent.getMajor().getCategory()); // Major 에 대한 select 밠생
Student 필드에 FK를 Major 객체로 저장
Student 객체에 Major 객체 자체가 포함이 된다
학생의 전공을 찾기 위해 불필요한 find를 하지 않아도 되며 실제 객체의 관계가 반영되었음을 알 수 있다
지연로딩 설정
fetch = FetchType.LAZY를 적용한 뒤에 똑같이 student를 find할 때 Student에 대한 조회 쿼리가 발생하고 이후 Major를 확인할 때 Major 조회 쿼리가 발생한다
지연로딩을 적용하면 해당 객체가 필요한 순간에 쿼리를 발생한다 (불필요한 쿼리는 보내지 않을 수 있다) 반대로 EAGER 옵션을 통해 즉시로딩도 설정할 수 있다