엔티티는 독립적으로 존재하는 것이 아니라 대부분 다른 엔티티와 관계를 맺고 있습니다.
자바 객체는 참조를 이용하여 관계를 맺고, 릴레이션은 외래 키를 이용해서 관계를 맺기 때문에 이들을 잘 이해하고 매핑해야 합니다. 앞으로 몇 개의 포스트들을 통해서 연관관계 매핑을 하는 방법에 대해서 알아보도록 하겠습니다.
한 쪽 엔티티만 상대를 알고 상대는 다른 쪽 엔티티를 전혀 모르는 관계를 단방향 연관관계라고 합니다.
한 쪽 엔티티에만 다른 쪽 엔티티의 참조 정보를 넣으면 되기 때문에 엔티티 구조가 간단해지지만 반대 방향의 참조가 필요한 경우에 추가적인 매핑 정보를 주어야하기도 합니다.
연관 관계 매핑에 사용되는 어노테이션은 @JoinColumn, @ManyToOne이 있습니다.
다대일(N:1) 연관관계를 매핑하기 위해 사용합니다.
1:1 관계에서는
@OneToOne을 사용합니다.
이 ERD를 보면 주문은 고객(주문자)의 정보를 가지고 있습니다. 반대로 고객은 주문에 대한 정보를 가지고 있지 않습니다.
그리고 하나의 고객은 여러 개의 주문을 가지도록 관계가 표현되었기 때문에 다대일(N:1, 주문 : 고객) 관계를 나타내고 있습니다.
위 ERD를 기반으로 단방향 연관관계를 매핑하면 다음과 같습니다.
@Entity
@Table(name = "customers_table")
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "c_id", nullable = false)
private Long id;
@Column(name = "name", nullable = false, length = 100)
private String name;
//생성자 및 getter
}
@Entity
@Table(name = "orders_table")
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "o_id", nullable = false)
private Long id;
@Column(name = "order_date", nullable = false)
private LocalDateTime orderDate;
/**
* 단방향 @ManyToOne
* orders_table.c_id → customers_table.c_id 외래 키 매핑
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "c_id", nullable = false)
private Customer customer;
//생성자 및 getter
}
주문 Order만이 Customer 엔티티의 정보를 알 고 있기 때문에 Order에 Customer 참조 필드를 추가하고 @ManyToOne을 사용했습니다.
@ManyToOne에서 사용가능한 속성은 다음과 같습니다.
| 속성 | 설명 |
|---|---|
| optional | false 설정 시 연관 엔티티를 항상 설정해야함 |
| fetch | fetch 전략 설정 |
| cascade | 영속성 전이 설정 |
| targetEntity | 연관 엔티티의 타입 정보를 설정 |
fetch전략에는 다음 두 가지 방식이 있습니다.
- FetchType.EAGER: 엔티티 조회 시 연관 엔티티까지 함께 조회
- FetchType.LAZY: 연관 엔티티를 실제 사용 시점에 조회
Fetch 전략에 대한 더 자세한 내용은 추후에 따로 다룰 예정입니다.
영속성 전이는 특정 엔티티를 영속 상태로 만들 때 관계된 엔티티들도 함께 영속 상태로 만드는 기능입니다. Type에 따라 다양한 영속성 전이 옵션을 제공하고 있습니다.마찬가지로 추후에 더 자세하게 다루려고 합니다.
@JoinColumn은 외래 키 컬럼을 매핑할 때 사용합니다. 위 예시에선 외래키인 Order의 Customer.c_id가 외래 키입니다.
/**
* 단방향 @ManyToOne
* orders_table.c_id → customers_table.c_id 외래 키 매핑
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "c_id", nullable = false)
private Customer customer;
@JoinColumn에서 사용가능한 속성들은 다음과 같습니다.
| 속성 | 설명 |
|---|---|
| name | 매핑할 외래 키 이름 |
| referencedColumnName | 외래 키가 참조하는 대상 테이블 컬럼 명 미사용 시 참조 대상 테이블의 기본 키 컬럼명을 사용 |
| foreignKey(DDL) | 외래 키 제약조건 설정 |
이 외에도
@Column에서 사용 가능한 속성들을 사용할 수 있습니다.
연관관계 엔티티를 CRUD 하는 방법에 대해 알아보겠습니다.
Create
생성은 위에서 본 방식대로 연관관계 매핑에 사용되는 어노테이션들을 붙여서 생성합니다.
Read
읽기(조회)는 객체 그래프 탐색, JPQL 두 가지 방식으로 조회합니다.
객체 그래프 탐색: 객체 연관관계를 이용하여 order.getCustomer()로 연관관계의 Customer를 조회할 수 있습니다.JPQL: JOIN을 활용하여 SELECT 구문을 통해 조회합니다.Update
수정은 setter 등을 통해 트랜잭션에서 Order 연관관계에 있는 Customer에 변경 사항이 발생하면 변경 감지 Dirty Check를 사용해서 자동으로 UPDATE가 수행됩니다.
Delete
연관관계가 포함된 엔티티를 삭제할 때는 연관관계를 먼저 제거해야합니다. 외래 키 제약조건이 걸려있기 때문에 그냥 삭제하면 오류가 발생합니다. 다음과 같이 연관관계 엔티티를 제거한 후 삭제를 수행해야합니다.
order.setCustomer(null); //연관관계 먼저 삭제
em.remove(order); //그 후 엔티티 삭제