티스토리 뷰

반응형

1:N 연관관계 맵핑

연관관계 맵핑이란 JPA가 관계형 DB와 연결되면서 관계형 DB의 정규화 과정을 표현하는 것이다.

예를 들어 team의 입장에서는 member가 여러명이라는 것에서 team과 member는 1:N 관계가 되는 것이다.

 

@Entity
public class Member{
	/* ~~ */
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    
    /* ~~*/
}

 

@Entity
public class Team{
	
    /* ~~ */
    
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
    
}

 

team 1개에 member가 여러명이라 OneToMany로 맵핑해주었고, member입장에서는 반대인 ManyToOne으로 맵핑을 해주었다.

여기서 가장 중요한 것 2개가 @JoinColumn과 mappedBy이다.

위 두개를 통해서 연관관계의 주인을 선정해주기 때문이다. 

 

DB와 Java의 엔티티의 차이는 양방향관계라는 점에서 있다.

DB는 기본적으로 join을 통해서 양방향으로 맵핑되어 있지만, 엔티티의 경우에는 그렇지 않다.

양방향이라고 해도 단방향 두개를 만들어 놓은 것이다.

 

앞서 말했던 것처럼 JPA는 변경감지를 통해서 자동으로 변경을 반영해준다. 

그렇다면 여기서 team과 member 중 누구를 변경할때 반영을 시켜주냐에 대한 딜레마가 생기게 된다.

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

연관관계의 주인은 외래키가 있는 곳을 주인으로 정하는 것이 좋다.

위의 예시로 들면 DB의 입장에서 member 테이블에 team_id라는 외래키를 가지고 있기 때문에 @JoinColumn을 통해서 연관관계의 주인으로 맵핑해주었다.

 

외래키가 있는 곳을 주인으로 하는 이유는 만약 team을 변경하게 되면, member 테이블에도 update쿼리가 날아가 복잡하게 된다.

하지만 member를 주인으로 하는 경우에는 member에만 update가 날아가니 운영의 관점에서 변경/추적관리가 쉽다고 할 수 있다.

 

따라서 한마디로 결론을 내리자면 N에 있는 쪽을 엔티티의 연관관계의 주인으로 하는 것이 좋다.

 

Member member5 = new Member();
member5.setUsername("member5");
em.persist(member5);

Team team2 = new Team();
team2.setName("TeamA");
// 연관 관계의 주인이 아닌 곳에 member를 넣어도 실질적으로 DB에 반영되지 않는다.
team2.getMembers().add(member5);
// 연관 관계의 주인인 곳에 넣으면 DB에 반영이 된다.
member5.setTeam(team2);

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

 

하지만 변경감지가 알아서 값을 바꿔준다고 해도 모두 넣어주는 것을 권장한다. 

왜냐하면 실제 team에서 member를 사용하는 시점에서 쿼리가 날아가게 된다.

하지만 테스트 케이스나 flush전 조회를 하는 것이나 실수를 줄이기 위해서 모두 세팅해주는 것이 좋다.

 

아래처럼 세팅하는 시점에서 team도 넣어준다면 실수를 줄이는데 도움이 된다.

(이런 습관은 이 케이스를 포함한모든 경우에서 하는 습관을 들이도록 하자)

@Entity
public class Member{
	/* ~~ */
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "TEAM_ID")
    private Team team;
    
    public void changeTeam(Team team) {
        this.team = team;
        // 세팅하는 시점에 넣어주면 실수를 줄일 수 있다.
        team.getMembers().add(this);
    }
    /* ~~*/
}

 

Member findMember3 = em.find(Member.class, member5.getId());
List<Member> members2 = findMember3.getTeam().getMembers();

for(Member mem: members2){
    System.out.println("member: " + mem.getUsername());
}

member5.changeTeam(team2);

 

1:1 연관관계 맵핑

1:1 연관관계는 DB상에서 주 테이블에 주인을 주는 것이 좋다.

예를 들어 member가 locker를 하나씩 가졌다고 생각을 할 때, member가 DB상에서 locker를 소유하는 사람이기 때문에 주인이고,

엔티티에서도 마찬가지라고 할 수 있다.

 

@Entity
public class Member{
	/* ~~ */
    
    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Team team;
    
    /* ~~*/
}

 

@Entity
public class Locker{

    /* ~~ */

    @OneToOne(mappedBy = "locker")
    private Member member;   
}

 


 

위에서 보면  @ManyToOne(fetch = FetchType.LAZY) 다음처럼 lazy 설정을 볼 수 있다.

뒤에서 더 자세하게 설명하겠지만 엔티티를 조회할 때 한번에 모든 데이터를 긁어오는 것이 아닌 프록시를 통해서

임시로 객체를 만들어두고 호출시(필요할 때) 해당 값을 채워 넣는 방식이다.

 

출처 

인프런 자바 ORM 표준 JPA 프로그래밍 - 기본편

728x90
반응형

'Back End > JPA' 카테고리의 다른 글

[JPA 기초] 4. 프록시  (0) 2022.10.14
[JPA 기초] 3-1. @MappedSuperclass  (0) 2022.10.14
[JPA 기초] 2. Entity - DB 맵핑  (2) 2022.10.04
[JPA 기초] 1. 영속성 컨텍스트  (0) 2022.10.03
[API] 5. 1:N 관계 조회 최적화 - 1  (0) 2022.09.17
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함
250x250