연관관계 매핑

이동건 (불꽃냥펀치)·2025년 3월 7일

단방향 연관관계

객체의 참조와 테이블의 외래키 매핑

@Entity
public class Member {
    @Id
    @GeneratedValue
    private Long id;
    private String username;
    private int age;
    @Enumerated(EnumType.STRING)
    private MemberType type;

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

    public void createMember(Team team) {
        this.team = team;
        team.getMembers().add(this);

    }

    @Enumerated(EnumType.STRING)
    private MemberType memberType;
 }

객체지향 모델링

Team team = new Team();

team.setName("TeamA");
 
em.persist(team);



//회원 저장

Member member = new Member();
 
member.setName("member1");

member.setTeam(team); //단방향 연관관계 설정, 참조 저장
 
em.persist(member);


객체 그래프 탐색 및 수정

//조회

Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회

Team findTeam = findMember.getTeam();

// 새로운 팀B

Team teamB = new Team();

teamB.setName("TeamB");

em.persist(teamB);



// 회원1에 새로운 팀B 설정

member.setTeam(teamB);




양방향 연관관계와 연관관계의 주인

양방향 매핑(팀 엔터티에 @OneToMany 추가)

@Entity
@Setter
@Getter
public class Team {
    @Id
    @GeneratedValue
    private Long id;
    private String name;

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

연관관계의 주인

  • 객체의 두 관계 중 하나를 연관관계의 주인으로 지정
  • 연관관계의 주인만이 외래키를 관리
  • 주인이 아닌 쪽은 읽기만 가능하고 수정을 불가능
  • 주인이 아니면 mappedBy 속성으로 주인 지정
  • 양방향 연관관계에서 주인은 외래키가 있는 (여기서는 Member) 쪽에 설정하는 것이 좋다



다대일 단방향/ 양방향

단방향

  • 가장 많이 사용하는 연관관계
  • 다대일의 반대는 일대다

양방향

  • 외래키가 있는 쪽이 연관관계의 주인
  • 양쪽을 서로 참고하도록 개발

일대다 단방향

일대다 단방향

  • 일대다 단방향은 일대다에서 일이 연관관계의 주인

  • 테이블 일대다 관계에서는 항상 다 쪽에 외래키가 있음

  • @JoinColumn을 사용하지 않으면 조인테이블 방식을 사용하여 중간에 테이블을 하나 추가함

  • 단점

    • 엔터티가 관리하는 외래키가 다른 테이블에 있음
    • 연관관계 관리를 위해 추가로 UPDATE SQL을 실행
    • 일대다 단방향 보다는 다대일 양방향을 권장

일대일 관계

  • 주 테이블이나 대상 테이블 중에 외래키 선택 가능
  • 다대일 단방향 매핑과 유사
  • 일대일 양방향 매핑에서는 외래키가 있는 곳이 다대일과 마찬가지로 연관관계의 주인이다
  • 반대편은 mappedBy 적용

다대다 관계

  • 무조건 일대다, 다대일 관계로 다시 나눠야함
  • 편리해 보이지만 실무에서 사용 x



상속관계 매핑

  • 관계형 데이터베이스는 상속이란 개념이 없다
  • 객체의 상속과 구조가 DB의 슈퍼타입 서브타입 관계를 매핑
    • 각각 테이블로 변환 => 조인 전략
    • 통합 테이블로 변환 => 단일 테이블 전략
    • 서브타입 테이블로 변환 => 구현 클래스마다 테이블 전략

주요 에노테이션

  • @Inheritance(strategy=InheritanceType.xxx)
    • Joined: 조인 전략
    • Single Table: 단일 테이블 전략
    • Table_Per_class: 구현 클래스마다 전략
  • @DiscriminatorColumn(name="DTYPE")
  • @DiscriminatorValue("XXX")

조인 전략

  • 장점
    • 테이블 정규화
    • 외래 키 참조 무결성 제약조건 활용 가능
    • 저장공간 효율화
  • 단점
    • 조회시 조인을 너무 많이 사용
    • 조회 쿼리의 복잡도
    • 데이터 저장시 INSERT 쿼리를 두번 호출

단일 테이블 전략

  • 장점
    • 조인이 필요 없으므로 일반적으로 조회 성능이 빠름
    • 조회 쿼리가 단순함
  • 단점
    • 자식 엔터티가 매핑한 컬럼은 모두 null 허용
    • 단일테이블에 모든 것을 저장하므로 테이블이 커질수 있다 상황에 따라 오히려 느려질 가능성이 있다

구현 클래스마다 전략

  • 이 전략은 가급적이면 사용하지 않는것이 좋다
  • 장점
    • 서브 타입을 명확하게 구분해서 처리할 때 효과적
    • not null 제약조건 사용 가능
  • 단점
    • 여러 자식 테이블을 함께 조회할 때 성능이 느림
    • 자식 테이블을 통합해서 쿼리하기 어려움

상속 매핑 예시

@Entity
@Setter
@Getter
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@DiscriminatorColumn
public abstract class Item {
    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int price;
}
@Entity
@Setter
@Getter
@ToString
@DiscriminatorValue("M")
public class Movie extends Item{
    private String director;
    private String actor;
}

@MappedSuperclass

  • 공통 매핑 정보가 필요할 때 사용

  • 테이블과 관계 없고 단군히 엔터티가 공통으로 사용하는 매핑정보를 모으는 역할

  • 주로 등록일, 수정일, 등록자, 수정자 같은 전체 엔터티에서 공통으로 적용하는 정보를 모을 때 사용

  • 사용 예시

BaseEntity Test

@MappedSuperclass
@Setter
@Getter
public class BaseEntity {
    @Column(name="created_by")
    private String createdBy;
    private LocalDateTime createdTime;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;
}


@Entity
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Test extends BaseEntity{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;
    @Column
    private String name;


}

실행 결과 테스트 테이블

profile
자바를 사랑합니다

0개의 댓글