JPA 강의 스터디 3 🧶

송현진·2023년 5월 13일
0

Jpa

목록 보기
5/9
post-thumbnail
post-custom-banner

연관관계 매핑 기초

단방향 연관관계

FK -> 외래키

두 테이블을 연결하는 데 사용되는 키
자식 테이블 - 외래키가 포함된 테이블
부모 테이블 - 외래키를 제공하는 테이블

  • 연관관계 필요 이유
    • '객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다' - 조영호(객체지향의 사실과 오해)
    • 객체를 테이블에 맞추어 모델링 -> 객체 지향 모델링(객체 연관관계 사용)
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);  // PK 값이 세팅된 상태
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeamId(team.getId()); // 외래키 식별자를 직접 다룸
em.persist(member);

객체를 테이블에 맞춰 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.

객체 지향 모델링

//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);  // PK 값이 세팅된 상태
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); // 단방향 연관관계 설정, 참조 저장
em.persist(member);
-------------------------------------------
// 조회
Member findMember = em.find(Member.class, member.getId());
// 참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();

양방향 연관관계

매핑

  • 다대일
@ManyToOne
@JoinColumn(name = "team_id")
private Team team;
  • 일대다
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<>();
  • 역방향 조회
Team findTeam = em.find(Team.class, team.getId());

int memberSize = findTeam.getMembers().size();

객체의 양방향 관계

  • 객체는 사실 서로 다른 단방향 관계 2개이다.

테이블의 양방향 관계

  • 테이블은 외래키 하나로 두 테이블의 연관관계 관리
select * from member m
join team t on m.team_id = t.team_id;

select * from team t
join member m on t.team_id = m.team_id;

⭐ 연관관계 주인(Owner)

객체 양방향 연관관계는 관리대상이 필요!(C언어의 포인터같은?)

'데이터베이스 테이블'은 '외래키 하나'로 양방향으로 조회할 수 있다.

  • @ManyToOne - 다 대 일
  • @OneToMany - 일 대 다
    • mappedBy ="이름" 이름은 반대쪽 매핑의 필드이름 값으로 준다.

양방향 매핑 규칙

  • 객체의 두 관계중 하나를 연관관계 주인으로 지정(다)
  • 주인이 아닌 쪽은 읽기만 가능하다
  • 연관관계 주인만이 외래키를 관리(등록, 수정)
  • 많이하는 실수
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");
//역방향(주인이 아닌 방향)만 연관관계 설정
// 주인이 아니기 때문에 null 값이 나올 수 있다.
team.getMembers().add(member);
em.persist(member)

옳은 것(순수한 객체 관리를 고려하면 항상 양쪽 값 다 입력해야 함)

Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setName("member1");
team.getMembers().add(member); 

//연관관계의 주인에 값 설정
member.setTeam(team);
em.persist(member);

💡단방향 매핑만으로도 이미 연관관계 매핑완료
양방향은 조회(객체 그래프 탐색) 기능 추가된 것
JPQL에서 역방향 탐색할 일 많음
단방향 매핑 잘하고 양방향은 필요할 때 추가(테이블 영향 X)

다양한 연관관계 매핑

연관관계 매핑 시 고려사항 3가지

1. 다중성

  • 다대일 : @ManyToOne
  • 일대다 : @OneToMany
  • 일대일 : @OneToOne
  • 다대다 : @ManyToMany - 실무에서 사용 X

2. 단방향, 양방향

  • 테이블
    • 외래키 하나로 양쪽 Join 가능
    • 방향이란 개념 X
  • 객체
    • 참조용 필드를 가지고 있는 객체만 연관된 객체를 조회할 수 있다.
    • 한쪽만 참조 - '단방향', 서로 참조 '양방향'
    • 단방향 2개로 이어짐

