본문 바로가기
JPA/ORM 표준 JPA

06 연관관계 Mapping 종류

by clearinging 2021. 8. 21.
반응형

다중성과 연관관계 종류

다대일

  • 현재 객체는 하나의 객체를 참조할 수 있는 구조

1, 단방향

@Entity
public class User {

    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private Long id;

    private String username;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="team_id") // 연과관계의 주인이 되는 곳
    private Team team;

    // .. codes
}

@Entity
public class Team {

    @Id
    @GeneratedValue
    @Column(name = "team_id")
    private Long id;

    private String name;
}
  1. 양방향
@Entity
public class User {

    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private Long id;

    private String username;

    @ManyToOne(fetch = FetchType.LAZY) // 연관관계 주인
    @JoinColumn(name="team_id")
    private Team team;

    // .. codes
}

@Entity
public class Team {
    @Id
    @GeneratedValue
    @Column(name = "team_id")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team") // user 객체의 필드 변수 이름
    private List<User> users = new ArryaList<>();
}
  • 앵간하면 왜래키가 존재하는 entity를 주인으로 해주면 좋습니다 -> 실제 reference key의 위치를 entity를 보고 이해할 수 있기 때문
  • 편의 method 인 changeTeam이나 addUser를 호출하게 되면 무한 루프를 발생시킬 수 있기 때문에 구현할때 조심해야 합니다.

일대다

  • 현재 entity객체가 여러개를 참조할 수 있는 구조
  • collection 프레임 워크의 정보 중 1개를 사용
  • 다대일 의 Team이라고 생각하면 됩니다
  • 주로 team에서 mappedBy를 사용해서 거울 역할만 하게 해야지 좋습니다. -> 연관관계 주인을 설정하기 위해서
  • 단점
    • 외래키가 다른 table에 존재 -> mappedBy 사용해서 거울로 바꾸기
    • 외래키가 다른 table 에 존재 하기 때문에 insert sql를 생성하게 되면, 다른 reference 객체의 table에 update query를 날려야 해서 서능 이슈 발생

일대일

  1. 특징
  • refernec key를 가지고 주 테이블과 부 테이블 모두 원하는 정보를 조회 가능
  1. 외래키를 두는 위치
  • 개발자 관점 : 주테이블에 위치
    • 객체 참조와 외래키 참조의 모습이 비슷해서 선호
  • DBA관점 : 부 테이블에 위치
    • 나중에 OneToOne에서 OneToMany 로 구조를 바꿀 때 테이블 변경 없이 사용 가능합니다
  1. 단방향
  • JPA는 부 Table의 외래키가 존재하고, 주 Table Entity객체에 연관관계를 설정할 수 없게 맊아 놨습니다
    테이블 구조
    • 이 구조를 설정하고 싶다면 양방향 관계를 해야합니다.
  1. 양방향 구조
  • 위에서 언급한 것과 동일하게 부 table에 외래키가 존재할 경우 사용하는 구조 입니다

다대다

  • table구조에서는 연결 테이블이 추가되는 형태이다
  • @ManyToMany로 연결
  1. 단방향
@Entity
public class User { 
    @Id 
    @GeneratedValue 
    @Column(name = "user_id") 
    private Long id; 

    private String username; 

    @ManyToMany 
    @JoinTable(name = "user_role", 
        joinColumns = @JoinColumn(name = "user_id"), 
        inverseJoinColumns = @JoinColumn(name = "role_id")) 
    Set<Role> roles = new HashSet<>(); 
}

@Entity  
public Role role {
  @Id
  @GeneratedValue
  @Column(name = "role_id")
  private Long id;

  private String name;
}
  • @JoinTable.name : 연결 테이블 지정, 현재 예시 에서는 user_role로 설정했습니다.
  • @JoinTable.joinColumns : 현재 User table를 Mapping 할 Column이름을 설정합니다. 현재 예시에는 user_id로 설정했습니다.
  • @JoinTable.inverseJoinColumns : 반대 방향의 table을 참조할 연결 테이블의 column 명을 설정하기 위해서 사용합니다. 현재 예시 에서는 role_id로 설정 했습니다.
    • 위의 구조로 인해서 user_role이라는 table 은 user_id, role_id를 가지게 됩니다.
  1. 양방향
@Entity
public class User {
    @Id
    @GeneratedValue
    @Column(name = "user_id")
    private Long id;

    private String username;

    @ManyToMany
    @JoinTable(name = "user_role",
        joinColumns = @JoinColumn(name = "user_id"),
        inverseJoinColumns = @JoinColumn(name = "role_id"))
    Set<Role> roles = new HashSet<>();
}

@Entity
public Role role {

    @Id
    @GeneratedValue
    @Column(name = "role_id")
    private Long id;

    private String name;
    @ManyToMany(mappedBy = "roles")
    private List<User> users = new ArryayList<>();
}
  1. 한계점
  • 연결 테이블의 정보를 확장하기 어렵습니다.
  • 만약 회원 권한 정보가 변경된 시점을 알고싶을 경우 user_role table에 updatetime이나, insertdate column 이 필요하지만 확인할 수 없습니다.
  • 해결법
    • 중간에 연결 테이블을 실제 Entity class로 만듭니다
    • User와 Role과 MnayToOne 관계로 Mapping 합니다
    • UserRole 이라는 Entity 객체를 확장할 수 있기 때문에 더 좋은 구조입니다.
  • 연결 Table 코드
@Entity
@IdClass(UserRoleId.class)
public class UserRole {
    @Id
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @Id
    @ManyToOne
    @JoinColumn(name = "role_id")
    private Role role;

    private LocalDatetime inserttime;
}

public class UserRoleId implements Serializable {
    private String user;
    private String role;

    @Overrid
    public boolean equals(Object o) {
        //... equal code
    }

    @Overrid
    public int hashCode() {
        // ...
    }
}
  • 복합키
    • 별도의 식별자 Id를 class로 생성해야합니다.
    • 영속성 컨택스트에서 Map 과 같은 곳에 저장하게 되는데 그때 데이터를 식별할 수 있도록 hashCode와 equals를 재저의 해야합니다.
    • 복합 key 객체는 default 생성자가 있어야합니다.
    • @IdClass외에 @EmbeddedId를 사용하는 경우도 존재합니다
  • 복합키를 사용한 Id 는 나중에 클러스터 키로인해 변경이되면 성능 이슈로 이어질 수 있습니다. 그러므로 새로운 대리키를 사용하는 것을 권장 합니다.
반응형

'JPA > ORM 표준 JPA' 카테고리의 다른 글

Atomic Query 작성  (0) 2022.04.28
Fetch Join 별칭 이슈  (2) 2022.01.22
05 연관관계  (0) 2021.07.28
04 Entity  (3) 2021.07.08
03. 영속성  (0) 2021.06.30