이 장의 목표는 객체의 참조와 테이블의 외래 키를 매핑하는 것이다.
- 객체 : 참조(주소)를 사용해서 관계를 맺음
- 테이블 : 외래 키를 사용해서 관계를 맺음
회원과 팀의 관계를 통해 다대일(N:1) 단방향 관계를 알아보자
- 회원과 팀이 있다.
- 회원은 하나의 팀에만 소속될 수 있다.
- 회원과 팀은 다대일 관계이다.
객체 연관관계
테이블 연관관계
객체 연관관계와 테이블 연관관계의 가장 큰 차이
객체 연관관계 vs 테이블 연관관계 정리
// 예제 5.1) JPA를 사용하지 않은 순수한 회원과 팀 클래스 코드
public class Member {
private String id;
private String username;
private Team team; // 팀의 참조를 보관
public void setTeam(Team team) {
this.team = team;
}
// Getter, Setter ...
}
public class Team {
private String id;
private String name;
// Getter, Setter ...
}
// 예제 5.2) 회원1과 회원2를 팀1에 소속시키는 코드
public static void main(String[] args) {
// 생성자(id, 이름)
Member member1 = new Member("member1", "회원1");
Member member2 = new Member("member2", "회원2");
Team team1 = new Team("team1", "팀1");
member1.setTeam(team1);
member2.setTeam(team1);
Team findTeam = member1.getTeam();
}
데이터베이스 테이블의 회원과 팀의 관계를 살펴보자
회원 테이블의 TEAM_ID에 외래 키 제약 조건 설정
회원1과 회원2를 팀1에 소속 시킴
회원1이 소속퇸 팀 조회
JPA를 사용하여 객체와 테이블을 매핑해보자
// 예제 5.4) 매핑한 회원 엔티티
@Entity
public class Member {
@Id
@Column(name = "MEMBER_ID")
private String id;
private String username;
// 연관관계 매핑
@ManyToOne
@JoinColumn(name="TEAM_ID")
private Team team;
// 연관관계 설정
public void setTeam(Team team) {
this.team = team;
}
// Getter, Setter ...
}
// 예제 5.5) 매핑한 팀 엔티티
@Entity
public class Team {
@Id
@Column(name = "TEAM_ID")
private String id;
private String name;
// Getter, Setter ...
}
이제 위 매핑 코드에 작성한 연관관계 매핑 어노테이션에 대해 알아보자!
참고) @JoinColumn 생략
- 생락하면, 외래 키를 찾을 때 기본 전략을 사용
- 기본 전략 : 필드명 + _ + 참조하는 테이블의 컬럼명
ex)
@ManyToOne
private Team team;
: 필드명(team) + _(밑줄) + 참조하는 테이블의 컬럼명(TEAM_ID)
= team_TEAM_ID 외래 키를 사용
// targetEntity 속성의 사용 예
@OneToMany
private List<Member> members; // 제네릭으로 타입정보 알 수 있음
@OneToMany(targetEntity=Member.class)
private List members; // 제네릭이 없으면 타입 정보 알 수 없음
참고) 일대일(@OneToOne)관계
- 단방향 관계 매핑할 때 다대일, 일대일 둘 중 어떤 것을
사용할지는 반대편 관계에 달려 있음.
- 반대편이 일대다 관계 -> 다대일
일대일 관계 -> 일대일
연관관계를 등록, 수정, 삭제, 조회 예제를 통해 연관관계를 어떻게 사용하는지 알아보자!
// 예제 5.6) 회원과 팀을 저장하는 코드
public void testSave() {
// 팀1 저장
Team team1 = new Team("team1", "팀1");
em.persist(team1);
// 회원1 저장
/**
JPA는 참조한 팀의 식별자(Team.id)를
외래 키로 사용하여 적절한 등록 쿼리 생성함.
**/
Member member1 = new Member("member1", "회원1");
member1.setTeam(team1); // 연관관계 설정 member1 -> team1
em.persist(member1);
// 회원2 저장
Member2 member2 = new Member("member2", "회원2");
member2.setTeam(team1); // 연관관계 설정 member2 -> team1
em.persist(member2);
}
* 주의 *
JPA에서 엔티티 저장할 때는 연관된 모든 엔티티 영속상태 여야 함.
Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // 객체 그래프 탐색
System.out.println("팀 이름 = " + team.getTeam());
// 출력결과 : 팀 이름 = 팀1
// 예제 5.7) 팀1에 소속된 모든 회원을 조회하는 JPQL 조인 검색
private static void queryLoginJoin(EntityManager em) {
// 회원이 팀과 관계를 가지고 있는 필드(m.team)를 통해 Member와 Team 조인
// t.name을 검색조건으로 사용해 팀1에 속한 회원만 검색
String jpql = "select m from Member m join m.team t where " +
"t.name=:teamName"; // :로 시작하는 것은 파라미터를 바인딩받는 문법
List<Member> resultList = em.createQuery(jpql, Member.class)
.setParameter("teamName", "팀1");
.getResultLit();
for (Member member : resultList) {
System.out.println("[query] member.username=" +
member.getUsername());
)
}
// 결과 : [query] member.username=회원1
// 결과 : [query] member.username=회원2
// 예제 5.8) 팀1 소속이던 회원을 새로운 팀2에 소속하도록 수정
private static void updateRelation(EntityManager em) {
// 새로운 팀2
Team team2 = new Team("team2", "팀2");
em.persist(team2);
// 회원1에 새로운 팀2 설정
Member member = em.find(Member.class, "member1);
member.setTeam(team2);
}
// 예제 5.9) 회원1을 팀에 소속하지 않도록 변경
private static void deleteRelation(EntityManager em) {
Member member1 = em.find(Member.dclass, "member1");
member1.setTeam(null); // 연관관계 제거
- 팀1에는 회원1과 회원2가 소속되어 있음.
- 팀1 삭제하려면 연관관계를 먼저 끊어야함.
출처 : 자바 ORM 표준 JPA 프로그래밍 책
[자바 ORM 표준 JPA 프로그래밍] 복합 키와 식별 관계 매핑