T_era

연관관계 매핑 본문

Programing/Spring

연관관계 매핑

블스뜸 2025. 5. 16. 10:27

객체지향 모델과 관계형 데이터베이스 모델 간의 불일치를 해결하고, 데이터베이스 테이블 간의 관계를 객체 간의 관계로 표현하는 데 사용된다.

연관관계 매핑의 종류

  • 일대일(One-to-One): 하나의 엔티티가 다른 엔티티 하나와 연관되는 관계다. @OneToOne 어노테이션을 사용하여 매핑한다. 주 테이블에 외래 키를 두거나, 대상 테이블에 외래 키를 두는 방식이 있다.
  • 일대다(One-to-Many): 하나의 엔티티가 여러 개의 다른 엔티티와 연관되는 관계다. @OneToMany 어노테이션을 사용하며, 일반적으로 대상 테이블에 외래 키를 가진다. 컬렉션(List, Set 등)을 사용하여 연관된 엔티티들을 관리한다.
  • 다대일(Many-to-One): 여러 개의 엔티티가 하나의 다른 엔티티와 연관되는 관계다. @ManyToOne 어노테이션을 사용하며, 현재 테이블에 외래 키를 가진다.
  • 다대다(Many-to-Many): 여러 개의 엔티티가 여러 개의 다른 엔티티와 연관되는 관계다. @ManyToMany 어노테이션을 사용하며, 일반적으로 중간 테이블을 사용하여 관계를 표현한다.

연관관계 매핑 시 고려사항

  • 방향성: 단방향 연관관계와 양방향 연관관계가 있다. 단방향은 한쪽 엔티티에서만 다른 엔티티로의 참조를 가지는 것이고, 양방향은 양쪽 엔티티 모두 서로를 참조하는 것이다. 양방향 연관관계는 @OneToMany, @ManyToMany에서 mappedBy 속성을 사용하여 주인이 아닌 쪽을 명시해야 한다.
  • 연관관계의 주인: 양방향 연관관계에서 데이터베이스에 외래 키를 관리하는 쪽을 연관관계의 주인이라고 한다. 주인만이 데이터베이스 연관관계를 변경할 수 있다. @JoinColumn 어노테이션을 사용하여 외래 키를 매핑한다.
  • 고아 객체 제거: 부모 엔티티와의 연관관계가 끊어진 자식 엔티티를 자동으로 삭제하고 싶을 경우 @OneToMany, @OneToOne 어노테이션의 orphanRemoval = true 옵션을 사용할 수 있다.
  • 지연 로딩과 즉시 로딩: 연관된 엔티티를 언제 로딩할 것인지 결정하는 전략이다. @ManyToOne, @OneToOne은 기본이 즉시 로딩(EAGER)이고, @OneToMany, @ManyToMany는 기본이 지연 로딩(LAZY)이다. 필요에 따라 @Fetch 어노테이션 등을 사용하여 로딩 전략을 변경할 수 있다.

매핑 예시 (다대일 단방향)

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

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

    // ... (getter, setter 등)
}

@Entity
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // ... (getter, setter 등)
}

위 예시는 Member 엔티티가 Team 엔티티와 다대일 단방향 연관관계를 맺는 것을 보여준다. Member 테이블에는 team_id라는 외래 키가 존재하여 Team 테이블의 기본 키를 참조한다.

매핑 예시 (다대일 양방향)

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

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

    // 연관관계 편의 메소드 (선택 사항)
    public void setTeam(Team team) {
        if (this.team != null) {
            this.team.getMembers().remove(this);
        }
        this.team = team;
        if (team != null) {
            team.getMembers().add(this);
        }
    }

    // ... (getter, setter 등)
}

@Entity
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

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

    // ... (getter, setter 등)
}

설명:

  • Member 엔티티: @ManyToOne 어노테이션과 @JoinColumn(name = "team_id")를 사용하여 Team 엔티티와의 다대일 관계를 설정한다. 이는 이전 단방향 예시와 동일하다. team_id 외래 키가 Member 테이블에 생성된다.
  • Team 엔티티: @OneToMany(mappedBy = "team") 어노테이션을 사용하여 Member 엔티티와의 일대다 관계를 설정한다.
    • mappedBy = "team": 이 속성은 양방향 연관관계에서 주인이 아닌 쪽을 명시한다. "team"은 Member 엔티티의 @ManyToOne으로 매핑된 필드 이름이다. 즉, Member 엔티티의 team 필드가 이 연관관계의 주인이 되어 데이터베이스의 외래 키를 관리한다.
    • List<Member> members = new ArrayList<>();: Team 엔티티는 해당 팀에 소속된 Member 엔티티들의 목록을 members 필드를 통해 관리한다.
  • 연관관계 편의 메소드 (setTeam in Member): 양방향 연관관계 시 객체 그래프의 일관성을 유지하기 위해 편의 메소드를 작성하는 것이 좋다. 이 메소드는 Member의 team을 설정함과 동시에 Team의 members 컬렉션에도 해당 Member를 추가하거나 제거하는 로직을 포함한다. 이렇게 하면 개발자가 실수로 한쪽의 연관관계만 설정하는 상황을 방지할 수 있다.

연관관계 매핑은 JPA에서 데이터베이스와 객체 간의 관계를 효과적으로 관리하는 데 중요한 개념이다. 각 연관관계의 특징과 매핑 방법을 정확히 이해하고 사용하는 것이 중요하다.