[인프런 김영한] 연관관계 매핑 기초 - 양방향 연관관계

2021. 2. 3. 22:59프로그래밍 언어/Spring Framework

[인프런 김영한] 연관관계 매핑 기초 - 양방향 연관관계


해당 글은 인프런 김영한강사님의 영상을 보고 정리한 글입니다.

Spring Boot, Spring Data JPA를 사용해 실습하였습니다.

김영한 인프런 : www.inflearn.com/users/@yh

 

인프런 - 김영한의 강의들을 만나보세요.

우아한형제들 개발 팀장 (전: 카카오, SK플래닛) 저서: 자바 ORM 표준 JPA 프로그래밍

www.inflearn.com


▣ Goal

 

1. 양방향 연관관계

2. 연관관계의 주인


▣ 문제

public JpaApplication(TeamRepository teamRepository, MemberRepository memberRepository) {
        this.teamRepository = teamRepository;
        this.memberRepository = memberRepository;

        Team team = new Team();
        team.setName("TeamA");
        teamRepository.save(team);

        Member member = new Member();
        member.setUsername("member1");
        member.setTeam(team);
        memberRepository.save(member);

        Member findMember = memberRepository.findById(member.getId()).get();
        Team findTeam = findMember.getTeam();

        // Team에서 member를 볼 수 없을까? -> 양방향
        findTeam.getMember(); // Error

        System.out.println("findTeam = " + findTeam.getName());

    }

| Member의 Team을 조회는 가능했다. (findMember)

그러면 반대로 Team에서 Member 조회는 안될까? (지금은 안됨)

 

 

 

▣ 단방향에서 양방향으로 변경할 때 (테이블 연관관계 , 객체 연관관계)

| 단방향에서 양방향으로 관계매핑을 할 때 [테이블 연관관계]에서는 전혀 변화가 없다. (FK로 끝)

[객체 연관관계]에서는 Team에 List<Member>를 넣어주어야 한다. Team은 Member를 모르기 때문.

 

 

 

 

▣ 단방향 -> 양방향

@Entity
@Setter
@Getter
public class Team {

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

    private String name;

// 코드 추가
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

}


@Entity
@Setter
@Getter
public class Member {

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

    @Column(name = "USERNAME")
    private String username;
    
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

}

* @OneToMany : One은 Team, Many는 Member를 의미

* mappedBy :  Member의 @ManyToOne에 해당하는 Team team 이라는 변수명을 적어준다.

매핑되는 객체에서 "team과 매핑이 된다" 라고 생각하면된다.

 

▶ 끝이다. 반대로 탐색이 가능하다.

 

 

 

▣ 예제

@SpringBootApplication
public class JpaApplication {

    public static void main(String[] args) {
        SpringApplication.run(JpaApplication.class, args);
    }

    private static TeamRepository teamRepository;
    private static MemberRepository memberRepository;

    public JpaApplication(TeamRepository teamRepository, MemberRepository memberRepository) {
        this.teamRepository = teamRepository;
        this.memberRepository = memberRepository;

        Team team = new Team();
        team.setName("TeamA");
        teamRepository.save(team);

        Member member = new Member();
        member.setUsername("member1");
        member.setTeam(team);
        memberRepository.save(member);
		
// 예제 핵심포인트
        Member findMember = memberRepository.findById(member.getId()).get();
        List<Member> members = findMember.getTeam().getMembers(); // member에서 team을 찾고 team의 member를 찾는다.

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

    }
}


``````````````````````````````````````````````````````````````````````````````````
// SQL문
Hibernate: 
    select
        member0_.member_id as member_i1_0_0_,
        member0_.team_id as team_id3_0_0_,
        member0_.username as username2_0_0_,
        team1_.team_id as team_id1_1_1_,
        team1_.name as name2_1_1_ 
    from
        member member0_ 
    left outer join
        team team1_ 
            on member0_.team_id=team1_.team_id 
    where
        member0_.member_id=?
Hibernate: 
    select
        members0_.team_id as team_id3_0_0_,
        members0_.member_id as member_i1_0_0_,
        members0_.member_id as member_i1_0_1_,
        members0_.team_id as team_id3_0_1_,
        members0_.username as username2_0_1_ 
    from
        member members0_ 
    where
        members0_.team_id=?
member = member1

T※ Unable to evaluate the expression Method threw 'org.hibernate.LazyInitializationException' exception.

에러가 발생할 시 Team의 private List<Member> members = new ArrayList<>();에

@OneToMany(mappedBy = "team", fetch = FetchType.EAGER)를 추가한다.

 

** Eager는 차후 영상 듣고 내용추가하기

 

 

▣ 그래서 양방향이 좋은건가?

-> 기본적으로 단방향이 좋다. 양방향은 신경쓸께 많다.

 

▣ 양방향관계, 객체와 테이블이 관계를 맺는 차이

[객체 연관관계] = 2개

회원 -> 팀 (1개, 단방향)

팀 -> 회원 (1개, 단방향)

▶ 객체의 양방향 관계는 서로 다른 단방향 관계 2개이다.

▶ 객체는 둘중(MEMBER, TEAM) 하나로 왜래키를 관리해야 한다. -> 연관관계의 주인을 정해야 한다.

 

[테이블 연관관계] = 1개

회원 <-> 팀 (1개, 양방향)

 

 

연관관계의 주인을 정해야 한다.

* 양방향 매핑 규칙

1. 객체의 두 관계중 하나를 연관관계의 주인으로 지정

2. 견관관계의 주인만이 왜래키를 관리(등록, 수정)

3. 주인이 아닌쪽은 읽기만 가능

4. 주인은 mappedBy 속성 사용X

5. 주인이 아니면 mappedBy 속성으로 주인 지정

 

 

▣ 그래서 누구를 주인으로 할까?

| 여기서는 Member.team이 연관관계 주인. 테이블 연관관계에서 FK를 생각하면 조금 더 쉽다.