Everyday Dev System

@OneToMany 단방향 본문

내배캠 주요 학습/Spring 숙련

@OneToMany 단방향

chaeyoung- 2023. 6. 21. 13:16
1대다 관계 말고는 일반적으로 외래키는 외래키의 주인이 DB에 갖고 있다.
1대다 관계에서 외래키의 주인이 외래키를 갖고 있지 않으니까 추가적인 update문이 발생한 것이다.

 

1대 N 관계 단방향

1대 N관계 에서는 1의 주인이 외래키의 주인이다.

외래키의 주인은 음식 Entity이지만, 실제 DB 테이블에는 외래키를 고객이 가지고 있는 형태이다.

 

외래키의 주인인 음식 Entity 외래키 필드이다.

Food가 1의 관계이기 때문에 고객 Entity를 여러명을 표현하기 위해서 Java Collection인 List 활

@OneToMany
@JoinColumn(name = "food_id")
private List<User> userList = new ArrayList<>();
@JoinColumn(name = "food_id")
private List<User> userList = new ArrayList<>();

1 대 N 단방향 관계에서는
N의 관계인 테이블이 User이기 때문에 외래키를 User 테이블에 놔야 한다.

그러므로, Food Entity에 food_id 칼럼을 기재하여 user Table에 food_id 컬럼이 생기도록 한다.

외래키의 주인은 Food 이지만 실제 테이블에 저장되는 것 외래키의 주인인 Food가 아닌
상대 Entity인 User이다. 그러므로 상대 Entity의 외래키를 Food Entity가 잠시 저장하고 있는 것이다.

N 대 1 단방향 일 때는 
실제 DB 테이블에 외래키를 갖는 Entity가 Food 본인이였기 때문에
Food가 외래키를 갖는 것이므로 아래와 같이 표현하는 것이 맞다.
@JoinColumn(name = "user_id")
private User user;


1 대 N 단방향 관계에서는 update가 추가적으로 발생된다는 문제 한계점이 발생한다.


1 대 N 관계에서는 일반적으로 양방향 관계는 미존재 
1 대 N 양방향을 JPA 자체에서 미지원


@OneToMany는 mappedBy 옵션 사용 가능

@ManyToOne는 mappedBy 옵션 사용 불가

 

package com.sparta.jpaadvance.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Setter
@Table(name = "food")
public class Food {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private double price;

    @OneToMany
    @JoinColumn(name = "food_id") // users 테이블에 food_id 컬럼
    private List<User> userList = new ArrayList<>();
}
package com.sparta.jpaadvance.entity;

import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;

@Entity
@Getter
@Setter
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}
package com.sparta.jpaadvance.relation;

import com.sparta.jpaadvance.entity.Food;
import com.sparta.jpaadvance.entity.User;
import com.sparta.jpaadvance.repository.FoodRepository;
import com.sparta.jpaadvance.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;

@Transactional
@SpringBootTest
public class OneToManyTest {

    @Autowired
    UserRepository userRepository;
    @Autowired
    FoodRepository foodRepository;

    @Test
    @Rollback(value = false)
    @DisplayName("1대N 단방향 테스트")
    void test1() {
        User user = new User();
        user.setName("Robbie");

        User user2 = new User();
        user2.setName("Robbert");

        Food food = new Food();
        food.setName("후라이드 치킨");
        food.setPrice(15000);
        food.getUserList().add(user); // 외래 키(연관 관계) 설정
        food.getUserList().add(user2); // 외래 키(연관 관계) 설정

        userRepository.save(user);
        userRepository.save(user2);
        foodRepository.save(food);

        // 추가적인 UPDATE 쿼리 발생을 확인할 수 있습니다.
    }
    
    
    @Test
    @DisplayName("1대N 조회 테스트")
    void test2() {
    	Food food = foodRepository.findById(1L).orElseThrow(NullPointerException::new);
    	System.out.println("food.getName() = " + food.getName());

    	// 해당 음식을 주문한 고객 정보 조회
    	List<User> userList = food.getUserList();
    	for (User user : userList) {
    	System.out.println("user.getId() = " + user.getId());
    	System.out.println("user.getName() = " + user.getName());
    }
}

 

 

중요한 코드 :

Food.java

    @OneToMany
    @JoinColumn(name="food_id")
    private List<User> userList = new ArrayList<>();

OneToManyTest.java

        User user = new User();
        user.setName("Robbie");

        User user2 = new User();
        user2.setName("Robbert");

        Food food = new Food();
        food.setName("후라이드 치킨");
        food.setPrice(15000);
        food.getUserList().add(user); // 외래 키(연관 관계) 설정
        food.getUserList().add(user2); // 외래 키(연관 관계) 설정

        userRepository.save(user);
        userRepository.save(user2);
        foodRepository.save(food);

User 객체를 생성 -> Food 객체를 생성 -> Food 객체의 UserList 필드에 생성한 User 객체 설정

 

 

결과 :

1. 1 대 N 단방향 데이터 넣기

 

 

Hibernate SQL query :

1. 1 대 N 단방향 데이터 넣기

Hibernate: 
    /* insert for
        com.sparta.jpaadvance.entity.User */insert 
    into
        users (name) 
    values
        (?)
Hibernate: 
    /* insert for
        com.sparta.jpaadvance.entity.User */insert 
    into
        users (name) 
    values
        (?)
Hibernate: 
    /* insert for
        com.sparta.jpaadvance.entity.Food */insert 
    into
        food (name,price) 
    values
        (?,?)
Hibernate: 
    update
        users 
    set
        food_id=? 
    where
        id=?
Hibernate: 
    update
        users 
    set
        food_id=? 
    where
        id=?

 

2. 1 대 N 단방향 데이터 조회

Hibernate: 
    select
        f1_0.id,
        f1_0.name,
        f1_0.price 
    from
        food f1_0 
    where
        f1_0.id=?
food.getName() = 후라이드 치킨
Hibernate: 
    select
        u1_0.food_id,
        u1_0.id,
        u1_0.name 
    from
        users u1_0 
    where
        u1_0.food_id=?
user.getId() = 1
user.getName() = Robbie
user.getId() = 2
user.getName() = Robbert

'내배캠 주요 학습 > Spring 숙련' 카테고리의 다른 글

@ManyToMany 양방향 관계 이해하기  (0) 2023.06.21
@ManyToMany 단방향 관계 이해하기  (0) 2023.06.21
@ManyToOne 활용하기  (0) 2023.06.21
@OneToOne 외래키 활용  (0) 2023.06.21
Entity 연관관계  (0) 2023.06.20