Entity의 연관 관계 - @OneToMany -
1 대 N 관계
1 : N 관계를 맺어주는 역할을 한다.
상품 Entity와 고객 Entity가 1 : N 관계라고 가정
단방향 관계
고객 Entity가 N의 관계로 외래 키를 가지고 있지만 외래 키를
관리하는 주인은 product entity
관계가 N인 users 테이블에 추가한 후 상품 Entity를 통해 관리한다.
Product Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@NoArgsConstructor
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@OneToMany
@JoinColumn(name = "product_id") // users 테이블의 product_id 컬럼
private List<User> userList = new ArrayList<>();
public Product(String name, double price) {
this.name = name;
this.price = price;
}
}
@OneToMany에서는
앞의 Entity가 주인 관계라고 생각하면 편하다.
"user"라는 테이블에 리스트를 만들어서 외래키를 저장하고 관리하라는 의미
User Entity
import jakarta.persistence.*;
import lombok.Getter;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Table(name = "users")
@NoArgsConstructor
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public User(String name) {
this.name = name;
}
}
외래 키를 Product Entity가 직접관리 한다면 INSERT 발생 시 한 번에 처리할 수 있지만
외래키를 User Entity가 가지고 있기 때문에 추가적으로 UPDATE가 발생되는 단점이 있다.
테스트 전 Product Entity에 추가해 주는 코드
public void addUserList(User user) {
this.userList.add(user);
}
테스트 코드
package com.practice.practice.relation;
import com.practice.practice.entity.Product;
import com.practice.practice.entity.User;
import com.practice.practice.repository.ProductRepository;
import com.practice.practice.repository.UserRepository;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
@SpringBootTest
@Transactional
public class OneToMany {
@Autowired
ProductRepository productRepository;
@Autowired
UserRepository userRepository;
@Test
@Rollback(value = false)
@DisplayName("1대N 단방향 테스트")
void test1() {
User user = new User("John");
userRepository.save(user);
User user2 = new User("Kate");
userRepository.save(user2);
Product product = new Product("곰인형", 15000);
productRepository.save(product);
product.addUserList(user);
product.addUserList(user2);
// 추가적인 UPDATE 쿼리 발생을 확인할 수 있습니다.
}
}
결과
Hibernate:
create table product (
id bigint not null auto_increment,
name varchar(255),
price float(53) not null,
primary key (id)
) engine=InnoDB
Hibernate:
create table users (
id bigint not null auto_increment,
name varchar(255),
product_id bigint,
primary key (id)
) engine=InnoDB
Hibernate:
alter table users
add constraint FK5620qugunmt2cvcs129ybq4w7
foreign key (product_id)
references product (id)
Hibernate:
/* insert com.practice.practice.entity.User
*/ insert
into
users (name)
values
(?)
Hibernate:
/* insert com.practice.practice.entity.User
*/ insert
into
users (name)
values
(?)
Hibernate:
/* insert com.practice.practice.entity.Product
*/ insert
into
product (name, price)
values
(?, ?)
Hibernate:
/* create one-to-many row com.practice.practice.entity.Product.userList */ update
users
set
product_id=?
where
id=?
Hibernate:
/* create one-to-many row com.practice.practice.entity.Product.userList */ update
users
set
product_id=?
where
id=?
외래키를 가지고 있지는 않지만 연관 관계의 주인이기 때문에
List에 user를 넣어주기만 하더라도 자동으로 추적하여 데이터를 넣어준다.
조회 테스트
@Test
@DisplayName("1대N 조회 테스트")
void test2() {
Product product = productRepository.findById(1L).orElseThrow(NullPointerException::new);
System.out.println("product.getName() = " + product.getName());
// 해당 음식을 주문한 고객 정보 조회
List<User> userList = product.getUserList();
for (User user : userList) {
System.out.println("user.getName() = " + user.getName());
}
}
결과
Hibernate:
select
p1_0.id,
p1_0.name,
p1_0.price
from
product p1_0
where
p1_0.id=?
product.getName() = 곰인형
Hibernate:
select
u1_0.product_id,
u1_0.id,
u1_0.name
from
users u1_0
where
u1_0.product_id=?
user.getName() = John
user.getName() = Kate
@OneToMany는 default로 지연로딩이다.
처음에는 product에서 조회를 했지만,
getUserList();를 하여 필요한 정보를 인식하고
users 테이블에서 SELECT 하여 정보를 가지고 오고 있다.
양방향 관계
1 : N 관계에서 일반적으로 양방향 관계는 존재하지 않는다.
1 : N 관계에서 양방향 관계를 맺으려면 상품 Entity를 외래 키의 주인으로 정해주기 위해
고객 Entity에서 mappedBy 옵션을 사용해야 하지만 @ManyToOne 애너테이션은
mappedBy 속성을 제공하지 않는다.
N 관계의 Entity인 고갱 Entity에서 @JoinColum의 insertable과 updatable 옵션을
false로 설정하여 양쪽으로 JOIN 설정을 하면 양방향처럼 설정 할 수는 있다.
내용 참고 - 스파르타 코딩클럽 -
'자바 탐구' 카테고리의 다른 글
스프링) SpringSecurity - 1) Security, Mustache 환경 설정 (0) | 2023.05.26 |
---|---|
JPA) Entity의 연관 관계 - @ManyToMany - (0) | 2023.05.03 |
JPA) Entity의 연관 관계 - @ManyToOne - (0) | 2023.05.03 |
JPA) Entity의 연관 관계 - @OneToOne - (0) | 2023.05.02 |
JPA) 데이터베이스 테이블과 Entity의 연관 관계 표현 차이 (0) | 2023.05.02 |