자바 ORM 표준 JPA 프로그래밍 - 단방향 연관관계, 연관관계 사용

05. 연관관계 매핑 기초

이 장의 목표는 객체의 참조와 테이블의 외래 키를 매핑하는 것이다.

  • 객체 : 참조(주소)를 사용해서 관계를 맺음
  • 테이블 : 외래 키를 사용해서 관계를 맺음
  • 방향(Direction) : [단방향, 양방향]
    ex) 회원 & 팀과의 관계
    단방향 관계 : 회원 -> 팀 or 팀 -> 회원
    양방향 관계 : 회원 -> 팀 and 팀 -> 회원
    -> 방향은 객체관계에만 존재, 테이블 관계는 항상 양방향!
  • 다중성(Multiplicity) : [다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:M)]
  • 연관관계의 주인(Owner) : 객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 함.

5.1 단방향 연관관계

회원과 팀의 관계를 통해 다대일(N:1) 단방향 관계를 알아보자

- 회원과 팀이 있다.
- 회원은 하나의 팀에만 소속될 수 있다.
- 회원과 팀은 다대일 관계이다.
  • 객체 연관관계

    • 회원 객체는 Member.team 필드(멤버변수)로 팀 객체와 연관관계를 맺음.
    • 단방향 관계.
      회원은 Member.team 필드를 통해 팀을 알 수 있지만, 반대로 팀은 회원을 알 수 없음.
      ex) member.getTeam()
  • 테이블 연관관계

    • 회원 테이블은 TEAM_ID 외래 키로 팀 테이블과 연관관계.
    • 양방향 관계.
      회원 테이블의 TEAM_ID 외래 키를 통해 회원과 팀을 조인할 수 있고, 반대로 팀과 회원도 조인 가능.
      ex) 회원과 팀 조인하는 SQL

      ex) 팀과 회원을 조인하는 SQL
  • 객체 연관관계와 테이블 연관관계의 가장 큰 차이

    • 참조를 통한 연관관계는 언제나 단방향.
    • 객체간에 연관관계를 양방향으로 만들고 싶으면 반대쪽에도 필드를 추가해 참조를 보관해야함
      -> 연관관계를 하나 더 만드는 것.
      -> 양방향 관계가 아닌 서로 다른 단방향 관계 2개
      ex) 단방향 연관관계

      ex) 양방향 연관관계
  • 객체 연관관계 vs 테이블 연관관계 정리

    • 객체 : 참조(주소)로 연관관계를 맺음. 참조를 사용해 데이터 조회
    • 테이블 : 외래 키로 연관관계를 맺음. 조인(JOIN)을 통해 테이터 조회

5.1.1 순수한 객체 연관관계

// 예제 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();
}

  • 객체 그래프 탐색 : 객체는 참조(member1.getTeam())를 사용하여 연관관계를 탐색할 수 있음.

5.1.2 테이블 연관관계

데이터베이스 테이블의 회원과 팀의 관계를 살펴보자

회원 테이블의 TEAM_ID에 외래 키 제약 조건 설정

회원1과 회원2를 팀1에 소속 시킴

회원1이 소속퇸 팀 조회
  • 조인 : 데이터베이스는 외래 키를 사용하여 연관관계를 탐색할 수 있음.

5.1.3 객체 관계 매핑

JPA를 사용하여 객체와 테이블을 매핑해보자

  • 객체 연관관계 : 회원 객체의 Member.team 필드 사용
  • 테이블 연관관계 : 회원 테이블의 MEMBER.TEAM_ID 외래 키 컬럼을 사용
    -> 연관관계 매핑 : Member.team과 MEMBER.TEAM_ID를 매핑하는 것
// 예제 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 ...
}

이제 위 매핑 코드에 작성한 연관관계 매핑 어노테이션에 대해 알아보자!

5.1.4 @JoinColumn

  • 외래 키를 매핑할 때 사용.
  • name 속성에는 매핑할 외래 키 이름을 지정.
    ex) 회원과 팀 테이블은 TEAM_ID 외래 키로 연관관계를 맺어 @JoinColumn(name="TEAM_ID")을 지정하면 됨.
  • 생략 가능
참고) @JoinColumn 생략
- 생락하면, 외래 키를 찾을 때 기본 전략을 사용
- 기본 전략 : 필드명 + _ + 참조하는 테이블의 컬럼명

ex)
@ManyToOne
private Team team;
: 필드명(team) + _(밑줄) + 참조하는 테이블의 컬럼명(TEAM_ID) 
  = team_TEAM_ID 외래 키를 사용

5.1.5 @ManyToOne

  • 다대일(N:1) 관계라는 매핑 정보.
  • 연관관계 매핑 시, 다중성을 나타내는 어노테이션 필수로 사용
    ex) 회원과 팀은 다대일 관계
// targetEntity 속성의 사용 예
@OneToMany
private List<Member> members; // 제네릭으로 타입정보 알 수 있음

@OneToMany(targetEntity=Member.class)
private List members; // 제네릭이 없으면 타입 정보 알 수 없음
참고) 일대일(@OneToOne)관계
- 단방향 관계 매핑할 때 다대일, 일대일 둘 중 어떤 것을 
  사용할지는 반대편 관계에 달려 있음.
- 반대편이 일대다 관계 -> 다대일
          일대일 관계 -> 일대일

5.2 연관관계 사용

연관관계를 등록, 수정, 삭제, 조회 예제를 통해 연관관계를 어떻게 사용하는지 알아보자!

5.2.1 저장

// 예제 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에서 엔티티 저장할 때는 연관된 모든 엔티티 영속상태 여야 함.
  • 이때 실행된 SQL : 회원 테이블의 외래 키 값으로 참조한 팀의 식별자 값인 team1 입력됨


5.2.2 조회

  • 객체 그래프 탐색(객체 연관관계를 사용한 조회)
Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // 객체 그래프 탐색
System.out.println("팀 이름 = " + team.getTeam());
// 출력결과 : 팀 이름 = 팀1
  • 객체지향 쿼리 이용(JPQL)
    • JPQL도 조인을 지원하기 때문에 연관된 테이블을 조인해서 검색조건을 사용하여 조회한다.
// 예제 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
  • 이때 실행된 SQL
    : JPQL은 객체(엔티티)를 대상으로 하고 SQL보다 간결.

5.2.3 수정

// 예제 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);
 }
  • 실행되는 SQL
    : 단순히 불러온 엔티티 값만 변경하면 트랜잭션 커밋 시에 플러시가 일어나면서 변경 감지 기능이 작동하여 변경사항을 데이터베이스에 자동으로 반영한다.
    = 연관관계를 수정할 때
    (참조하는 대상만 변경하면 나머지는 JPA가 자동으로 처리)

5.2.4 연관관계 제거

// 예제 5.9) 회원1을 팀에 소속하지 않도록 변경
private static void deleteRelation(EntityManager em) {
	Member member1 = em.find(Member.dclass, "member1");
    member1.setTeam(null); // 연관관계 제거
  • 실행되는 SQL

5.2.5 연관된 엔티티 삭제

  • 기존에 있던 연관관계를 먼저 제거하고 삭제해야함
  • 그렇지 않으면, 외래 키 제약조건 때문에 DB에서 오류 발생.
- 팀1에는 회원1과 회원2가 소속되어 있음.
- 팀1 삭제하려면 연관관계를 먼저 끊어야함.

출처 : 자바 ORM 표준 JPA 프로그래밍 책

좋은 웹페이지 즐겨찾기