1) 두 엔티티 간 생성할 수 있는 연관관계
- 일대일 (One To One)
- 다대일 (Many To One)
- 일대다 (One To Many)
- 다대다 (Many To Many)
- DB에서는 컬럼에 외래키 속성을 주면 다른 테이블의 기본키를 얻을 수 있고, Join으로 참조할 수 있다.
- JPA에서는 엔티티에서 참조 방향에 맞게 관계를 설정한다.
2) 관계 매핑 방향
- 단방향: 한쪽이 다른 한쪽의 엔티티만 참조하는 관계
- 양방향: 양쪽이 서로 참조할 수 있는 관계
-> (일대일, 다대일, 일대다, 다대다) - (단방향, 양방향) 각 조합이 모두 나올 수 있다.
-> 객체 지향 관점에서이며, 실제 DB에선 따로 방향이 없다. (외래키가 있는 쪽, 없는 쪽, 또는 다대다)
1.1. 일대일 (1:1) 단방향
: 두 엔티티가 서로 하나씩만 매핑될 수 있고 한 쪽에서만 참조할 수 있다.
@OneToOne
@JoinColumn(name="product_id")
private Product product;
- owner 엔티티(외래키를 가질 엔티티)에서 다른 엔티티 객체를 필드로 갖고, OneToOne 어노테이션으로 매핑한다.
OneToOne
- optional: true (nullable), false (notNull)
- fetch: EAGER (즉시 로딩)
- mappedBy: (양방향에서) 상대 엔티티에도 읽기 전용으로 매핑 정보를 줄 수 있으며, owner 엔티티에서의 필드 이름(나의 id를 외래키로 갖고 있는 필드의 이름)을 값으로 넣는다. -> mappedBy가 설정된 엔티티는 주인이 아니므로 이 값이 바뀌어도 DB에 영향을 주지 않는다.
JoinColumn
- name: 외래키 컬럼 이름 설정 - 선언하지 않아도 자동으로 이름이 생성되지만 매핑하는 중간 테이블이 생긴다.
- referencedColumnName: 외래키가 참조할 상대 테이블의 컬럼명 - 기본적으로 기본키(id) 값이 들어간다.
- foreignKey: 외래키 제약조건 설정 (unique, nullable, insertable, updatable)
- repository에서 참조하는 엔티티를 조회해오면 left outer join을 이용해 양쪽 객체를 함께 조회한다. (즉시 로딩)
(left outer join : 매핑된 외래키가 없어도 현재 엔티티의 값은 모두 출력)
- 외래키를 가지고 있는 테이블이 그 외래키 컬럼의 이름을 표시하기 위해 사용한다.
1.2. 일대일 (1:1) 양방향
: 일대일 단방향에서 매핑하지 않았던 다른 엔티티도 일대일 매핑한다.
- mappedBy 설정을 하지 않으면 양쪽에 외래키가 생겨 동일한 두 테이블 간 join이 두 번 일어나 비효율적이게 된다.
- ToString.Exclude로 순환 참조를 방지해야 한다.
-> 필요하지 않으면 일대일 단방향 매핑을 사용한다.
2.1. 다대일 (n:1) 단방향
: 엔티티의 한 컬럼이 다른 엔티티의 여러 컬럼에 매핑될 수 있는 경우에서 n에 해당하는 엔티티에 1의 외래키를 지정하는 것.
@ManyToOne
public Provider provider;
- 여러 컬럼이 올 수 있는 엔티티에서 매핑할 다른 엔티티 객체에 ManyToOne 어노테이션을 추가한다.
2.2. 다대일 (n:1) 양방향 - 일대다 (1:n)
: 다대일에서 양방향으로 연결하기 위해 1에 해당하는 엔티티에 일대다 속성을 추가한다.
@OneToMany(mappedBy="provider")
public List<Product> products = new ArrayList<>();
OneToMany
- 여러 외래키가 매핑될 수 있으므로 컬렉션(List 등)으로 받는다.
- JoinColumn을 사용하여 상대 엔티티에 추가할 외래키를 설정.
- 디폴트 fetch 전략은 Lazy(지연로딩)으로 연관된 데이터를 가져오지 않고 현재 테이블 데이터만 가져온다.
3. 일대다 (1:n) 단방향
: 1에 해당하는 엔티티에서 매핑 정보를 가지는 경우.
- 설정한 엔티티가 아닌 상대 엔티티에 외래키가 생성된다. => Many(List) 필드를 가지는 엔티티는 주인이 될 수 없다.
- 외래키 설정을 위해 해당 엔티티 리스트에 추가하여 저장해야 하며 추가한 테이블에 대한 update 쿼리가 발생한다.
-> 다대일 연관관계를 쓰는 것이 좋다.
4. 다대다 (n:n)

