[JPA] 섹션 5 : 연관관계 매핑 기초

헌치·2022년 7월 11일
0

JPA

목록 보기
4/4

노션 정리


5-1 용어


  • 방향(Direction): 단방향, 양방향
  • 다중성(Multiplicity): 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)
  • 연관관계의 주인(Owner): 객체 양방향 연관관계는 관리 주인이 필요

5-2 연관관계가 필요한 이유


@Entity
public class Member {
    @Id
    private Long id;

    @Column(name = "USERNAME")
    private String name;

    @Column(name = "TEAM_ID")
    private Long teamId;
}

@Entity
public class Team {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
}
//조회
Member findMember = em.find(Member.class, member.getId());
//연관관계가 없음
Team findTeam = em.find(Team.class, team.getId());
  • 테이블 : 외래키 조인 vs 객체 : 참조
  • 객체를 테이블에 맞춰 모델링
    • 식별자로 다시 조회해야 한다
  • 연관관계 탐색
    • 객체 그래프 탐색 : 객체참조를 사용해 연관관계를 탐색
    • 조인 : DB외래 키를 사용해 연관관계를 탐색

5-3 단방향 연관관계


@Entity
public class Member {
    @Id
    private Long id;

    @Column(name = "USERNAME")
    private String name;

    private int age;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}
  • @JoinColumn(name = "TEAM_ID")
    • 외래 키를 매핑할 때 사용한다.
    • name 속성에는 매핑할 외래 키 이름을 지정한다.
    • 회원과 팀 테이블은 TEAM_ID 외래 키로 연관관계를 맺으므로 이 값을 지정하면 된다.
    • 생략 시 기본값 : 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명
      • 여기서는 team_TEAM_ID
public class MainJPA {
    public static void main(String[] args) {
        //팀 저장
        Team team = new Team("TeamA");
        em.persist(team);

        //회원 저장
        Member member = new Member("member1", team); //단방향 연관관계 설정, 참조 저장
        em.persist(member);

        //조회
				Long id = member.getId();
        Member findMember = em.find(Member.class, id);

        //참조를 사용해서 연관관계 조회
        Team findTeam = findMember.getTeam();

        // 새로운 팀B
        Team teamB = new Team("TeamB");
        em.persist(teamB);

        // 회원1에 새로운 팀B 설정
        member.setTeam(teamB);
		}
}

5-4 양방향 연관관계


@Entity
public class Member {
    @Id
    private Long id;

    @Column(name = "USERNAME")
    private String name;
    private int age;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

@Entity
public class Team {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @OneToMany(mappedBy = "team")
    List<Member> members = new ArrayList<Member>();
}

5-5 연관관계의 주인과 mappedBy


@OneToMany(mappedBy = "team")

  • 객체 : 사실 2개의 단방향 연관관계
  • 테이블 : PK, FK를 통해 양방향 연관관계

1) 딜레마~!

Member 객체 자체와 테이블 매핑 vs Team 속 members 리스트를 통해 테이블 매핑

  • 만약 Member 객체는 그대로 인데 members 리스트는 빈 리스트이면???
  • 둘중 뭐로 매핑할지 정해야 함

2) 연관관계의 주인(Owner)

위의 두 관계 중 하나만 연관관계 주인으로 설정

a. 주인

외래키가 있는 쪽이 주인!!!(team)

  • 1대n 관계에서 n 쪽
  • 외래키 관리(등록, 수정)
  • mappedBy 속성 사용X

b. 비 주인

주인의 반대편, PK 있는 쪽(members)

  • 1대n 관계에서 1 쪽
  • 수정 불가, 읽는 것만 가능
  • mappedBy 속성 사용!
    • @OneToMany(mappedBy = "team")

5-6 자주 하는 실수


1) 양방향 객체 설정 필수

class Main {
    public static void main(String[] args) {
        Team team = new Team("TeamA");
        em.persist(team);

        // 1. 역방향(주인이 아닌 방향)만 연관관계 설정
        Member member = new Member("member1");
        team.getMembers().add(member);
        em.persist(member);
        // 오류!!!

        // 2. 연관관계 주인 방향도 값 설정
        Member member = new Member("member1", team);
        team.getMembers().add(member);
        em.persist(member);
        // 정상 작동!!!
    }
}
  • 주인값, 비 주인값(리스트) 꼭 입력해야 함
  • 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정하자!

2) 연관관계 편의 메서드

@Entity
public class Member {
    //...
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    public void changeTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);// !!!!
    }
}
  • Team.addMembers(team) 형태로 만들기도 함
    • 둘중 하나 정해서 쓰면 됨
    • 두 메소드 다 있으면 무한루프 우려... 하나만 정하기(컨벤션)
  • 주의사항
    • 연관관계 변경 시 기존 연관관계가 있으면, 해당 관계를 삭제하는 코드를 추가해야 함!!

3) 무한루프

a. toString()

member{team{member{team{...}}}}
  • 무한루프 형태
  • JSON 변환시 자주 발생
    • JSON 관련 라이브러리에서 제공하는 어노테이션/기능을 써 해결 가능
  • lombok에서는 toString() 쓰지마라!

b. 이외

  • 컨트롤러에서 entity를 반환하는 대신 DTO 를 사용해라

참고자료

  • [책, 강의] 자바 ORM 표준 JPA 프로그래밍(김영한)
profile
🌱 함께 자라는 중입니다 🚀 rerub0831@gmail.com

0개의 댓글