inflearn logo
강의

Course

Instructor

Java ORM Standard JPA Programming - Basics

Bidirectional Association and the Owner of Association 2 - Caveats, Summary

양방향 관계 질문이 있습니다.

649

kangsy763

49 asked

3

안녕하세요 영한님, 프로젝트를 하다가 막히는 부분이 생겨서 해당 부분 복습하다 이 부분에도 궁금한점이 생겨 질문드립니다!

1. 

Team team = new Team();
team.setName("java");

Member member = new Member();
member.setUsername("kang");

member.setTeam(team);

em.persist(team);
em.persist(member);

다음과 같은 상황에서는 insert 문이 2번 나갑니다.

Team team = new Team();
team.setName("java");

Member member = new Member();
member.setUsername("kang");

member.setTeam(team);

em.persist(member);
em.persist(team);

위의 상황에서는 insert문 2번 후에 update 문이 나갑니다. 
이 이유는 member.setTeam(team); 을 했지만 jpa는 엔티티 저장시에 연관된 엔티티들이 모두 영속 상태여야 하니깐(team의 id가 없으니깐?) team_id를 null 로 두어서 em.persist(team) 후에 update 문이 호출된 것으로 보이는데 맞나요 ?? 

사실 이 질문을 드리는 이유는 제가 현재 진행하고 있는 프로젝트에서 어떻게 처리해야할지 의문이 드는 부분이 생겨서입니다.

2. 티켓판매 어플리케이션이고, Order 테이블이 있고 Ticket 테이블이 있습니다. 일대다 매핑을 해둔 상태입니다. 주문을 받으면 해당 티켓을 주문에 등록하는 느낌입니다. 
 그래서 Order 를 생성 시에 Ticket을 생성한 후에 Order와 매핑을 해주려고 했습니다. 그런데,  'Many'(Ticket) 쪽을 먼저 save 한 후에(영속성컨텍스트에 올린 후) 'One' (Order)에 집어넣고 order를 save 하면 1번 질문과 같이 update 문이 나갈 것이라는 것을 알게되었습니다. 이 로직은 OrderService -> OrderRepository 에서 일어나는 로직입니다. 설계가 잘못된건가요? Ticket 을 create 하는 부분에서 order를 생성을 먼저하는 것이 맞다고는 생각이 드는데, 실제 주문단계를 생각해보면 order 안에 ticket이 있는 것이라고 생각이 들어서 인지부조화가 오는 기분입니다... 어떻게 해야할까요?

+)  생각해보니깐 jpa 활용 1편 주문 관련 부분이랑 굉장히 유사한 것 같은데 해당부분을 다시 복습해보겠습니다...

java JPA

Answer 3

1

kangsy763

친절한 답변 너무나도 감사드립니다 !!!! 잘 해결해보겠습니다 감사합니다! ^^

1

yh

안녕하세요. kangsy763님

먼저 1번 질문입니다.

결론부터 말씀드리면 연관관계(setXxx)를 설정하기 전에 먼저 연관관계 대상들을 영속 상태로 만드세요.

여기서는 연관관계의 대상이 되는 team을 먼저 영속 상태로 만들고, 그 다음에 member.setTeam(team)을 설정하시는 것이 좋습니다.

단순하게 접근해서 먼저 필요한 연관관계 엔티티들을 먼저 영속상태로 만든 다음에 영속상태가 되면, 연관관계를 맺어주면 됩니다.

다음 코드를 보시면 알겠지만 연관관계를 먼저 맺은 다음에, 영속상태로 바꾸면, 내부에서는 객체가 변경된 것으로 인식할 수 있습니다.

Team team = new Team();
team.setName("java");

Member member = new Member();
member.setUsername("kang");

member.setTeam(team);

em.persist(member); //member를 persist 하는 시점에 member[id=1], team[id=null, name="java"]
em.persist(team); //team을 persist 하는 시점에 member[id=1], team[id=2, name="java"]
//결과적으로 member가 사용해야 하는 team의 id가 null -> 2로 변경됨, 따라서 update 추가 발생
em.flush();

2번 질문입니다.

