연관관계 매핑 기초

slee2·2022년 2월 28일
0

객체지향 설계의 목표는 자율적인 객체들의 협력 공동체를 만드는 것이다.

단방향 연관관계

여기서 책 추천을 한다.

객체지향의 사실과 오해(조영호)
오브젝트(조영호)

중요한 말은 우리가 JPA를 배우고 있지만, 결국 핵심은 객체지향적 설계이다.
객체지향이라는 근본적인 것이 어렵다. 이를 위해 위 책을 읽으면 어느정도 객체지향이 무엇인지 알게 되고 설계하는데 도움이 될 것이라는 뜻.

객체지향적이라는게 뭔지 알고 객체지향적으로 코드를 짜고 싶다면, ORM에 손이 가게 된다고 한다.

객체를 테이블에 맞춰서 모델링

대충 팀과 멤버는 1:N의 관계라는 뜻.

잘 보면 그냥 TEAM_ID를 참조하지 않고, 그냥 외래 키로 사용하는 것을 확인할 수 있다.

보면, Setter를 통해 직접 TEAM_ID를 주입시키는 것을 확인할 수 있다.



테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.
객체는 참조를 사용해서 연관된 객체를 찾는다.
테이블과 객체 사이에는 이런 큰 간격이 있다.

객체 지향적이 아니다.

객체 지향적 모델링

암튼 조회가 잘된다.

객체 지향적이다.

수정하면, 이후에 커밋시점에 더티체킹(변경감지)가 되어 수정할 수 있다.

양방향 연관관계와 연관관계의 주인

이전과 다르게 양방향은 1:N에서 1 부분에 N을 추가해준다.
그래서 1인 Team에 N인 Member가 추가된 것을 확인할 수 있다.

그러면 양방향이 좋냐 단방향이 좋냐 로 나누자면,
단뱡향이 웬만하면 좋다. 양방향은 나중에 가면 복잡해진다. 이와 관련된건 뒤에서 설명.

연관관계의 주인과 mappedBy

  • mappedBy = JPA의 멘탈붕괴 난이도
  • mappedBy는 처음에는 이해하기 어렵다.
  • 객체와 테이블간에 연관관계를 맺는 차이를 이해해야 한다.

차이

즉, 객체에서는 회원에서 팀으로 가는 연관관계 1개
팀에서 회원으로 가는 연관관계 1개로 해서
양방향이라고 우기는 거다.

테이블은 그냥 연관관계가 양방향으로 1개이다.

객체의 경우

class A {
	B b;
}

class B {
	A a;
}

이렇게 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

외래 키 하나로 두 테입르의 연관관계를 관리
MEMBER.TEAM_ID 외래키 하나로.

그러니까 둘 중 하나를 외래 키로 관리해야 한다.

여기서 연관관계의 주인이라는게 나온다.

양방향 매핑 규칙

  • 객체의 두 관계중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래 키를 관리(등록, 수정)
  • 주인이 아닌쪽은 읽기만 가능
  • 주인은 mappedBy 속성 사용X
  • 주인이 아니면 mappedBy 속성으로 주인 지정

진짜 매핑 - 주인
가짜 매핑 - 읽기만 가능

1 : N에서 1(Member)을 주인으로 정할수도 있다.
그런데 N(Team)을 그니까, Member에 있는 Team team을 주인으로 정하는 이유는, 여러가지가 있는데

먼저 자세히 이해하지는 못했는데 Team을 수정하는데, Member에 업데이터 쿼리가 나간다던지 여러 문제가 있기 때문에,
그냥 데이터베이스에서 FK를 가지는 곳을 주인으로 정한 것 같다.

쉽게 말해 N이 주인이다.
그러므로 주인이 아닌,
@oneToManymappedBy를 넣으면 된다.

양방향 매핑시 많이하는 실수

리스트는 무조건 읽기 전용이다.
읽기 전용에 값을 넣고 저장하려니 저장이 안된것이다.

이렇게 주인에 값을 설정하고 저장해야 한다.

프로젝트를 통해 많이 겪어본 것 같다.

그런데, 강사님이 정말 객체지향적으로 하기 위해서는
양쪽에 전부 값을 넣어주는 것이 맞다고 하신다.

문제는 어디서 생기냐면,
쉽게 말해 flush를 안한 상태에서 em.find로 값을 가져오려고 하면,
1차 캐시에서 조회된 값이 그대로 오기 때문에 List에 값이 없다.

즉, 데이터베이스에서 값을 가져오면 상관없는데,
커밋 전에 값을 꺼내오면 문제가 된다는 것.

근데 뭐 일반적으로
Transaction을 통해 flush가 되기 때문에 어... 문제는 없을 것 같긴 하다.

강사님은 양쪽에 값을 둘다 넣기 위해서
연관관계 메서드를 생성하라라고 하신다.

연관관계 메서드란,
Member에서

public void changeTeam(Team team) {
	this.team = team;
    team.getMembers().add(this);
}

이렇게 setTeam메서드에서 값을 주입시켜버리면, 팀을 세팅할때마다 자동으로 List에도 추가되니 문제될 것이 없다는 말씀이다.
Setter로 해도 되지만, 특별한 로직이 들어간다는 것을 보여주기 위해
changeTeam으로 메서드를 생성하는 것이다.
오 맞말추

Team에서는 이렇게

public void addMember(Member member) {
	member.setTeam(this);
    members.add(member);
}

하는데 또, 양쪽에 만드는건 문제가 발생할 수 있으므로, Member에 있는걸 지우고, Team에 addMember를 추가하는 방향으로 한다.
물론 Team에 있는걸 지우고 Member에 changeTeam을 이용해도 된다.
유동적으로 하면 된다는 뜻

양방향 매핑시에 무한 루프를 조심

예: toString(), lombok, JSON 생성 라이브러리

정리

  • 단방향 매핑만으로도 이미 연관관계 매핑은 완료
  • 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐
  • JPQL에서 역방향으로 탐색할 일이 많음
  • 단방향 매핑을 사용하고 양방향은 필요할 때 추가.
  • 연관관계의 주인은 외래 키의 위치를 기준으로 정하라.

0개의 댓글