코드 그라데이션

일대다 [1 : N] 본문

Spring/JPA 공부

일대다 [1 : N]

완벽한 장면 2023. 8. 20. 12:52

여기서는 1이 연관관계 주인

1 방향에서 연관관계를 관리하겠다.

영한쌤은 이 모델을 추천하지 않음.

 

이제는 팀을 기준으로 매핑을 처리하고 싶으

객체 연관관계에서는 Team을 기준으로 멤버를 알고 싶은데, 멤버를 기준으로는 별로 팀을 알고 싶지 않은 그런 상태

 

 

일대다 단방향

 

=> 즉, Team에 있는 members를 바꾸면, MEMBER 테이블의 TEAM_ID도 변경이 되어야 한다.

 

 

 

코드로 살펴보면

Member

@Entity
@NoArgsConstructor
public class Member {

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  @Column(name = "MEMBER_ID")
  private Long id;

  @Column(name = "USERNAME")
  private String username;

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getUsername() {
    return username;
  }

  public void setUsername(String username) {
    this.username = username;
  }

}

 

TEAM

@Entity
public class Team {

  @Id
  @GeneratedValue
  @Column(name = "TEAM_ID")
  private Long id;

  private String name;

  // 여기가 이제 연관관계를 관맇라기 위해 추가
  @OneToMany
  @JoinColumn(name = "TEAM_ID")
  private List<Member> members = new ArrayList<>();

  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public List<Member> getMembers() {
    return members;
  }

  public void setMembers(List<Member> members) {
    this.members = members;
  }
}

 

JPAMain

public class JpaMain {
  public static void main(String[] args) {
    EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
    EntityManager em = emf.createEntityManager();

    EntityTransaction tx = em.getTransaction();
    tx.begin();

    try {
      Member member = new Member();
      member.setUsername("member1");

      em.persist(member);
      //여기까지 하면 멤버가 저장

      Team team = new Team();
      team.setName("TeamA");
      // 여기까지 팀 테이블에 인서트

      // 얘가 좀 애매함.
      // 팀 테이블에 인서트 될 수 있는 상황이 아님.
      // 이 외래키는 MEMBER 테이블에 있음.
      // 그럼 MEMBER 테이블에 있는 TEAM_ID(FK)를 업데이트 쳐줘야 함.)
      team.getMembers().add(member);

      em.persist(team);

      tx.commit();
    } catch (Exception e) {
      tx.rollback();
    } finally {
      em.close();
    }
    emf.close();
  }
}

 

 

실행 결과

10:56:55.919 [main] DEBUG org.hibernate.SQL - 
    call next value for hibernate_sequence
Hibernate: 
    call next value for hibernate_sequence