고민하시는 부분이 뭔지 이해가 되네요. 코드를 볼 때 order가 ticket를 가지는 그림으로 가고 싶으신거지요? 주문을 할 때 그 안에 티켓을 담는 그림이 더 잘 이해가 되니까요.

그런데 연관관계의 주인은 비즈니스 로직의 흐름이 아니라 외래키(FK)가 있는 곳을 기준으로 가져가야 합니다.

지금은 ticket.setOrder() 이 부분이 연관관계의 주인이 되는 것이지요.

자~ 여기에서 인지 부조화를 해결할 수 있는 좋은 대안이 있습니다.

바로 연관관계 편의 메서드를 잘 활용하는 방법입니다.

그러면 order.addTicket(ticket) 라는 로직을 활용해서 인지부조화도 해결하고, 고민하시는 문제도 해결할 수 있습니다.

예제 코드를 보여드릴께요.

@Entity
@Getter @Setter
public class Ticket {

@Id
@GeneratedValue
private Long id;
private String name;

@ManyToOne
@JoinColumn(name = "order_id")
private Order order;

}

@Entity
@Getter @Setter
@Table(name = "orders")
public class Order {

@Id
@GeneratedValue
private Long id;
private String name;

@OneToMany(mappedBy = "order")
private List<Ticket> tickets = new ArrayList<>();

public void addTicket(Ticket ticket) {
this.tickets.add(ticket);
ticket.setOrder(this);
}

}

이렇게 되어 있을 때, 아마 다음 방법으로 아마 하셨을거에요.

방법1 - 연관관계의 주인 바로 사용

@Test
void orderTicket() {
//Order 생성로직
Order order = new Order();
em.persist(order);

Ticket ticket = new Ticket();
ticket.setOrder(order); //연관관계 주인

em.persist(ticket);

}

방법2 - order 중심의 연관관계 편의 메서드 사용

@Test
void orderTicket2() {
//Order 생성로직
Order order = new Order();
em.persist(order);

Ticket ticket = new Ticket();
order.addTicket(ticket); //연관관계 편의 메서드

em.persist(ticket);
em.flush();
}

이렇게 방법2 처럼 사용하시면 쿼리 문제도 해결하고, 인지 부조화도 해결할 수 있습니다.

추가로 이 두 예제에서는 update 쿼리가 나가지 않습니다. 그런데, 애플리케이션 로직이 정말 복잡해지면, 깔끔한 애플리케이션 로직을 얻기 위해서 update 쿼리 한 두게 정도는 허용하는게 더 나을 수도 있습니다.

도움이 되셨길 바래요^^

0

yh

kangsy763님 화이팅^^!

실무 조언 관련 질문입니다.

0

37

1

H2데이터베이스 파일 생성

0

45

2

서브쿼리 강의에서 ALL 예시 관련 질문드립니다.

0

47

2

수정또는 삭제시 영속성 엔티티에 값이 무조건 있어야 하나요?

0

45

1

JPQL 메소드와 락

0

47

1

Delivery @OneToOne

0

54

1

17강 4~5분대 테이블 값 조회가 안됩니다.

0

85

2

UnsupportedOperationException 발생

0

80

3

H2 Database 연결이 안됩니다.

0

86

2

연관관계 매핑 질문드립니다.

0

77

2

h2데이터베이스 실행오류

0

103

2

persistence.xml

0

101

2

양방향 연관관계에서 연관관계의 주인(mappedBy)을 왜 꼭 정해야 하나요?

0

74

1

영속성 컨텍스트

0

59

1

JPA 프록시

0

86

1

Native Query와 MyBatis

0

61

1

영속성 컨텍스트는 어떤 메모리에 저장되는건가요?

0

81

1

임베디드 타입 예시 코드 관련 질문

0

110

3

명시적 조인에서 별칭을 주면 왜 객체에 접근할 수 있나요

0

89

3

인텔리제이 패키지 커서 단축키 질문

0

100

2

혹시 현재는 ID 데이터 타입이 String이면 안되나요?

0

133

1

양방향 연관관계 시 연관관계 주인을 설정하는 이유

0

67

1

임베디드 타입과 MappedSuperClass의 차이점이 궁금합니다.

0

95

1

데이터베이스가 초기화되는 것 같아요

1

172

2