티스토리 뷰
1. 단방향 연관관계
테이블은 외래키를, 객체는 참조를 이용해 연관관계를 설정한다.
Member 클래스가 TeamId 가 아닌 Team 객체를 참조함으로써 객체지향적 설계가 가능하다.
> 연관관계 매핑
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
// Member - Team : 다대일 관계
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
// getter, setter
}
@Entity
public class Team {
@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;
private String name;
// getter, setter
}
> 객체 참조를 통한 저장 및 조회
//1.저장
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
//2.조회
//조회 - 1차 캐시에서 member와 team조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
//3.수정
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정 -
// 영속성 컨텍스트에 저장한 후 tx 커밋시점에 update 쿼리 실행
member.setTeam(teamB);
2. 양방향 연관관계
테이블 사이에서는 TEAM_ID를 통해 TEAM에 속한 MEMBER를 조회할 수 있지만
객체 사이에서는 Team을 통해 속한 Member를 찾을 수 있는 방법이 없다.
Team 클래스의 인스턴스 변수로 Member List를 갖도록 설계해서 양방향 연관관계를 설정한다.
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
// Team - Member : 일대다 관계
// ArrayList로 초기화 : nullpointerexception 방지
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
// getter, setter
}
> mappedBy : 클래스 자신이 연관관계의 반대편에 어떤 이름의 참조변수로 설정되어 있는지를 명시
외래키가 있는곳을 주인으로 정하자.
Member 클래스의 team과 Team 클래스의 members 모두 값을 변경할 수 있는데
MEMBER 테이블이 갖고있는 TEAM_ID 외래키를 update 하기 위해선 값의 명확한 주인이 필요하다.
연관관계 주인만 값(외래키)를 관리할 수 있다.
주인이 아닌 경우 mappedBy 속성으로 주인을 지정해 주어야 한다.
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
Team team = new Team();
team.setName("TeamA");
em.persist(team);
// 주인인 Member.team은 MEMBER 테이블의 TEAM_ID 외래키를 업데이트 한다.
Member member = new Member();
member.setTeam(team);
em.persist(member);
// 주인이 아닌 Team.members 는 MEMBER 테이블의 TEAM_ID 외래키를 업데이트 하지 않는다.
team.getMembers().add(member);
3. 양방향 연관관계 매핑 주의할 점
1) 연관관계 양쪽에 값을 세팅해 주어야 한다.
// team 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team); // team 1차 캐시에 저장
// member 저장
Member member = new Member();
member.setTeam(team);
em.persist(member); // member 1차 캐시에 저장
// 추가코드 필요...
// 1차 캐시에서 조회
// team.members 리스트는 1차 캐시에 비어있는 상태
Team findTeam = em.find(Team.class, team.getId());
System.out.println("size: " + findTeam.getMembers().size()); // 0
1. 주인이 아닌 Team.members 리스트에도 함께 저장해 주거나
2. 1차 캐시를 DB에 반영하고 영속성 컨텍스트를 초기화해 em.find() 호출시 DB에서 조회할 수 있도록 해주어야 한다.
// 추가코드
// 1
team.getMembers().add(member);
// 2
em.flush();
em.clear();
DB에서 team을 조회할 때 2개의 쿼리 실행
select team where team_id = ?
select member where team_id = ?
2) 1번을 간편하게 해결하기 위해 연관관계 편의 메소드를 활용하자.
기존 setter와 차별을 두도록 새로운 메소드를 만들자.
@Entity
public class Member {
// 생략 ...
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
// 연관관계 편의 메소드
public changeTeam(Team team) {
if(this.team != null) {
this.team.getMembers().remove(this);
}
this.team = team;
this.team.getMembers().add(this);
}
}
// 사용
member.changeTeam(team);
3) 양방향 매핑시 무한루프를 조심하자.
> toString(), lombok
@Entity
public class Team {
// 생략 ...
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
// members에서 member조회
// member에서 member.team조회
// team에서 members조회
// members에서 member조회
// ...
@Override
public String toString() {
return "Team{" +
"id=" + id +
", name=" + name +
", members=" + members +
'}';
}
}
> JSON 생성 라이브러리
spring의 controller의 response로 엔티티를 반환할 때
spring이 엔티티를 JSON형태로 변형해 반환하는 과정에서 무한루프 발생.
(추가) 엔티티가 변경되면 controller api 또한 변경되기 때문에
--> DTO를 만들어 활용하자.
4) 정리
> 단방향 매핑으로 1차 설계를 완료하자. (객체-테이블 매핑 완료)
> 양방향 매핑은 객체 그래프 탐색이 필요한 경우에 추가하자. (테이블에 영향을 주지 않음)
출처
https://www.inflearn.com/course/ORM-JPA-Basic 자바 ORM 표준 JPA 프로그래밍 - 기본편(김영한)
'ORM > JPA' 카테고리의 다른 글
[JPA] 6. 프록시와 연관관계 관리 (0) | 2021.12.14 |
---|---|
[JPA] 5. 고급 매핑 (0) | 2021.12.01 |
[JPA] 4. 다양한 연관관계 매핑 (0) | 2021.11.29 |
[JPA] 2. 엔티티 매핑 (0) | 2021.11.07 |
[JPA] 1. 내부동작방식 (0) | 2021.11.06 |
- Total
- Today
- Yesterday
- java8
- Spring
- Stream
- 스프링 예외 추상화
- 계층형 아키텍처
- MySQL
- 스프링 카프카 컨슈머
- 이벤트 스토밍
- HTTP 헤더
- 마이크로서비스 패턴
- named query
- Git
- 클린코드
- 트랜잭셔널 아웃박스 패턴
- 폴링 발행기 패턴
- ATDD
- JPA
- clean code
- 학습 테스트
- spring rest docs
- H2
- 도메인 모델링
- mockito
- Spring Data JPA
- http
- TDD
- Spring Boot
- Ubiquitous Language
- 육각형 아키텍처
- kafka
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 | 31 |