코드 그라데이션
일대다 [1 : N] 본문
여기서는 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은 읽기 전용이 되어서 연관관계 주인 자격을 부여하지 않은 셈이 되는 것.
일대다 양방향 정리
'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 |