기존의 데이터베이스에서는 외래 키(FK)를 사용하나 객체로 표현하기 어려운 데이터 간의 관계를 위해 JPA에서는 객체를 참조하는 방식으로 연관관계를 매핑할 수 있다.
연관관계 매핑 시 고려사항 3가지
- 다중성
- 다대일 [N:1] : @ManyToOne
- 일대다 [1:N] : @OneToMany
- 일대일 [1:1] : @OneToOne
- 다대다 [N:M] : @ManyToMany
- 방향
- 단방향
- 양방향 : 양방향 관계는 없고 두 객체가 단방향 참조를 각각 가져서 양방향 관계처럼 사용하는 것.
- 연관관계의 주인
- FK(외래 키) 하나로 두 개의 테이블이 연관관계를 맺는다.
- 연관관계의 주인: 외래 키를 관리하는 참조
- 양방향 관계는 참조가 2개라 연관 관계인 두 개의 테이블 중 외래 키를 관리하는 곳을 지정해야 한다. 연관관계의 주인을 지정하는 것은 두 단방향 관계 중, 제어의 권한(외래 키를 비롯한 테이블 레코드 CRUD)을 갖는 실질적인 관계가 어떤 것인지 JPA에게 알려줘야 하기 때문이다.
- 연관관계의 주인이 아닌 객체에서 mappedBy 속성을 사용해서 주인을 지정해줘야 한다.
다대일 [N:1]
유저(User)과 팀소속(Team)으로 예를 들어보자.
- 요구 사항
- 하나의 팀(1)당 여러 유저(N)을 가입할 수 있다.(User N : Team 1)
- 팀과 유저은 다대일 관계를 갖는다.
외래 키를 유저(N)가 관리하는 일반적인 형태다. (참고로 데이터베이스는 무조건 다(N)쪽이 외래 키를 갖는다.)
다대일 [N:1] 단방향
@Entity
@Getter
@Setter
public class User {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long id;
@Column(name = "NAME")
private String name;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue
private Long id;
private String team_name;
}
단방향의 경우 User에만 @ManyToOne을 추가하였다.
다대일 [N:1] 양방향
@Entity
@Getter
@Setter
public class User {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long id;
@Column(name = "NAME")
private String name;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue
private Long id;
private String team_name;
@OneToMany(mappedBy = "team")
List<User> users = new ArrayList<>();
}
다대일 양방향으로 만들기 위해서는 Team(1)에 @OneToMany를 추가하여 양방향 매핑을 사용하고 mappedBy속성을 사용해 연관관계를 지정해준다.
mappedBy로 지정할 때 값은 대상이 되는 변수명을 따라 지정해준다. (User 객체의 Team 변수명이 team으로 되어있어서 mappedBy = "team"으로 지정)
일대다 [1:N]
DB에서는 외래키를 N에서 관리하지만 1에서 N쪽을 제어(CRUD)할 수 있는 방법이다.
※권장하지 않는다.
일대다 [1:N] 단방향
@Entity
@Getter
@Setter
public class User {
@Id
@GeneratedValue
@Column(name = "USER_ID")
private Long id;
@Column(name = "NAME")
private String name;
}
@Entity
@Getter
@Setter
public class Team {
@Id
@GeneratedValue
private Long id;
private String team_name;
@OneToMany
@JoinColumn(name = "USER_ID") //일대다 단방향 @JoinColumn필수
List<User> users = new ArrayList<>();
}
일대다 [1:N] 단방향은 치명적인 단점을 가지고 있다.
연관관계 관리를 위해 추가로 UPDATE SQL이 실행된다는 점이다.
분명 TEAM(1)만 INSERT했는데 USER(N)을 UPDATE하는 쿼리가 나가게 된다.
TEAM엔티티는 TEAM 테이블에 매핑이 돼서 TEAM테이블에서 직접 지정가능하지만 USER의 경우 엔티티가 관리하는 외래키가 다른 테이블에 있어서 USER테이블의 외래키를 저장할 방법이 없기 때문에 조인 및 업데이트 쿼리가 나가게된다.
일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용하자.
일대다(1:N) 양방향
// USER 클래스
@ManyToOne
@JoinColumn(name="TEAM_ID", insertable = false, updatable = false) //중요!!
private Team team;
공식적으로 지원하는 게 아니기 때문에 일대다 양방향은 사용하지 않는게 좋다.(실무에서도 사용하지않는다한다.)
읽기전용필드를 사용해서 양방향처럼 사용하는 방법이지만 알아만 두고 사용하지 말자. 🙅♀️
참고
'스프링' 카테고리의 다른 글
@Controller와 @RestController 차이 (0) | 2022.03.23 |
---|---|
Domain(Entity) / DTO / DAO (0) | 2022.03.14 |
Annotation - @Entity (0) | 2022.03.09 |
Spring Bean (0) | 2022.02.20 |
[SpringBoot] PasswordEncoder 적용하기 (0) | 2022.02.18 |