목차
테이블 설계
일대다 연관관계를 고객이 주문한 음식(Food)과 고객(Member)을 예시로 설명하겠습니다.
- 하나의 음식(Food)는 여러명의 고객(Member)에게 주문될 수 있다.
- 음식(Food) : 고객(Member) = 1 : N
단방향 연관관계를 위한 추가 조건은 다음과 같습니다.
- One에 해당하는 Entity(Food Entity)가 외래 키를 관리하며, 외래 키의 주인입니다.
- Food Entity는 Food.memberList 필드를 통해서 음식을 주문한 고객 목록(List<Member>)에 접근할 수 있습니다.
- Member Entity는 주문한 음식 Entity인 Food Entity에 접근할 수 없습니다.
- 일대다 연관관계는 표준 스펙이서 지원하고 있지만, 실무에서는 권장하지 않는 연관관계입니다.
- 이 연관관계는 One 관계인 Food Entity가 외래키를 관리하고, Many 관계인 ManyMember Entity가 외래키를 소유하고 있는 특이한 구조를 갖습니다.
Entity 관계 매핑
- Food Entity
- 일대다에서 일(One)의 관계를 갖습니다.
@Entity
public class Food {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
@OneToMany
@JoinColumn(name = "food_id") // merber 테이블에 food_id 컬럼
private List<Member> memberList = new ArrayList<>();
public void addMemberList(Member member) {
this.memberList.add(member);
}
// constructor, getter, setter, ...
}
- Member Entity
- 일대다에서 다(Many)의 관계를 갖습니다.
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// constructor, getter, setter, ...
}
- 다대일 단방향 매핑에서는 One에 해당하는 Entity(Food Entity)에서는 Many에 해당하는 Entity(Member Entity)를 Collection 프레임워크로 감싼 형태의 참조 필드로 작성합니다.
- 참조 필드 위에
@OneToMany
애노테이션과@JoinColumn(name = "{foreign key name}")
애노테이션을 추가로 작성합니다. - 위의 Entity 관계를 적용하여 테이블을 생성하였을 때, 실행되는 SQL은 아래와 같습니다.
create table food (
id bigint not null auto_increment,
name varchar(255),
price float(53) not null,
member_id bigint,
primary key (id)
);
create table member (
id bigint not null auto_increment,
name varchar(255),
food_id bigint,
primary key (id)
);
alter table member
add constraint FKnyfi1wxbgo0pqliou7xkfe4lw
foreign key (food_id)
references food (id)
Entity 관계 사용법
- 저장
@Transactional
@SpringBootTest
public class OneToManyTest {
@Autowired FoodRepository foodRepository;
@Autowired MemberRepository memberRepository;
@Test
@Rollback(value = false)
@DisplayName("1대N 단방향 저장 테스트")
void OneWaySaveTest() {
// 음식(Food)1
Food food = new Food("후라이드 치킨", 15000);
foodRepository.save(food);
// 고객(Member)1
Member member1 = new Member("Inyong");
memberRepository.save(member1);
food.addMemberList(member1);
// 고객(Member)2
Member member2 = new Member("Robert");
memberRepository.save(member2);
food.addMemberList(member2);
}
}
- 위 코드 실행 시, 적용되는 SQL은 다음과 같습니다.
- 외래키의 주인과 외래키를 소유한 Entity가 서로 다르기 때문에, INSERT 이후 UPDATE를 사용하여 외래키를 입력해줘야 합니다.
- 추가로 발생하는 UPDATE SQL로 인한 성능적 이슈와 구조적 복잡함이
@OneToMany
단방향 연관관계의 단점입니다.
INSERT INTO FOOD (MEMBER_ID, NAME, PRICE) VALUES (NULL, "후라이드 치킨", 15000);
INSERT INTO MEMBER (NAME) VALUES ('Inyong');
INSERT INTO MEMBER (NAME) VALUES ('Robbert');
UPDATE MEMBER SET (MEMBER_ID=1, NAME="Inyong", FOOD_ID=1) WHERE MEMBER_ID=1;
UPDATE MEMBER SET (MEMBER_ID=1, NAME="Robbert", FOOD_ID=1) WHERE MEMBER_ID=1;
- 조회
@Transactional
@SpringBootTest
public class ManyToOneTest {
@Autowired FoodRepository foodRepository;
@Autowired MemberRepository memberRepository;
@Test
@DisplayName("1대N 단방향 조회 테스트")
void OneWayViewTest() {
// Food -> Member
System.out.println("Food Entity 조회")
Food food = foodRepository.findById(1L).orElseThrow(NullPointerException::new);
System.out.println("food.getName() = " + food.getName());
System.out.println("Member Entity 조회")
List<Member> userList = food.getMemberList();
for (Member member : userList) {
System.out.println("member.getName() = " + member.getName());
}
}
}
// 출력 결과
// Food Entity 조회
// food.getName() = 후라이드 치킨
// Member Entity 조회
// member.getName() = Inyong
// member.getName() = Robert
- 위 코드 실행 시, 적용되는 SQL은 다음과 같습니다.
/* Food Table 조회 */
SELECT f.ID, f.NAME, f.PRICE FROM FOOD f
WHERE f.ID = 1
/* Member Table 조회 */
SELECT m.FOOD_ID, m.ID, m.NAME FROM MEMBER m
WHERE m.FOOD_ID = 1
'JPA' 카테고리의 다른 글
[JPA] 다대다 연관관계 (단방향) (0) | 2023.04.29 |
---|---|
[JPA] 다대다 연관관계 (양방향) (0) | 2023.04.29 |
[JPA] 다대일 연관관계 (양방향) (0) | 2023.04.29 |
[JPA] 일대일 연관관계 (양방향) (0) | 2023.04.29 |
[JPA] 다대일 연관관계 (단방향) (0) | 2023.04.29 |
댓글