일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- Mockito #Reflection #Sigleton #Test #JUnit
- Spring Framework
- LiveTemplate
- tomcat
- 디자인패턴 #싱글톤
- 톰캣
- 외장톰캣
- autocomplete
- spring
- Today
- Total
자라선
[자바 ORM 표쥰 JPA 프로그래밍] 11일차 - 엔티티 및 테이블 연관관계 본문
1. 단방향 매핑
테이블은 JOIN 를 사용하여 어떤 테이블이 주 테이블이던 간에 서로간의 테이블의 컬럼 데이터를 조회할 수있다.
하지만 객체는 객체 끼리의 참조가 되어버리기 때문에 테이블과 같이 양방향에서 조회할 수는 없다.
@Data
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
@GeneratedValue
private long id;
@Column(name = "USER_NAME")
private String name;
@ManyToOne // N:1 관계를 나타내며 1개의 TEAM 에는 여러 MEMBER가 있을수 있다.
@JoinColumn(name = "TEAM_ID") // Team 엔티티의 PK
private Team team;
}
@Data
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
@GeneratedValue
private long id;
@Column(name = "TEAM_NAME")
private String name;
}
예시로는 Member.getTeam().getName() 과 같이 Member 에서는 Team 엔티티를 참조하여 가져올 수는 있으나
Team 객체에서는 Member를 호출하지 못하게 된다.
설령 Team 객체에 Member 라는 필드를 생성하더라도 이것은 서로서로 단방향 매핑을 한 것이지
테이블 처럼 양방향 매핑이라고 할 수는 없다.
public static void login(EntityManager em){
Team team = new Team();
team.setName("새로운 팀");
em.persist(team);
Member member = new Member();
member.setName("팀원1");
member.setTeam(team);
em.persist(member);
Member member1 = new Member();
member1.setName("팀원2");
member1.setTeam(team);
em.persist(member1);
// 단방향 매핑을 하여 Member -> Team 를 매핑
System.out.println("팀 이름: " + member.getTeam().getName());
}
2. 양방향 매핑 (객체 그래프 탐색 기능 추가)
객체끼리의 양방향 매핑이 불가능하기 때문에 JPA는 이를 지원해주고자 주인이라는 개념을 만들었다.
즉 엔티티 끼리의 외래키를 관리하는 객체를 주인이라하고 주인 엔티티를 기준으로 서로간의 매핑을 하게 되는것이다.
주인은 무조건 외래키를 관리하는 엔티티로 지정하게 된다.
DB 관점으로 보자면 MEMBER N:1 TEAM 의 관계가 되고 아래와 같이 ERD가 만들어진다.
서로의 매핑를 관리해주는 FK 즉 외래키는 MEMBER가 TEAM_ID 라는 컬럼으로 관리하고 있기 때문에
이 테이블의 주인은 MEMBER가 된다.
@Data
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
@GeneratedValue
private long id;
@Column(name = "USER_NAME")
private String name;
@ManyToOne // 주인 엔티티는 N:1 로 N이 되므로 @ManyToOne 를 사용
@JoinColumn(name = "TEAM_ID") // 매핑할 컬럼을 지정
private Team team;
}
주인 엔티티에서는 @ManyToOne 어노테이션을 사용하여 N 관계인걸 명시해주고
@JoinColumn 어노테이션으로 매핑할 컬럼을 지정해준다.
name은 필수값이 아니긴하다 default 값은 쓰기엔 적합하지않아서 일반적으로는 명시해는편
@Data
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
@GeneratedValue
private long id;
@Column(name = "TEAM_NAME")
private String name;
@OneToMany(mappedBy = "team") // 양방향 매핑을 위해 필드를 추가, mappedBy 로 매핑할 필드명을 지정
private List<Member> members = new ArrayList<Member>();
}
주인과 매핑할 대싱인 Team 엔티티에서는 @OneToMany 어노테이션으로 1 관계라는것을 명시한다.
이때 필수로 mappedBy 속성을 지정해주어야하는데.
주인이 아닌 이 엔티티와 관계를 지을 필드를 작성해야한다.
그리고 List 컬렉션을 사용해 인스턴스 객체까지 생성하고 제네릭으로 주인 엔티티를 지정한다.
public static void view(EntityManager em){
Team findByTeam = em.find(Team.class, (long) 1);
System.out.println(findByTeam.getName());
// Team 엔티티의 1 ID에 속한 모든 Member 객체를 호출
for (Member m : findByTeam.getMembers()) {
System.out.println("MEMBER = " + m.getName());
}
}
그리고 조회하게 된다면 Team만 SELECT 했을뿐인데 관련된 모든 Member 객체를 호출하여 가져왔다.
3. 더 쉽게 정리
아직도 이해가 잘안되는데... 머릿속에서 최대한 간단하게 생각해보니깐
객체 관점으로 살펴봤을때 Member 객체를 사용하여 Team 객체를 가져오고 싶으면
member.getTeam() 으로 호출하면 된다. 즉 단방향 관점으로만 본다면 간편하지만....
Team 객체를 사용하여 포함된 모든 Member 를 가져오려면 결국 1:N 관계에서의
N의 여러 ROW가 발생되기 때문에 무조건 List 와 같은 컬렉션으로 받아야한다.
그러면 Team 객체는 아래와 같은 필드가 필요하게 될 것이고.
private List<Member> members = new ArrayList<Member>();
이렇게 N개의 ROW를 반환한다면 @OneToMany 가 된다. (Many 가 To 뒤에 있으니 N개의 Row와 관계라는 뜻)
그리고 테이블특성상 N 관계인 테이블 Member 가 외래키를 갖고있어
JPA도 @OneToMany 에서만 mappedBy를 지원한다.
@OneToMany(mappedBy = "team")
private List<Member> members = new ArrayList<Member>();
그러면 주인이 아닌 엔티티에서는 위와 같은 필드가 나오게 되고 양방향 매핑이 끝이 난다.
4. 주의사항
보기만하면 뭐 별거없네 하는데 이게 막상사용하면 헷갈린다...
- 1
public static void edit(EntityManager em){
Team findByTeam = em.find(Team.class, (long) 1);
findByTeam.getMembers().clear(); // Team 1 ID의 모든 MEMBER의 매핑을 제거??
}
find 를 사용하여 DB상의 ID가 1번인 Team 테이블을 SELECT 하였다.
그리고 TEAM 테이블과 매핑되어있는 MEMBER 객체를 전부 제거하였다...
이것은 절대 DB 상에 반영되지않는다.
왜냐하면 주인 엔티티가 아니기 때문이다.
주인이 아닌 엔티티에서 @OneToMany로 지정된 필드는 Only 조회용이다.
값을 수정, 삭제, 추가하더라도 그것은 JAVA 상에서만 적용되지 절대 영속성 컨텍스트는 물론 DB에도 반영이 안된다.
- 2
public static void login(EntityManager em){
Team team = new Team();
team.setName("새로운 팀");
em.persist(team);
Member member = new Member();
member.setName("팀원1");
member.setTeam(team);
em.persist(member);
Member member1 = new Member();
member1.setName("팀원2");
member1.setTeam(team);
em.persist(member1);
// ID가 1인 Team 객체를 조회하여 호출
Team findByTeam = em.find(Team.class, (long) 1);
// Team 엔티티의 1 ID에 속한 모든 Member 객체를 호출
for (Member m : findByTeam.getMembers()) {
System.out.println("MEMBER = " + m.getName());
}
}
하나의 트랜잭션 상에서 persist 를 사용해 엔티티들을 영속성 컨텍스트로 등록하였다.
물론 Member와 Team 엔티티를 매핑 하고 난뒤에다.
여기까지는 문제가 없으나 바로 find 를 사용해 영속성컨텍스트 상의 ID 1번의 Team 객체를 호출 후
매핑된 Member의 객체를 가져오려했다.
당연히 List의 size는 0이 되고 매핑된 정보는 없었다.
이유는 간단하다.. 매핑 할때 Member -> Team 으로만 매핑하였고 Team 엔티티는 Member와 매핑한 적이 없기 떄문.
DB상에 올라가 있다면 JPA가 서로 JOIN 후 불러오기 때문에 Team 객체에 Member 리스트가 있겠지만
동일한 트랜잭션이기 때문에 아직 DB로 INSERT도 안된 것이다...
'Develop > JPA' 카테고리의 다른 글
[자바 ORM 표쥰 JPA 프로그래밍] 13일차 - 엔티티 상속 (0) | 2021.08.26 |
---|---|
[자바 ORM 표쥰 JPA 프로그래밍] 12일차 - 엔티티 매핑 (0) | 2021.08.26 |
[자바 ORM 표쥰 JPA 프로그래밍] 10일차 - 객체와 DB의 불일치성 (0) | 2021.08.19 |
[자바 ORM 표쥰 JPA 프로그래밍] 9일차 - 필드와 컬럼 매핑: 래퍼런스 (0) | 2021.08.18 |
[자바 ORM 표쥰 JPA 프로그래밍] 8일차 - 테이블 초기화 및 기본키 전략 (0) | 2021.08.14 |