본문 바로가기
  • 실행력이 모든걸 결정한다
Spring 사전 준비/JPA Hibernate

[JPA] 단방향 매핑과 양방향 매핑

by 김코더 김주역 2021. 8. 18.
반응형

테이블 환경에서는 JOIN문을 이용하여 두 테이블간의 양방향 매핑이 가능하지만, 객체 환경에서라면 말이 달라진다.

그렇다면 객체에서는 어떻게 단방향/양방향 매핑을 구현할 수 있는지 알아보자.

참고로, JPA에서 엔티티를 저장할 때 연관된 모든 엔티티는 영속 상태여야 한다는 점에 주의하자.

 

1. 단방향 매핑

단순히 객체의 멤버로 객체를 지정하면 된다.

 

<Member.java>

package com.example.jpastudy.entity;

import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;

@Entity
@Getter
@Setter
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name="USERNAME", unique = true)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private Team team;
	
    
    // 결과 확인용
    @Override
    public String toString() {
        return "Member{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", team=" + team +
                '}';
    }
}

- @ManyToOne Annotation은 N:1관계의 객체임을 알린다. Member은 Team에 속해있는 관계인 것이다. fetch 옵션값인 FetchType.LAZY는 지연 로딩 옵션이며, 현업에서는 이를 권장하고 있다고 한다.

- @JoinColumn Annotation은 외래키를 매핑할 때 사용한다. name 속성으로 매핑할 외래키의 이름을 직접 지정할 수도 있으며, 생략시 name의 기본값은 [필드명+_+참조테이블의 기본 키 컬럼명]이다.

 

 

<Team.java>

package com.example.jpastudy.entity;

import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
@Getter
@Setter
public class Team {

    @Id
    @GeneratedValue
    private Long id;

    private String name;


    // 결과 확인용
    @Override
    public String toString() {
        return "Team{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

 

 

 

2. 양방향 매핑

N:1관계에서 N측 객체안에 1측 객체에 대한 리스트를 추가하면 된다. 즉, 양방향 매핑의 장점은 반대 방향으로도 객체 탐색이 가능한 것이다.

객체 환경에서는 양측에 단방향 매핑을 적용함으로써 양방향 매핑을 구현할 수 있다.

 

<Member.java>

package com.example.jpastudy.entity;

import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;

@Entity
@Getter
@Setter
public class Member {

    @Id
    @GeneratedValue
    private Long id;

    @Column(name="USERNAME", unique = true)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn
    private Team team;


    // 결과 확인용
    @Override
    public String toString() {
        return "Member{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", team=" + team.getName() +
                '}';
    }
}

- toString()에서 team을 참조하게 되면 두 객체가 서로 무한으로 참조하게 되면서 StackOverflow 현상이 발생한다. 그래서 team.getName()으로 대신 객체를 식별할 수 있게 했다. toString() 메소드가 아니더라도 무한 참조를 일으킬 수 있는 메소드에는 조건문을 걸어서라도 무한 참조를 방지하도록 하자.

 

 

<Team.java>

package com.example.jpastudy.entity;

import lombok.Getter;
import lombok.Setter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Setter
public class Team {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

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


    // 결과 확인용
    @Override
    public String toString() {
        return "Team{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", members=" + members +
                '}';
    }
}

- @OneToMany Annotation은 1:N관계의 객체임을 알린다. mappedBy 속성은 양방향 매핑일 때 사용하며, 연관관계의 주인을 지정하는 속성이다. mappedBy 속성으로 매핑 설정한 요소에 따로 값을 추가하지 않아도 DB에는 외래 키 값이 정상적으로 입력되지만, 두 쪽 다 값을 지정해주는 것이 객체로 관리한다는 취지에 맞다.

연관관계의 주인★ : 두 객체 연관관계 중에서 외래 키가 있는 곳을 말한다. 일대다/다대일 관계에서는 항상 다 쪽이 외래 키를 가진다. 연관관계의 주인은 DB 연관관계와 매핑되고 외래 키를 관리할 수 있다. 반면에 연관관계의 주인이 아닌 쪽에 입력한 값은 DB에 저장될 때 무시된다.

※ mappedBy 속성값은 매핑할 객체 이름을 대소문자까지 일치하게 작성해야 한다.

 

- 객체의 다대일 양방향 연관관계에서 양쪽 관계를 편리하고 안전하게 맺어주기 위해, 연관관계 주인의 setter 메소드는 다음과 같이 개선해주는 것이 좋다.

public void setTeam(Team team) {
    if(this.team!=null) this.team.getMembers().remove(this); // 기존 관계가 있었다면 제거
    this.team=team;
    team.getMembers().add(this);
}

 

 

반응형

댓글