3. 연관관계의 주인

  • 테이블 2개 중 외래키 관리할 곳 지정해야 함
  • 연관관계의 주인 - 외래키를 관리하는 참조
  • 주인의 반대편
    • 외래키에 영향 안줌
    • 단순 조회만 가능

💡객체 양방향 관계에서 연관관계의 주인은 항상 '' 쪽이다.

다대일 [N:1]

  • 가장 많이 사용하는 연관관계
  • 관계형 DB는 항상 ''쪽에 외래키가 들어가는 설계가 맞다
    단방향
@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id;

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

    @ManyToOne   // Member와 Team의 관계 : 다대일 단방향
    @JoinColumn(name = "TEAM_ID") //Team team이 조인(매핑)하는 컬럼은 TEAM_ID이다(통상적 기본키를 받아옴)
    private Team team;

양방향

@Entity
public class Team {

    @Id
    @GeneratedValue
    @Column(name = "Member_ID")
    private Long id;
    private String username;

    @OneToMany(mappedBy = "team") //1대다 매핑에서 반대편 사이드 Member의 team이 걸려있다.
    private List<Member> members = new ArrayList<Member>(); //단순히 조회만 가능하다

    @Column(name = "TEAM_ID")
    private Long teamID;

일대다 [1:N]

단방향

  • 일(1)이 연관관계 주인
  • 테이블 일대다 관계는 항상 다(N)쪽에 외래키 존재
  • 객체와 테이블의 차이 때문에 반대편 테이블의 외래키를 관리하는 특이한 구조
  • @JoinColumn을 꼭 사용. 그렇지 않을 시 중간 테이블 하나 추가됨
  • ORM 보다는 DB에 설계방향을 맞추어서 유지보수하기 쉽게한다.
  • 단점
    • 엔티티가 관리하는 외래키가 다른 테이블에 존재
    • 외래키를 넣을 때 추가로 UPDATE SQL 실행
@Entity
public class Team {

    @Id
    @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;
    private String username;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();

양방향

  • @JoinColumn(insertable=false, updatable=false) 사용(읽기전용필드)
  • 다대일로 쓰자~!
@Entity
public class Team {
...
    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<>();
-----------------------------------------
@Entity
public class Member {
...
    @ManyToOne
    @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false) //읽기전용
    private Team team;

    public void changeTeam(Team team) { //연관관계 편의 메서드
        this.team = team;
        team.getMembers().add(this);
    }

일대일 [1:1]

  • 테이블 주 테이블 대상 테이블 둘 중 하나 아무데나 외래키 선택 가능
  • 외래키에 DB 유니크(UNI) 제약조건 추가
  • 주 테이블 외래키(객체지향)
    • 단방향
      • @ManyToOne 단방향과 유사
    • 양방향
      • 다대일 양방향 매핑 처럼 외래키가 있는 곳이 연관관계의 주인
    • 장점 : 주 테이블만 조회해도 대상 테이블 데이터 확인 가능
    • 단점 : 값이 없으면 외래키에 null 허용
  • 대상 테이블 외래키(전통)
    • 단방향
      • JPA 지원 X
    • 양방향
      • 다대일 양방향 매핑 처럼 외래키가 있는 곳이 연관관계의 주인
    • 장점 : 주 테이블과 대상 테이블을 일대일 관계에서 일대다 변경 시 유지보수 용이
    • 단점 : 프록시 기능 한계로 지연 로딩 설정해도 항상 즉시 로딩됨

다대다 [N:M]

  • 테이블 2개로 다대다 관계 표현못함
  • 연결 테이블 추가해 일대다, 다대일로 표현
  • @ManyToMany사용
  • @JoinTable 연결 테이블 지정
    • 한계
      • 실무 사용 X
      • 연결 테이블이 단순 연결만 하고 끝나지 않음
    • 극복
      • 연결 테이블용 엔티티로 추가
      • @ManyToMany -> @OneToMany, @ManyToOne
profile
개발자가 되고 싶은 취준생
post-custom-banner

0개의 댓글