Spring

6. 스프링 공부 / 양방향 연관관계와 연관관계의 주인

공대키메라 2021. 7. 15. 23:22

 

단방향 매핑에서 양방향 매핑으로 한단계 상승!

 

전에 내용에서 본 단방향 관계와 양방향 관계는 테이블 구조로 보면 전혀 다른게 없다.

왜? 테이블을 생각해보면 외래키로 조인이 가능하기 때문임

서로 조인으로 왓다갓다 하면 끝이기 때문이다

 

그런데 class의 경우에는 그게 안됨!~

 

그래서 각자 안에 Member.class에는 Team을 넣어주고, Team.class안에는 Member를 넣어준다.

근데 Team에는 구성원이 여러명이라면 List<Member>로 넣어준거다. 

 

//=======================Team.class에 팀 하나당 여러명의 멤버가 있으니
//=======================OneToMany를 members에 붙여줌.
package jpabook.jpashop.domain;

import javax.persistence.*;
import java.util.ArrayList;
@Entity
public class Team {

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

    @OneToMany(mappedBy = "team") // 일대다 매핑에서 뭐랑 연결되 있는지 알려줌. 
    private ArrayList<Member> members = new ArrayList<>();

	//getter setter 생략
}

//=======================Member.class에 팀 여러명의 멤버를 팀 하나가 포함하고 있으니
//=======================ManyToOne 어노테이션을 붙여줌
package jpabook.jpashop.domain;

import javax.persistence.*;

@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;*/
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

   //getter setter 생략
}

 

그리고 이렇게 main에서 사용할 수 가 있다.

package jpabook.jpashop;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Team;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.ArrayList;

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try{

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

            Member member = new Member();
            member.setUsername("member1");
            member.setTeam(team); // 알아서 PK값 꺼내서 FK값으로 넣어줌
            em.persist(member);

            em.flush();
            em.clear();

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

            ArrayList<Member> members = findMember.getTeam().getMembers();

            for(Member m : members){
                System.out.println("m = " + m.getUsername());
            }

            tx.commit();

        }catch(Exception e){
            tx.rollback();
        }finally{
            //code
            em.close();
        }

        emf.close();
    }
}

 

여기서 눈여겨 봐야 할 것은 mappedBy가 있다. 

이게 JPA에서는 왜 이렇게 설계를 했을까?

언제 써야할지 감이 안온다. 

 

우선 객체와 테이블이 관계를 어떻게 맺는지 차이점을 보자. 

객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개다. 

 

객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다. 

 

A -> B.(a.getB())

B -> A(.b.getA())

 

테이블의 양방향 연관 관계는 테이블은 외래키 하나로 양쪽에 접근이 가능하다는 것이다.

MEMBER>TEAM_ID외래키 하나로 말이다!

 

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

 

 

여기서 딜레마!

그럼 둘중에 뭘 매핑해야하는거야? 그러니까 mappedBy를 뭐로해야지?

 

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

둘중에 하나 주인을 정해야 한다. 

 


주인만이 외래 키를 관리할 수 있다.

 

주인은 외래키가 있는 곳을 주인으로 정한다.!

 

Member 테이블 안에 TeamId를 FK로 가지고 있다! = > member가 주인

 

1. 헷갈리지 않음(쿼리가 나가면 직관적으로 인식 가능)

2. DB입장에서 외래 키가 있는곳이 무조건 다임

3. 경험상 성능 이슈 및 설계가 깔끔함!(김영한 teacher 팁...)

 

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

 

 

package jpabook.jpashop;

import jpabook.jpashop.domain.Member;
import jpabook.jpashop.domain.Team;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();

        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try{
            Member member = new Member();
            member.setUsername("member1");
            em.persist(member);

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

            em.flush();
            em.clear();

            tx.commit();
        }catch(Exception e){
            tx.rollback();
        }finally{
            //code
            em.close();
        }

        emf.close();
    }
}

위 코드를 실행하면 같은 값이 두 테이블에 넣어야 하는데 서로 다른 값이 들어갈 수 가 잇다.

 

그러면 어떻게 이것을 해결할 수 있냐?