JPA 5th Step

최보현·2022년 9월 3일
0

JPA

목록 보기
4/10
post-thumbnail

자바 ORM 표준 JPA 프로그래밍 - 기본편 - sec05
출처 : JPA 기본편

나는 이번 강의를 통해 느꼈다. 데이터베이스 이 망할놈.

연관관계 매핑 기초

궁극적인 목표 : 객체의 참조와 테이블의 외래 키를 매핑하자!

알고가면 좋은 친구들
1. 방향 : 단방향, 양방향
2. 다중성 : 다대일, 일대다, 일대일, 다대다
3. 연관관계의 주인 : 객체 양방향 연관관계는 관리 주인이 필요

연관관계가 필요한 이유

객체 지향 설계의 목표는 자율적인 객체들의 **협력 공동체**를 만드는 것
우선, 객체를 테이블에 맞추어 모델링 할 때(연관관계가 없는 객체)

이 상황을 그대로 코드로 작성을 해보면

@Entity
public class Member {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

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

    @Column(name = "TEAM_ID")
    private Long teamId;
@Entity
public class Team {

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


그래서 이제 이거를 가지고 한 번 멤버를 저장해보고자 할 때

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

Member member = new Member();
member.setUsername("member1");
member.setTeamId(team.getId); //이 부분이 객체지향스럽지 않음
em.persist(member);


그리고 얘를 조회할 때는 always 귀찮은 일을 반복하게 됨 => 연관관계가 없으니깐 일일이 데베한테 물어와서 가져오고 무한 루트

Member findMember = em.find(Member.class, member.getId());

Long findTeamId = findMember.getTeamId();
Team findTeam = em.find(Team.class, findTeamId);

즉, 결론을 이야기 하자면.
객체를 테이블에 맞추어 데이터 중심으로 모델링을 하면, 협력관계를 만들 수 없음
WHY?!!!!!
1. 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾음
2. 반면, 객체는 참조를 사용해서 연관된 객체를 찾음
=> 거의 뭐 그냥 따로 놀자는 거지 뭐~

단방향 연관관계

그럼 정말 객체지향스럽게 만들어 보자

이전과 달라진 점? 팀의 id값을 가져오지 않고 팀의 참조값을 가져왔음
코드로 작성해보자면 팀 id를 가져오는 부분이 굉장히 달라지지

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

    @ManyToOne //멤버 입장에서는 many 그리고 team은 one
    @JoinColumn(name = "TEAM_ID") //위의 관계일때 조인할 칼럼명을 적어줌
    private Team team;

저장하는 부분도 달라짐!

Member member = new Member();
member.setUsername("member1");
member.setTeam(team); //바로 team을 넣어버리는 거지
//단방향 연관관계가 설정되고, 참조를 저장하는 과정

조회도 훨씬 간단해 지지!

Member findMember = em.find(Member.class, member.getId());

Team findTeam = findMember.getTeam(); //바로 id를 끄집어 내줌

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

위의 코드로 하면 멤버에서 팀은 가져올 수 있지만, 팀에서는 멤버를 가져올 수 없음
그래서 이걸 가능하게 만들어보자! => 양방향 매핑!

테이블의 연관관계는 외래키 하나로 양방향이 다 있는 거임!
솔직히 말해서, 테이블의 연관관계에 입장에서 방향이라는 개념이 없음
왜? 그냥 외래키 하나가 있으면 양쪽을 다 알 수 있음
이제 코드로 작성해보자!

@Entity
public class Team {

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

    @OneToMany(mappedBy = "team") //무엇으로 매핑되어 있는지 보여줌
    //team은 변수명, 즉 나의 반대편 사이드의 무엇과 걸려있는지 작성
    private List<Member> members = new ArrayList<>();
    //ArrayList로 초기화를 해두면 add할 때 null point가 안 뜸

mappedby

객체와 테이블 간에 연관관계를 맺는 차이를 이해하자
그 차이가 무엇이죠?

즉, 객체의 양방향 관계는 사실 양방향이 아니라 서로 다른 단방향 관계 2개
=> 객체를 양방향으로 참조하려면 단방향 연관관계를 2개를 만들어야 함!
반대로, 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리!
SELECT * FROM MEMBER M JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID

그래서 객체는 서로 다른 단방향 관계를 2개를 생성하는 만큼, 둘 중 하나로 외래 키를 관리해야 함!

만약에, 우리가 팀을 바꾸거나 멤버를 바꾸고자 할 때, Member에 있는 team을 바꿔야 할 지, 아니면 Team에 있는 members를 바꿔야 할 지 딜레마에 빠지게 됨
=> 그래서 우리는 주인을 정해야 함

연관관계 주인

객체의 두 관계 중 하나를 연관관계의 주인으로 지정해야 함
연관관계의 주인만이 외래 키를 관리(등록 수정)
<-> 주인이 아닌 쪽은 읽기만 가능
주인은 mappedBy 속성 사용 ❌(mappedBy 뜻 자체가 나 무언가에 의해 매핑 당했어라는 뜻이니깐 참고) <-> 주인이 아니면 mappedBy를 통해 주인을 지정

그럼 누구를 주인으로 해야하는 가

외래 키가 있는 곳을 주인을 하자!
우리가 양방향을 매핑할 때 참고한 사진을 보면, 외래키가 MEMBER 테이블에 들어가 있음 => 즉 객체를 설계할 때도 Member객체에 team을 주인으로 설정해야 함

데이터베이스에 입장으로 볼때, 외래키가 있는 곳이 무조건 '다/N', 아닌 곳은 '일'
=> N쪽이 무조건 연관관계의 주인이 되는 겨!

양방향 매핑시 주의할 점

연관관계의 주인에 값을 입력하자

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

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

DB로 갔을 때의 결과는 TEAM_ID가 null값이 나옴
왜 그럴까?
team.getMembers()부분은 mappedby속성을 사용하는 즉 주인이 아닌 곳으로 읽기 전용임! 그래서 여기에 값을 넣는다고 해도 외래키가 수정이 안됨! => null값이 들어가게 됨

그럼 어떻게 작성해야할까?
연관관계의 주인에 값을 넣어줘야 한다!!!!

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);

근데 양쪽에 값을 넣어주는게 좋다

연관관계 편의 메소드를 생성하는 것이 좋음!

=> 원래 같으면 team.getMembers().add(member)라는 코드도 적어둬야 하는데 인간은 망각의 동물이다 보니 자주 까먹을 수 있어서 차라리 setTeam에서 참조값을 넣어줄 때 한 번에 처리할 수 있도록 메서드를 추가하자

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

근데 이 편의 메서드도 양쪽에 다 있으면 문제가 발생할 수 있으니, 한 쪽에만 남겨두자~

양방향 매핑시에 무한 루프를 조심해야 함!

예를 들어, toString(), lombok, JSON 생성 라이브러리 등에서 문제가 발생할 가능성이 매우 high 함

최종 정리

처음 설계를 할 때는 무조건 단방향 매핑으로!

단방향 매핑만으로도 이미 연관관계 매핑은 완료됨!
양방향 매핑은 그저 반대 방향으로 조회하는 기능이 추가된 것일 뿐이다!
즉, 단방향 매핑을 잘 해두고 양방향은 필요할 때 추가하면 된다!
=> 테이블에 영향을 주지 않음(아까도 봤듯이 테이블은 바뀌는게 없다!)

객체 입장에서는 양방향으로 해봤자 고민할 것만 많아지고 힘들어 질 뿐

profile
Novice Developer's Blog

0개의 댓글