연관관계 매핑 기초

Jeongyeon Kim·2023년 1월 18일
0

JPA

목록 보기
4/11
post-thumbnail

연관관계 매핑 핵심 키워드

  • 방향(Direction): [단방향, 양방향], 방향은 객체관계에만 존재하고 테이블 관계는 항상 양방향
  • 다중성(Multiplicity): [다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)]
  • 연관관계의 주인(Owner): 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 함.

1. 단방향 연관관계

❗️객체 연관관계와 테이블 연관관계의 가장 큰 차이

  • 참조를 통한 연관관계는 언제나 단방향
  • 객체 간에 연관관계를 양방향으로 만들고 싶으면 반대쪽에도 필드를 추가해 연관관계를 하나 더 만들어야 함 ➡️ 양방향 관계가 아니라 서로 다른 단방향 관계 2개
  • 테이블은 외래 키 하나로 양방향으로 조인할 수 있음

객체 연관관계 vs 테이블 연관관계

  • 객체는 참조(주소)로 연관관계를 맺음(객체 그래프 탐색)
  • 테이블은 외래 키로 연관관계를 맺음(조인)

@JoinColumn

@JoinColumn: 외래 키를 매핑할 때 사용

  • 속성
    • name: 매핑할 외래 키 이름
    • referencedColumnName: 외래 키가 참조하는 대상 테이블의 컬럼명
    • foreignKey(DDL): 외래 키 제약조건ㅇ르 직접 지정할 수 있음. 테이블 생성할 때만 사용
    • unique, nullable, insertable, updatable, columnDefinition, table: @Column 속성과 같음

@ManyToOne

@ManyToOne: 다대일 관계에서 사용

  • 속성
    • optional: false로 설정하면 연관된 엔티티가 항상 있어야 함
    • fetch: 글로벌 페치 전략 설정
    • cascade: 영속성 전이 기능 사용
    • targetEntity: 연관된 엔티티의 타입 정보 설정

2. 연관관계 사용

1. 저장

JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 함

2. 조회

  • 객체 그래프 탐색(객체 연관관계를 사용한 조회)
  • 객체지향 쿼리 사용(JPQL)

3. 수정

참조하는 대상만 변경하면 나머지는 JPA가 자동으로 처리(변경 감지)

4. 연관된 엔티티 삭제

연관된 엔티티를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 함

3. 양방향 연관관계

  • 일대다 관계는 여러 건과 연관관계를 맺을 수 있으므로 컬렉션을 사용해야 함.
  • @OneToMany로 일대다 관계 매핑 ➡️ mappedBy 속성은 양방향 매핑일 때 사용
  • 양방향 매핑 시에는 무한 루프에 빠지지 않게 조심해야 함.
    ex) Member.toString()에서 getTeam()을 호출하고 Team.getString()에서 getMember()를 호출하면 무한 루프에 빠질 수 있음.

4.연관관계의 주인

연관관계의 주인: 두 객체 연관관계 중 하나를 정해서 테이블의 외래 키 관리
✅ 연관관계의 주인은 외래 키가 있는 곳!

  • 연관관계의 주인만이 데이터베이스 연관관계와 매핑되고 외래 키를 관리(등록, 수정, 삭제)
  • 주인이 아닌 쪽은 읽기만 할 수 있음
  • 주인이 아닌 족에서 mappedBy 속성을 사용해서 속성의 값으로 연관관계의 주인 지정

5. 양방향 연관관계 저장

  • 주인이 아닌 방향은 값을 설정하지 않아도 데이터베이스에 외래 키 값이 정상 입력됨.
// member와 team은 일대다(1:N)
team.getMembers().add(member1); 	// 무시(연관관계의 주인이 아님)
team.getMembers().add(member2);		// 무시(연관관계의 주인이 아님)

member1.setTeam(team1);		// 연관관계 설정(연관관계의 주인)
member2.setTeam(team1);		// 연관관계 설정(연관관계의 주인)

6. 양방향 연관관계의 주의점

객체 관점에서 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전

public void testORM_양방향() {
	// 팀1 저장
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);
    
    Member member1 = new Member("member1", "회원1");
    
    // 양방향 연관관계 설정
    member1.setTeam(team1);
    team1.getMembers().add(member1);
    em.persist(member1);
    
    Member member2 = new Member("member2", "회원2");
    
    // 양방향 연관관계 설정
    member2.setTeam(team1);
    team1.getMembers().add(member2);
    em.persist(member2);
}

연관관계 편의 메소드

  • 한 번에 양방향 관계를 설정하는 메소드
public class Member {
	
	private Team team;
    
    public void setTeam(Team team) {
    	this.team = team;
        team.getMembers().add(this);
    }
    ...
}
public void testORM_양방향_리팩토링() {
	
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);
    
    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1);		// 양방향 설정
    em.persist(member1);
    
    Member member2 = new Member("member2", "회원2");
    member2.setTeam(team1);		// 양방향 설정
    em.persist(member2);
}

연관관계 편의 메소드 작성 시 주의사항

  • 연관관계를 변경할 때는 기존 팀이 있으면 기존 팀과 회원의 연관관계를 삭제하는 코드를 추가해야 함.
public void setTeam(Team team) {
	
    // 기존 팀과의 관계를 제거
    if (this.team != null) {
    	this.team.getMembers().remove(this);
    }
    
    this.team = team;
    team.getMembers().add(this);
}
profile
Backend Developer👩🏻‍💻

0개의 댓글