본문 바로가기
개발공부 개발새발/DB

JPA) 연관관계 매핑

by 휴일이 2022. 11. 24.

연관관계 매핑

객체의 참조와 테이블의 외래 키를 매핑하자 !


방향 : 단방향, 양방향
다중성 : n:1, 1:n, 1:1, n:m (다대일, 일대다, 일대일, 다대다)
* 연관관계의 주인 : 객체 양방향 연관관계는 관리주인이 필요 *


 


단방향 연관관계(객체지향모델링)

 

			// FK를 가지고 있는 객체에게 얘랑 조인할거야 알려주는 애너테이션
            @ManyToOne // Member가 1, team이 n
    		@JoinColumn(name = "TEAM_ID") // 얘랑 JOIN 할거얌
    		private Team team;
            
            ===============================
            
            // 팀 저장
            Team team = new Team();
            team.setName("TeamA");
            em.persist(team);

            // 회원 저장
            Member member = new Member();
            member.setUsername("member1");
            // 내가 설정한 Team의 Id가 저장됨
            member.setTeam(team);
            em.persist(member);


            // Member 객체의 Id값(PK)로 찾아주고
            Member findMember = em.find(Member.class, member.getId());

            // 직접 getTeam해서 불러옴
            Team findTeam = findMember.getTeam();
            System.out.println("findTeam.getName() = " + findTeam.getName());

 

 

 

간단하게 불러오깅

 

 

 

 

 


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



mappedBy

객체와 테이블이 관계를 맺는 차이


객체 연관관계 = 2개
회원 -> 팀 (단방향)
팀 -> 회원 (단방향)
ㄴ 단방향 연관관계가 두개 있으니까 그냥 양방향 연관관계라고 하는 것
ㄴ 참조 두개로 왔다갔다

테이블 연관관계 = 1개 (외래키로 양방향)
회원 <-> 팀 (양방향)
ㄴ 키 하나(참조 하나)로 와따가따


객체 : 객체를 양방향으로 참조하려면, 단방향 연관관계를 2개 만들어야하는 것.
테이블 : 외래키 하나만 있으면 테이블의 연관관계가 형성됨



연관관계의 주인 ?
양방향 매핑을 할 때, 객체 두 관계중 하나를
연관관계의 주인으로 지정한다
*연관관계의 주인만이 외래 키를 관리(등록, 수정)
*주인이 아니면 읽기만 가능
주인은 mappedBy 속성 사용 안 함
주인이 아니면 mappedBy로 주인이 뭔지 지정하기




그러면 누구를 주인으로 해야 좋을까?
!!외래 키(FK)가 있는 곳!!
PK가 있는 곳을 주인으로 해버리면,
다른 PK를 외래키로 가지고 있는 것들이 같이 업데이트 될 수 있음
성능도 좀 떨어져용


DB는 FK가 있으면 무조건 n, PK는 1 -> 다수 쪽이 주인이 됩시다




양방향 매핑시 실수를 주의하자
*** 연관관계의 주인에 값을 입력하지 않으면 X

 

 

            // 주인 : Team
            // 주인이 아닌 걸 먼저 넣어버리면
            Member member = new Member();
            member.setUsername("member1");
            em.persist(member);

            // 주인이 member 객체를 넣어도, 키값이 안 바뀜
            Team team = new Team();
            team.setName("TeamA");
            team.getMembers().add(member);
            em.persist(team);

            // 결과는 MEMBER의 TEAM_ID가 null

역방향(주인이 아닌 방향)만 연관관계 설정하면 안 됨

 

// 주인 : Team
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);

//정상적으로 값이 들어감

 

 

 

 

 

사실 그냥 양 쪽에 값을 다 넣어주는 것이 맞음

(양 쪽에 안 넣어주면, 순수 객체를 find 할 가능성이 있어요)

 

            // 주인 : Team
            Team team = new Team();
            team.setName("TeamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            member.setTeam(team);
            em.persist(member);

            // 1. 양쪽 다 값을 입력하지 않으면?
//            team.getMembers().add(member);
            
            // 2. 순수한 team 객체를 찾기때문에(JPA, DB와 관련X)
            // 3. em.find로는 team을 찾을 수 없어요
            Team findTeam = em.find(Team.class, team.getId()); // 1차 캐시
            // 4. 그래서 members.size() == 0
            List<Member> members = findTeam.getMembers();

 

 

 

 

flush, clear가 없으면 1차 캐시에 아무것도 없어서 DB에서 다시 조회해 온다

하지만 1차 캐시에 값이 존재할 경우, 양쪽에 다 넣지 않으면 못 가져온다 JPA가 동작하지 않는다

 

 

// 주인 : Team
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setTeam(team); // 1. 나한테 넣기
em.persist(member);

team.getMembers().add(member); // 2. 쟤한테도 넣기

 

이렇게 넣는 걸 까먹을 수도 있으니

 

 

 

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

주인이 아닌 메서드에 mapped 된 객체를 가져오는 set 메서드에 이런 식으로 추가하면

(셋팅 시점에 걍 양쪽에 넣어버리기)

 

 

            // 주인 : Team
            Team team = new Team();
            team.setName("TeamA");
            em.persist(team);

            Member member = new Member();
            member.setUsername("member1");
            member.setTeam(team); // 1. 나한테 넣기
            em.persist(member);

//            team.getMembers().add(member); // 2. 쟤한테도 넣기

 

쟤한테도 넣기를 생략 가능하다

 

 

 

단순 setter로만 생각될 수 있으니

 

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

이렇게 메서드명을 특이하게 바꿔주는 것도 조은 방법이다

1에나 n에 넣을 수 있지만, 양쪽에 다 넣진 않는다

 

 

 

 

 

양방향 매핑할 때 무한루프 조심~

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

 

 

toString()

 

@Override
public String toString() {
    return "Member{" +
            "id=" + id +
            ", username='" + username + '\'' +
            ", team=" + team + //team의 toString을 불러온다
            '}';
}
@Override
public String toString() {
    return "Team{" +
            "id=" + id +
            ", name='" + name + '\'' +
            ", members=" + members + // 근데 얘는 members.toString()을 불러온다(무한루프)
            '}';
}

 

 

양쪽으로 무한 호출된다

 

lombok

ㄴ toString 자동 생성 쓰지 마세요

 

 

JSON 생성 라이브러리

컨트롤러에서 엔티티를 JSON으로 바꿀 때 ---- 문제생김

해답 : Controller에서 Entity 소환 금지, JSON으로 반환할때 문제됨

Entity -> DTO로 변환해서 소환하세욘~~!

 

 

 

 

------------

 

 

양방향 매핑 정리

 

처음 설계는 단방향으로 하세요

단방향 매핑만으로도 이미 연관관계 매핑은 완료! 

ㄴ 양방향은 반대 방향으로 조회 기능이 추가된 것 뿐이에요

 

JPQL에서는 역방향 탐색할 일이 많지만

 

단방향 매핑을 잘 하고, 양방향은 필요할 때 추가해도 됩니다(테이블에 영향 주지 않아요)

 

 

연관 관계 주인을 정하는 기준?

ㄴ비즈니스 로직을 기준으로 연관관계 주인을 선택하면 안 됨

주인은 외래 키의 위치를 기준으로 해야합니다

 

 

 

 

728x90