10:56:55.920 [main] DEBUG org.hibernate.id.enhanced.SequenceStructure - Sequence value obtained: 2
10:56:55.920 [main] DEBUG org.hibernate.resource.jdbc.internal.ResourceRegistryStandardImpl - HHH000387: ResultSet's statement was not registered
10:56:55.920 [main] DEBUG org.hibernate.event.internal.AbstractSaveEventListener - Generated identifier: 2, using strategy: org.hibernate.id.enhanced.SequenceStyleGenerator
10:56:55.927 [main] DEBUG org.hibernate.engine.transaction.internal.TransactionImpl - committing
10:56:55.928 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Processing flush-time cascades
10:56:55.929 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Dirty checking collections
10:56:55.932 [main] DEBUG org.hibernate.engine.internal.Collections - Collection found: [inflearn.exjpa.jpaExample.Team.members#2], was: [<unreferenced>] (initialized)
10:56:55.934 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Flushed: 2 insertions, 0 updates, 0 deletions to 2 objects
10:56:55.934 [main] DEBUG org.hibernate.event.internal.AbstractFlushingEventListener - Flushed: 1 (re)creations, 0 updates, 0 removals to 1 collections
10:56:55.935 [main] DEBUG org.hibernate.internal.util.EntityPrinter - Listing entities:
10:56:55.935 [main] DEBUG org.hibernate.internal.util.EntityPrinter - inflearn.exjpa.jpaExample.Team{members=[inflearn.exjpa.jpaExample.Member#1], name=TeamA, id=2}
10:56:55.935 [main] DEBUG org.hibernate.internal.util.EntityPrinter - inflearn.exjpa.jpaExample.Member{id=1, username=member1}
10:56:55.939 [main] DEBUG org.hibernate.SQL - 
    /* insert inflearn.exjpa.jpaExample.Member
        */ insert 
        into
            Member
            (USERNAME, MEMBER_ID) 
        values
            (?, ?)
Hibernate: 
    /* insert inflearn.exjpa.jpaExample.Member
        */ insert 
        into
            Member
            (USERNAME, MEMBER_ID) 
        values
            (?, ?)
10:56:55.943 [main] DEBUG org.hibernate.SQL - 
    /* insert inflearn.exjpa.jpaExample.Team
        */ insert 
        into
            Team
            (name, TEAM_ID) 
        values
            (?, ?)
Hibernate: 
    /* insert inflearn.exjpa.jpaExample.Team
        */ insert 
        into
            Team
            (name, TEAM_ID) 
        values
            (?, ?)

 

인서트 쿼리는 두 번 나가고

 

그 아래

10:56:55.947 [main] DEBUG org.hibernate.SQL - 
    /* create one-to-many row inflearn.exjpa.jpaExample.Team.members */ update
        Member 
    set
        TEAM_ID=? 
    where
        MEMBER_ID=?
Hibernate: 
    /* create one-to-many row inflearn.exjpa.jpaExample.Team.members */ update
        Member 
    set
        TEAM_ID=? 
    where
        MEMBER_ID=?

업데이트 쿼리가 나간다.

 

DB에 값도 정상적으로 들어왔다.

 

그런데 왜 업데이트 쿼리가 추가로 나가야 하는가?

      // 그럼 MEMBER 테이블에 있는 TEAM_ID(FK)를 업데이트 쳐줘야 함.
      team.getMembers().add(member);

이거 때문이다.

 

왜 실무에서 잘 안 쓸까?

나는 Team 테이블을 손을 댔는데, 왜 Member 테이블에서 업데이트가 되지? 헷갈릴 수 있다.

실무에서는 테이블이 수십개가 한꺼번에 돌아가는 상황에서 이렇게 되면 운영이 굉장히 어려워진다.

 

 

그래서 이것보다는 다대일 단방향 관게 => 필요하면 양방향을 추가하는 전략으로 많이들 간다.

거의 비슷하게 쓸 수 있음.

 

 

일대다 단방향 정리 (1)

• 일대다 단방향은 일대다(1:N)에서 일(1)이 연관관계의 주인
• 테이블 일대다 관계는 항상 다(N) 쪽에 외래 키가 있음
• 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하는 특이한 구조
• @JoinColumn을 꼭 사용해야 함. 그렇지 않으면 조인 테이블 방식을 사용함(중간에 테이블을 하나 추가함)

예시

Hibernate: 
    
    create table Team_Member (
       Team_TEAM_ID bigint not null,
        members_MEMBER_ID bigint not null
    )
11:48:10.831 [main] DEBUG org.hibernate.SQL - 

이렇게 못보던 테이블을 하나 생성한다.

이렇게

- 성능상 문제 등

JoinTable 넣어서 여러 설정을 해줄 수도 있음

 

 

 

일대다 단방향 정리(2)

 


일대다 양방향

현재 "얘" 가 연관관계 주인.

 

어? 근데 양방향인데 나도 Member에서 Team을 조회하는 걸 하고싶어.

스펙상 되는 건 아니지만 

어떻게?

 

Member에

  //양방향 추가(역방향)
  @ManyToOne
  @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
  private Team team;

이걸 추가

 

이 값을 넣었다는 것은 지금

insertable = false, updatable = false

읽기 전용화.

 

매핑은 되어 있고, 다 사용하지만 Members에 있는 team은 읽기 전용이 되어서 연관관계 주인 자격을 부여하지 않은 셈이 되는 것.

 

일대다 양방향 정리

 

728x90

'Spring > JPA 공부' 카테고리의 다른 글

다대다 [N : N]  (0) 2023.08.21
일대일 [1 : 1]  (0) 2023.08.21
다대일 [N : 1]  (0) 2023.08.20
다양한 연관관계 매핑 도입  (0) 2023.08.20
실전 예제 - 2. 연관관계 매핑 시작  (0) 2023.08.19
Comments