파게로그

연관관계 매핑 본문

콤퓨타 왕기초/JPA

연관관계 매핑

파게 2021. 5. 28. 01:34

객체가 연관관계 없이, 테이블에 맞게 모델링되어 있을 때(참조 대신 FK를 그대로 사용)

 

@Entity public class Member { ​​​​@Id @GeneratedValue ​​​​private long id; ​​​​@Column(name = "USERNAME") ​​​​private String name; ​​​​private int age; ​​​​@Column(name = "TEAM_ID") ​​​​private long teamId; ​​​​@Enumerated(EnumType.STRING) ​​​​private Tier tier; ​​​​// getter and setters... } ////////// @Entity public class Team { ​​​​@Id @GeneratedValue ​​​​private long id; ​​​​private String name; ​​​​// getter and setters... }

 

이렇게 했을 때...

 

public class Main { ​​​​public static void main(String[] args) { ​​​​​​​​EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); ​​​​​​​​EntityManager em = emf.createEntityManager(); ​​​​​​​​EntityTransaction tx = em.getTransaction(); ​​​​​​​​tx.begin(); ​​​​​​​​try { ​​​​​​​​​​​​Team team = new Team(); ​​​​​​​​​​​​team.setName("teamA"); ​​​​​​​​​​​​em.persist(team); ​​​​​​​​​​​​Member member = new Member(); ​​​​​​​​​​​​member.setName("member1"); ​​​​​​​​​​​​member.setTeamId(team.getId()); ​​​​​​​​​​​​em.persist(member); ​​​​​​​​​​​​Member findMember = em.find(Member.class, member.getId()); ​​​​​​​​​​​​long teamId = findMember.getTeamId(); ​​​​​​​​​​​​Team findTeam = em.find(Team.class, teamId); ​​​​​​​​​​​​tx.commit(); ​​​​​​​​} catch (Exception e) { ​​​​​​​​​​​​tx.rollback(); ​​​​​​​​} finally { ​​​​​​​​​​​​em.close(); ​​​​​​​​} ​​​​​​​​emf.close(); ​​​​} }

 

조회를 하려면? 연관관계가 없기 때문에 조회를 두 번, 따로 해야 한다.

- 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾는다.

- 객체는 참조를 사용해서 연관된 객체를 찾는다.

-> 객체를 테이블에 맞추어 데이터 중심으로 모델링하면, 협력 관계를 만들 수 없다.

 

// 팀 조회 Team findTeam = em.find(Team.class, team.getId()); // 멤버 조회 Member findMember = em.find(Member.class, member.getId()); // 멤버의 팀 조회 Member m = em.find(Member.class, member.getId()); Long teamId = m.getTeamId(); Team t = em.find(Team.class, teamId);

 

 

======== 연관관계 매핑 이론 ============ JPA계의 포인터..? ㅋㅋㅋ

 

== 단방향 매핑 ==

Member는 Team을 참조하는데

Team에서는 Member로 갈 수 없다.

 