: 한 테이블의 여러 데이터가 다른 테이블의 여러 데이터와 매핑될 수 있는 경우이며 각 엔티티에서 서로를 리스트로 가진다.
- 중간 테이블(교차 엔티티)을 생성하여 일대다 구조로 만들어 사용한다.
- 중간 엔티티를 만들어 JPA에서 관리할 수 있게 하는 것이 좋다.
- 실무에서는 잘 사용되지 않는 구조
4.1. 다대다 (n:n) 단방향
// Producer Entity
@ManyToMany
@JoinTable(name="middle_table")
private List<Product> products = new ArrayList<>();
// 사용
Producer producer = new Producer();
producer.setName("name");
List<Product> list = new ArrayList<>();
list.add(new Product()); // 저장될 Product는 DB에 저장되어 있어야 함.
producer.setProducts(list);
producerRepository.save(producer);
: 한쪽에 ManyToMany로 다른쪽 엔티티 List를 필드로 갖는다. 중간 테이블이 생기므로 실제 양쪽 엔티티에 외래키 컬럼이 생기지는 않는다.
4.2. 다대다 (n:n) 양방향
: 단방향에서 다른 엔티티에도 ManyToMany 어노테이션으로 필드를 추가한다. 실제 DB의 테이블 구조는 단방향과 같다.
영속성 전이
: 엔티티의 영속성 상태를 변경할 때 그 엔티티와 연관된 엔티티의 영속성도 변경하는 것
cascade
: 연관관계매핑 어노테이션에 정의된 영속성 전이 설정에 사용하는 요소로 주어진 상태 변경이 일어나면 매핑돼있는 엔티티에도 동일한 동작이 일어나도록 한다.
- ALL : 모든 변경에 대해 영속성 전이 (아래 속성 모두 포함)
- PERSIST : 엔티티가 영속화할 때 연관된 엔티티도 함께 영속화 (비영속 -> 영속)
- MERGE : 엔티티를 영속성 컨텍스트에 병합할 때 연관된 엔티티도 병합 (준영속 -> 영속)
- REMOVE : 엔티티를 제거할 때 연관된 엔티티도 제거 (삭제)
- REFRESH : 엔티티를 새로고침할 때 연관된 엔티티도 새로고침 (영속 엔티티 갱신)
- DETACH : 엔티티를 영속성 컨텍스트에서 제외하면 연관된 에티티도 제외 (영속 -> 준영속)
- 엔티티 생명주기: 비영속, 영속, 준영속, 삭제
- 리스트로 여러개 사용 가능
persist
// Provider 엔티티에서 Product 엔티티를 영속성 전이
@OneToMany(mappedBy="provider", cascade = Cascade.PERSIST)
@ToString.Exclude
private List<Product> products = new ArrayList<>();
// 사용
Provider provider = new Provider(..);
Product product1 = new Product(..);
Product product2 = new Product(..);
provider.getProducts().addAll(Lists.newArrayList(product1, product2));
providerRepository.save(provider); // Product 테이블에도 product1, product2가 저장됨
=> 엔티티를 저장하면 cascade로 매핑돼있는 엔티티를 따로 저장하지 않아도 저장할 수 있게 된다.
고아 객체
: 부모 엔티티와 연관관계가 끊어진 엔티티.
- 연관관계 어노테이션에서 orphanRemoval = true로 주면 연관관계가 끊어진 엔티티를 자동 삭제해준다.
@OneToMany(orphanRemoval=true)
private List<Product> products = new ArrayList<>();
-> 위 엔티티의 Product List에서 데이터를 삭제하면 Product 테이블의 데이터도 삭제된다.
'공부 > 백엔드' 카테고리의 다른 글
| [스프링 부트 핵심 가이드] 11 : 액추에이터 활용하기 (1) | 2024.06.08 |
|---|---|
| [스프링 부트 핵심 가이드] 10 : 유효성 검사와 예외 처리 (0) | 2024.05.31 |
| [스프링 부트 핵심 가이드] 8 : Spring Data Jpa (0) | 2024.05.19 |
| [스프링 부트 핵심 가이드] 6 : 스프링 데이터베이스 연동 (0) | 2024.05.10 |
| [스프링 부트 핵심 가이드] 4-5 : 스프링부트 APP 개발, API 개발 (0) | 2024.04.29 |