본문 바로가기
공부/Spring

[Spring] JPA 엔티티 다대일(N:1) 연관 관계 매핑 (빌더 패턴 사용)

by 웅대 2023. 5. 15.
728x90
반응형

https://growth-coder.tistory.com/172

 

[Spring] 빌더 패턴에 대한 이해와 사용법

member = new Member(1L, "chulsoo", "seoul"); 어떤 클래스에 대한 인스턴스를 생성할 때 종종 빌더(builder) 패턴을 사용하곤 했다. 지금까지는 단순히 setter로 필드 값을 세팅하는 것보다 좋다고만 알고 있었

growth-coder.tistory.com

이전 포스팅에서 빌더 패턴에 대해서 알아보았다.

 

그리고 JPA로 엔티티 간의 N:1 연관 관계를 매핑하면서 빌더 패턴을 사용해보았다.

 

우선 연관 관계부터 세팅해보려고 한다.

 

N:1 양방향 연관 관계 매핑

하나의 팀에 여러 멤버가 존재한다고 가정하면 Team 엔티티와 Member 엔티티는 N:1 관계를 가진다.

 

그리고 N:1 관계에서는 N에 해당하는 쪽이 FK(외래키)를 가지게 된다.

 

이는 관계형 데이터베이스를 잘 생각해보면 알 수 있다.

 

일반적인 SQL에서는 크게 문제가 없다.

 

그런데 JPA는 ORM(Object Relational Mapping)으로 객체와 데이터베이스를 매핑해주는 도구이다.

 

객체 지향적으로 구성을 하다보니 외래키를 가지고 있지 않은 Team에서는 자신을 참조하고 있는 Member를 알기가 어렵다.

 

그래서 JPA에서는 Team이 Member의 리스트를 가지는 방식으로 해결할 수 있다.

 

우선 Team 엔티티는 다음과 같다.

 

<Team>

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String teamName;
    @OneToMany(mappedBy = "team", fetch = FetchType.LAZY)
    private List<Member> memberList = new ArrayList<>();
    @Builder
    public Team(String teamName) {
        Assert.hasText(teamName, "teamName은 필수");
        this.teamName=teamName;
    }
}

Team 테이블의 컬럼은 id와 teamName이 존재한다.

 

그리고 Team 엔티티에는 추가로 자신을 참조하고 있는 멤버들의 리스트인 memberList를 가지고 있다.

 

Team 입장에서 Member는 1:N 관계이기 때문에 @OneToMany 어노테이션을 사용한다.

 

그리고 자세히보면 @OneToMany 어노테이션의 속성에 mappedBy가 있는 모습을 확인할 수 있다.

 

이는 외래키의 주인을 알려주는 것이다.

 

실제 데이터베이스에서는 N에 해당하는 쪽이 FK를 소유하기 때문인데 이 FK의 소유권을 알려주는 속성이 mappedBy이다.

 

N에 해당하는 Member가 FK를 소유하고 있다는 것을 알려야 한다.

 

Member 또한 Team을 필드로 가지고 있을텐데 이 Team 필드의 이름을 적어주면 된다.

 

정리를 하자면

 

  1. 1에 해당하는 쪽의 @OneToMany 어노테이션에 mappedBy를 사용한다.
  2. N에 해당하는 쪽의 필드 중 1에 해당하는 엔티티가 존재할텐데 이 필드의 이름을 mappedBy로 적어주면 된다.

그리고 builder 패턴을 사용하였다.

 

이제 Member 엔티티를 볼 차례이다.

 

<Member>

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String address;
    @ManyToOne
    private Team team;
    @Builder
    public Member(String name, String address, Team team) {
        Assert.hasText(name, "name은 필수");
        Assert.hasText(address, "address은 필수");
        this.name=name;
        this.address=address;
        this.team=team;
        team.getMemberList().add(this);
    }
}

보다싶이 @ManyToOne 어노테이션이 달려있는 곳에 Team 엔티티가 존재한다.

 

필드 이름이 team이기 때문에 mappedBy에 team을 넣어준 것이다.

 

Team 또한 builder 패턴을 사용한 모습을 볼 수 있다.

 

자세히 보면 Team 엔티티를 인자로 받는 것을 확인할 수 있는데 team의 MemberList를 가져와서 자신(member)를 추가하는 모습을 확인할 수 있다.

 

관계형 데이터베이스에서는 단순히 member가 team을 참조하도록 구성하면 끝이다.

 

언제든 join 연산을 통해서 원하는 결과를 가져올 수 있기 때문인데 JPA는 ORM 기술이다보니 쉽지 않다.

 

그렇기 때문에 위에서 Team이 MemberList를 갖도록 구성한 것이다.

 

그래서 양방향 매핑이라 부르는데 엄밀히 따지자면 두 개의 단방향 매핑이다.

 

그래서 Member가 Team을 참조하도록 세팅하는 것만으로는 부족하다.

 

그래서 추가로 Member를 생성할 때 Member에도 Team을 매핑해주고 Team의 MemberList에도 Memeber를 추가해줘야 하는 것이다.

 

깃허브

https://github.com/ezcolin2/Spring-Class/tree/main/JPA%20OneToMany

 

728x90
반응형

댓글