JPA를 활용한 연관 관계 설정

Violet_Evgadn·2023년 4월 24일
0

DB연동

목록 보기
19/22

단방향 연관관계

단방향 연관관계란?

JPA를 활용해 연관 관계를 설정할 때는 "방향"을 지정해줄 수 있다.

예시로 팀과 선수를 생각해보자.

팀은 1개일 때 해당 팀에 소속된 선수는 다수 있을 것이므로 팀과 선수는 1:N 관계라고 생각할 수 있다.

여기에서 단방향을 설명하기 전에 양방향을 먼저 설명하자면, 사실 양방향은 따로 양방향이라는 개념이 존재하는 것이 아니라 "단방향 연관관계 2개를 설정한 것"이라고 생각하면 된다.

자 머리가 점점 아파온다. 이제 예시를 통해 조금씩 풀어보자.

먼저 나는 팀 데이터를 확인할 때는 선수를 보고 싶지만, 굳이 선수를 확인할 때는 해당 선수가 속한 팀 정보까지는 알고 싶지 않을 수도 있다.

내가 "최정"이라는 선수의 정보를 보고 싶은데, 굳이 SSG 팀의 연봉이나 승수, 관객 총 수를 알고 싶지는 않을 수 있다.

이런 경우 나는 연관 관계를 단방향으로 팀 → 선수로 설정하는 것이다. 이 경우 SSG라는 팀을 검색하면 선수 데이터도 같이 불러오기 때문에 여러 선수 데이터 중 "최정"이라는 이름을 가진 선수를 찾으면 되는 것이다.

반대로 나는 SSG라는 팀을 검색하고 싶은데, 굳이 SSG의 팀원에 대한 상세 정보까지는 알고 싶지 않을 수 있다. 하지만 선수 정보를 검색하면 해당 팀에 대한 정보가 나와서 팀의 성적까지 알고 싶을 수도 있다.

이 경우는 반대로 선수 → 팀으로 방향을 설정하여 "최정"이라는 선수를 검색하면 SSG 팀에 대한 정보를 볼 수 있지만, SSG 팀을 검색하면 팀에 속한 어떤 선수들에 대한 정보도 가져오지 않는 방식인 것이다.

@ManyToOne

다대일(N:1) 관계일 때 활용하는 어노테이션이다.

다대일이기 때문에 FK는 해당 Entity와 연동된 table에 FK 값이 저장될 것이다.

@ManyToOne은 @JoinColumn 어노테이션과 많이 활용된다.

위에서 말했듯 FK는 해당 Entity와 연동된 Table을 의미하기 때문에 FK가 저장될 Column Name을 지정해줄 때는 @Column으로만 입력하기에는 조금 부족한 점이 있다.

이 @JoinColumn은 "FK가 저장되는 Column이다"라는 특수한 의미를 나타내며, 멤버 변수가 기본 자료형이 아닌 연동될 Entity라는 특징이 존재한다.

만약 @JoinColumn을 생략한다면 기본적으로 "필드명 + _ + FK Column Name"으로 설정된다.

@OneToMany

일대다(N:1) 관계일 때 활용되는 어노테이션이다.

먼저 일대다 같은 경우 어차피 FK는 외부 Table에 저장될 것이므로 굳이 연결될 Column Name을 지정하지 않아도 된다. 따라서 오로지 @OneToMany만 활용하면 된다.

일대다라는 것은 결국 해당 Entity에 대한 정보를 가지고 올 때 연동된 Table에 가져올 데이터가 많다는 것을 의미한다.

데이터가 많다? 당연히 자바에서는 이 데이터를 List 형식을 통해 받아오게 된다.

방식은 아래와 같다.

@OneToMany
private List<Member> members;           // 제너릭 사용

@OneToMany(targetEntity=Member.class)   // 거의 사용 안함
private List members;                   // 타입 알 수 없음.

양방향 연관관계

연관관계의 주인

양방향 매핑일 경우 연관관계의 주인이 중요해진다. 연관관계의 주인이란 "두 객체 연관관계 중 테이블의 외래키를 관리하는 주체"이다.

결론만 말하자면 항상 "다(N)"쪽이 외래키를 가지기 때문에 @ManyToOne이 주인이 된다.

사실 생각해보자면 당연한 게, @ManyToToOne Table 쪽에 FK값이 저장되기 때문에 무조건 다(N) 쪽에 FK가 저장되는 것이 당연하다.

여기서 중요한 점은 주인이 아닌 반대편은 읽기만 가능하며 외래 키를 변경하지 못한다는 것이다.

예를 들어 팀-멤버라는 관계가 있는데 멤버가 팀을 변경했을 경우 팀에서 멤버를 뽑아낸 이후 FK 값을 바꾸는 것은 수행할 수 없고 멤버를 검색하여 멤버에서 직접 PK 값을 변경해야 한다는 것이다.

주인이 아닌 양방향 관계의 객체 변수는 mappedBy 속성을 활용해 연관관계의 주인을 지정해줘야 한다. 즉, 주인은 mappedBy 속성이 없다는 의미로도 이해할 수 있을 것이다.

양방향 연관관계 사용 방법

@Entity
public class Member {

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

    private String username;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    //연관관계 설정
    public void setTeam(Team team) {
        this.team = team;
    }

    //Getter, Setter
}
@Entity
public class Team {

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

    private String name;

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

    // Getter, Setter ...
}

가장 중요한 것은 @OneToMany의 "mappedBy" 속성이 아닐까 싶다.

mappedBy를 반대쪽 Entity인 Member의 멤버 변수 명인 "team"으로 지정하여 이 연관관계의 주인이 Member라는 것을 명시해준다.

연관관계 주인의 중요성

public void testSaveNonOwner() {

    //회원1 저장
    Member member1 = new Member("member1", "회원1");
    em.persist(member1);

    //회원2 저장
    Member member2 = new Member("member2", "회원2");
    em.persist(member2);

    Team team1 = new Team("team1", "팀1");

    //주인이 아닌 곳에 연관관계 설정
    team1.getMembers().add(member1);
    team2.getMembers().add(member2);

    em.persist(team1);
}

이 결과를 DB에서 조회하면 아래와 같은 결과가 나온다

member1회원1null
member2회원2null

다시 한번 되씹자. "연관관계 주인만이 외래 키 값을 변경할 수 있다!"

(예시로 보자면 연관관계의 주인이 될 Member만이 외래키 값을 변경할 수 있는 것이다)

좋아요공감
공유하기통계게시글 관리

profile
혹시 틀린 내용이 있다면 언제든 말씀해주세요!

0개의 댓글