Orm 표준 jpa 프로그래밍 (3) - 연관관계 매핑
- -
ex1. 객체를 테이블에 맞추어 모델링
// (참조 대신에 외래키를 그대로 사용)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
@Column(name = "TEAM_ID")
private Long teamId;
...
}
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
...
}
// (외래 키 식별자를 직접 다룸)
//팀 저장
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());
//연관관계가 없음
Team findTeam = em.find(Team.class, team.getId());
- 객체를 테이블에 맞추어 데이터 중심으로 모델링하면 협력관계를 만들 수 없다.
> 테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾음
> 객체는 참조를 사용해서 연관된 객체를 찾음
> 테이블과 객체 사이에는 이러한 간격이 있음
1. 단방향 연관관계
@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")
private Team team;
...
// 연관관계 저장
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam();
// 새로운 팀B
Team teamB = new Team();
teamB.setName("TeamB");
em.persist(teamB);
// 회원1에 새로운 팀B 설정
member.setTeam(teamB);
2. 양방향 연관관계와 연관관계의 주인
// (Member 엔티티는 단방향과 동일)
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
@Column(name = "USERNAME")
private String name;
private int age;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
...
// (Team 엔티티는 컬렉션 추가)
@Entity
public class Team {
@Id @GeneratedValue
private Long id;
private String name;
@OneToMany(mappedBy = "team")
List<Member> members = new ArrayList<Member>();
...
}
//조회
Team findTeam = em.find(Team.class, team.getId());
int memberSize = findTeam.getMembers().size(); //역방향 조회
객체의 양방향 관계
- 서로 다른 단방향 관계가 2개인 것
- 객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어야 함
테이블의 양방향 관계
- 테이블은 외래 키 하나로 두 테이블의 연관관계를 관리
연관관계의 주인
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록,수정)
- 주인이 아닌쪽은 읽기만 가능 (외래키에 영향을 주지 않고 단순 조회만 가능)
- 주인은 mappedBy 속성 사용 X
- 주인이 아니면 mappedBy 속성으로 주인을 지정
- 주인은 외래키가 있는곳이 되어야 함
Team team = new Team();
team.setName("TeamA");
em.persist(team);
Member member = new Member();
member.setName("member1");
//역방향(주인이 아닌 방향)만 연관관계 설정
team.getMembers().add(member);
//양방향 매핑시 가장 많이 하는 실수: 연관관계의 주인에 값을 입력하지 않음
//실수 해결 >> 연관관계의 주인에 값 설정
// 양방향 매핑시 연관관계의 주인에 값을 입력해야 함
//순수한 객체 관계를 고려하면 항상 양쪽다 값을 입력해야 함
member.setTeam(team); //**
em.persist(member);
- 양방향 연관관계는 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정해야 함
- 연관관계 편의 메소드 추천
- 양방향 매핑시에는 무한루프 조심해야 함!! ( toString(), lombok, JSON 생성 라이브러리 )
- 단방향 매핑만으로도 이미 연관관계 매핑은 완료된것이다.
- 양방향 매핑은 반대 방향으로 조회(객체 그래프 탐색)기능이 추가된 것 뿐
- JPQL에선 역방향으로 탐색할 일이 많음
- 단방향 매핑을 하고 필요할 때 양방향 매핑을 추가하는 것이 좋음 (테이블에 영향 X)
3. 다대일
3-1. 다대일 단방향
- 가장 많이 사용하는 연관관계
3-2. 다대일 양방향
- 외래 키가 있는 쪽이 연관관계의 주인
- 양쪽을 서로 참조하도록 개발
4. 일대다
4-1. 일대다 단방향
- 1이 연관관계의 주인
- 테이블 일대다 관계는 항상 '다(n)'쪽에 외래 키가 있음
- 객체와 테이블의 차이 때문에 반대편 테이블의 외래 키를 관리하는 특이한 구조
- 일대다 단방향 매핑보다는 다대일 양방향 매핑 사용
4-2. 일대다 양방향
- 공식적으로 존재 X
- 다대일 양방향 사용하기
5. 일대일
- 주 테이블이나 대상 테이블 중에 외래 키 선택 가능
- 외래 키에 DB 유니크 제약 조건 추가
5-1. 주 테이블에 외래 키 단방향
- 다대일 단방향 매핑과 유사
5-2. 주 테이블에 외래 키 양방향
- 다대일 양방향 매핑 처럼 외래 키가 있는 곳이 연관관계의 주인, 반대편은 mappedBy 적용
5-3. 대상 테이블에 외래 키
- 단방향은 jpa가 지원하지 않음, 양방향은 지원
- 일대일 주 테이블에 외래 키 양방향과 매핑 방법은 같음
>> 주 테이블에 외래 키
- 객체지향 개발자가 선호하며 jpa 매핑이 편리
- 장점: 주 테이블만 조회해도 대상 테이블에 데이터가 있는지 확인 가능
- 단점: 값이 없으면 외래 키에 null 허용됨
>> 대상 테이블에 외래 키
- 장점: 주 테이블과 대상 테이블을 일대일에서 일대다 관계로 변경할 때 테이블 구조가 유지됨
- 단점: 프록시 기능의 한계로 지연 로딩 설정을 해도 즉시 로딩이 됨
6. 다대다
- RDBMS는 정규화된 테이블 2개로 다대다 관계 표현 불가능 => '연결 테이블'을 추가해서 일대다, 다대일 관계로 풀어내야 함
- 객체는 '컬렉션'을 사용해서 객체 2개로 다대다 관계 가능
- @ManyToMany 사용하며, @JoinTable로 연결 테이블을 지정
- 다대다 매핑은 단방향/양방향 둘 다 가능
- 편리해 보이지만 실무에서는 사용 X (연결 테이블이 단순히 연결만 하지 않고, 추가 데이터가 더 들어올 수 있음)
- 다대다의 한계를 극복하기 위해 연결 테이블용 엔티티를 추가 할 수 있음(연결 테이블을 엔티티로)
- @ManyToMany >> @OneToMany + @ManyToOne
@JoinColumn 의 주요 속성
1. name: 매핑할 외래 키 이름 (기본값은 필드명 + _ + 참조하는 테이블의 기본 키 컬럼명)
2. referencedColumnName: 외래 키가 참조하는 대상 테이블의 컬럼명 (기본값: 참조하는 테이블의 기본키 컬럼명)
3. foreignKey(DDL): 외래 키 제약 조건을 직접 지정, 테이블 생성 시에만 사용
@ManyToOne 의 주요 속성
1. Optional: false로 설정하면 연관된 엔티티가 항상 있어야 한다(기본값: true)
2. fetch (기본값: @ManyToOne=FetchType.EAGER / @OneToMany=FetchType.LAZY)
3. cascade: 영속성 전이 기능을 사용
@OneToMany 의 주요 속성
1. mappedBy: 연관관계의 주인 필드를 설정
( '다(n)' 쪽에 외래키가 있고 외래 키가 있는 쪽이 연관관계의 주인이므로 '일(one)'쪽에서 mappedBy로 주인 필드 지정함)
2. fetch (기본값: @ManyToOne=FetchType.EAGER / @OneToMany=FetchType.LAZY)
3. cascade: 영속성 전이 기능을 사용
'back > spring' 카테고리의 다른 글
Orm 표준 jpa 프로그래밍 (5) - 값 타입 (0) | 2023.05.08 |
---|---|
Orm 표준 jpa 프로그래밍 (4) - 매핑, 프록시 (0) | 2023.05.08 |
Orm 표준 jpa 프로그래밍 (2) - 엔티티 매핑 (0) | 2023.05.08 |
Orm 표준 jpa 프로그래밍 (1) - 영속성 관리 (주석 내용 추가 예정) (0) | 2023.05.07 |
스프링 핵심6 - 빈 생명주기 콜백 / 빈 스코프 (0) | 2023.03.22 |
소중한 공감 감사합니다