package com.ddoongi.web.entity; import javax.persistence.*; @Entity public class Member { ​​​​@Id @GeneratedValue ​​​​private long id; ​​​​@Column(name = "USERNAME") ​​​​private String name; ​​​​private int age; // @Column(name = "TEAM_ID") // private long teamId; ​​​​@ManyToOne // 여러 개의 멤버 : 하나의 팀 ​​​​@JoinColumn(name = "TEAM_ID") // 객체의 Team team 필드가 → DB의 TEAM_ID(FK)와 '연관관계 매핑'됨 ​​​​private Team team; ​​​​@Enumerated(EnumType.STRING) ​​​​private Tier tier; ​​​​// getters and setters... }

public class Main { ​​​​public static void main(String[] args) { ​​​​​​​​EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); ​​​​​​​​EntityManager em = emf.createEntityManager(); ​​​​​​​​EntityTransaction tx = em.getTransaction(); ​​​​​​​​tx.begin(); ​​​​​​​​try { ​​​​​​​​​​​​Team team = new Team(); ​​​​​​​​​​​​team.setName("teamA"); ​​​​​​​​​​​​em.persist(team); ​​​​​​​​​​​​Member member = new Member(); ​​​​​​​​​​​​member.setName("member1"); ​​​​​​​​​​​​member.setTeam(team); ​​​​​​​​​​​​em.persist(member); ​​​​​​​​​​​​em.flush(); // DB에 쿼리를 다 보낸다. ​​​​​​​​​​​​em.clear(); // 캐시를 비운다. ​​​​​​​​​​​​// member에서 team 검색 ​​​​​​​​​​​​Member findMember = em.find(Member.class, member.getId()); ​​​​​​​​​​​​Team findTeam = findMember.getTeam(); ​​​​​​​​​​​​// team에서 member 검색 ​​​​​​​​​​​​List<Member> members = findTeam.getMembers(); ​​​​​​​​​​​​for (Member item : members) { ​​​​​​​​​​​​​​​​System.out.println("member = " + item); ​​​​​​​​​​​​} ​​​​​​​​​​​​tx.commit(); ​​​​​​​​} catch (Exception e) { ​​​​​​​​​​​​tx.rollback(); ​​​​​​​​} finally { ​​​​​​​​​​​​em.close(); ​​​​​​​​} ​​​​​​​​emf.close(); ​​​​} }

 

join이 싫고 멤버만 가지고 오고 싶다?

@ManyToOne(fetch = FetchType.LAZY)

 

이렇게 지연로딩을 하면 Team에 대한 조회는 findTeam.getName(); 과 같이,

실제로 사용될 때 시행됨. 대부분 LAZY로 하고, 꼭 필요한 곳에서만 EAGER 쓰는 것을 권장.

속단해서 최적화하지 말자.

 

 

 

======= 양방향 매핑=======

DB는 원래 양방향이라 변화가 없다.

 

 

Member entity는 동일

 

Team entity에 컬렉션 추가

@Entity public class Team { ​​​​@Id @GeneratedValue ​​​​private long id; ​​​​private String name; ​​​​@OneToMany(mappedBy = "team") // owner가 아님을 나타냄 (컬럼명이 아니라 자바 코드 상의 필드 이름) ​​​​private List<Member> members = new ArrayList<>(); ​​​​// getters and setters... }

 

mappedBy는 왜 필요한가?

왜 JoinColumn은 필요가 없는가?

 

ㅇ객체와 테이블이 관계를 맺는 차이

- 객체 연관관계

-- 회원 -> 팀 연관관계 1개 (단방향)

-- 팀 -> 회원 연관관계 1개 (단방향)

- 테이블 연관관계

-- 회원 <-> 팀 연관관계 1개 (양방향)

 

객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 2개이다.

객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 한다. (양방향 참조로 보이는 것 뿐)

 

테이블은 외래 키 하나로 두 테이블의 연관관계를 관리

MEMBER.TEAM_ID 외래 키 하나로 양방향 연관관계 가짐(양쪽으로 조인할 수 있다)

 

 

ㅇ 연관관계의 주인(owner)

#양방향 매핑 규칙

- 객체의 두 관계중 하나를 연관관계의 주인으로 지정

- 연관관계의 주인만이 외래 키를 관리(등록, 수정)

- 주인이 아닌 쪽은 읽기만 가능

- 주인은 mappedBy 속성 사용 x

- 주인이 아니면 mappedBy 속성으로 주인 지정

#그럼 누구를 주인으로 지정하지?

- 외래 키가 있는 곳을 주인으로 정해라

- 여기서는 Member.team이 연관관계의 주인

#양방향 매핑 시 많이 하는 실수: 연관관계의 주인에 값을 입력하지 않음

Team team = new Team(); team.setName("TeamA"); em.persist(team); Member member = new Member(); member.setName("member1"); // 역방향(주인이 아닌 방향)만 연관관계 설정 // 이렇게 하면 DB에서는... // ID: 1, USERNAME: member1, TEAM_ID: null // team.getMembers().add(member); // 이렇게 해야한다. // (둘다 하면 역방향은 무시. 그러나 실제로는 둘다 쓰는걸 권장. DB에는 하나만 써도 되지만 객체 관점에서.) member.setTeam(team); em.persist(member);

 

#양방향 매핑의 장점

- 단방향 매핑만으로도 이미 연관관계 매핑은 완료

- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색) 기능이 추가된 것 뿐

- JPQL에서 역방향으로 탐색할 일이 많음

- 단방향 매핑을 잘 하고 양방향은 필요할 때 추가해도 됨(테이블에 영향을 주지 않음)

 

 

ㅇ 연관관계 매핑 어노테이션

- 다대일(@ManyToOne)

- 일대다(@OneToMany)

- 일대일(@OneToOne)

- 다대다(@ManyToMany) -> 현업에서는 제약조건이 너무많아서 1:다, 다:1로 푼다

- @JoinColumn, @JoinTable

 

ㅇ 상속관계 매핑 어노테이션

- @Inheritance

- @DiscriminatorColumn

- @DiscriminatorValue

- @MappedSuperclass(매핑 속성만 상속)

 

ㅇ 복합 키 어노테이션

- @IdClass

- @EmbeddedId

- @Embeddable

- @Mapsld

'콤퓨타 왕기초 > JPA' 카테고리의 다른 글

JPA와 객체지향 쿼리  (0) 2021.06.02
JPA 내부 구조  (0) 2021.06.02
실습 3 (Mapping annotations)  (0) 2021.05.27
[실습] Mapping to DDL  (0) 2021.05.27
[실습] JPA in Maven project  (0) 2021.05.27
Comments