JPA & Querydsl/JPA

3. 연관관계 매핑 기초

1. 연관관계가 필요한 이유

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

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

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

   (테이블과 객체 사이에는 큰 간격이 있다(패러다임의 차이))

 

객체를 테이블에 맞추어 모델링의 예 - 협력 관계가 없음)

- 참조 대신에 외래 키를 그대로 사용

@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);

2. 단방향 연관관계

연관관계를 가지는 두 테이블에 대한 객체 중 한쪽이 다른 한쪽에 대한 관계를 가지는 관계

 

 

객체 지향 모델링의 예)

member에서 team을 참조를 통해서 값을 얻을 수 있다.

- 객체의 참조와 테이블의 외래 키를 매핑

@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();

3. 양방향 연관관계와 연관관계의 주인

mappedBy를 통해서 team에서 member를 참조할수 있게 설정할 수 있음.

@Entity
 public class Team {
   @Id @GeneratedValue
   private Long id;
   private String name;
   @OneToMany(mappedBy = "team")
   List<Member> members = new ArrayList<Member>();
   …
 }

이를 통해서 member -> team, team ->member로의 연관관계를 가지는 것을 양방향 연관관계라고 한다.

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

객체를 양방향으로 참조하려면 단방향 연관관계를 2개 만들어 야 한다.

객체 연관관계 = 2개

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

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

테이블 연관관계 = 1개 

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

 

양방향 매핑 규칙

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

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

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

- 주인은 mappedBy 속성 사용X, 주인이 아니면 mappedBy 속성으로 주인이 어느 엔티티의 값인지 지정

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

- 양방향 매핑시 연관관계의 주인에 값을 입력 (순수한 객체 관계를 고려하면 항상 양쪽다 값을 입력 권장)

Team team = new Team();
 team.setName("TeamA");
 em.persist(team);
 Member member = new Member();
 member.setName("member1");
 //역방향(주인이 아닌 방향)만 연관관계 설정
 team.getMembers().add(member);
 em.persist(member);

 

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);

관련해서 이해하기 도움이 되는 영상

 

백기선님 영상 www.youtube.com/watch?v=brE0tYOV9jQ&t=202s

 

양방향 매핑 정리

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

- 단방향으로 우선 전체를 셋팅하고 필요할때 양방향을 추가하는 방향

 

 

실습 전체 코드

import javax.persistence.*;

@Entity
public class Member3 {

    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

    @ManyToOne // n : 1
    @JoinColumn(name = "TEAM_ID") // 조인 칼럼 설정
    private Team team;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    private Locker locker;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public Team getTeam() {
        return team;
    }

    public void changeTeam(Team team) {
        this.team = team;
        team.getMembers().add(this);
    }
}
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member3> members = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Member3> getMembers() {
        return members;
    }

    public void setMembers(List<Member3> members) {
        this.members = members;
    }
}
import javax.persistence.*;

@Entity
public class Locker {

    @Id @GeneratedValue
    @Column(name = "LOCKER_ID")
    private Long id;
    private String name;

    // 양방향
    @OneToOne(mappedBy = "locker")
    private Member3 member;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import java.util.List;

public class JpaRelationMappingTest {
    public static void main(String[] args) {
        // persistence.xml 의 persistence-unit 을 받아서 EntityManagerFactory 생성 - 하나만 생성해서 전체 어플리케이션 공유
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("test03");

        //EntityManagerFactory 에서 EntityManager 생성 - 쓰레드간에 공유 X (사용하고 버려야함)
        EntityManager em = emf.createEntityManager();

        // EntityManager 의 트랜잭션 받아옴
        EntityTransaction tx = em.getTransaction();

        // 트랜잭션 시작 - 모든 데이터 변경은 이 안에서 처리해야함
        tx.begin();

        try {
            Team team = new Team();
            team.setName("TemaA");
            em.persist(team);

            Member3 member = new Member3();
            member.setUsername("이름");
            member.changeTeam(team);
            em.persist(member);

            System.out.println("===========");
            for (Member3 member3 : member.getTeam().getMembers()) {
                System.out.println(member3.getUsername());
            }
            System.out.println("===========");

            Member3 findMember = em.find(Member3.class, member.getId());

            Team findTeam = findMember.getTeam();

            List<Member3> members = findTeam.getMembers();
            System.out.println("===========");
            for(Member3 obj : members) {
                System.out.println(obj.getUsername());
            }
            System.out.println("===========");

            // 트랜잭션 커밋
            tx.commit();
        } catch (Exception e){
            // 롤백
            tx.rollback();
        } finally {
            // EntityManager 닫음
            em.close();
        }

        // EntityManagerFactory 닫음
        emf.close();
    }
}

 

Reference

자바 ORM 표준 JPA 프로그래밍 - 김영한님

'JPA & Querydsl > JPA' 카테고리의 다른 글

5. 고급 매핑  (0) 2021.02.24
4. 다양한 연관관계 매핑  (0) 2021.02.24
2. 엔티티 맵핑  (0) 2021.02.21
1. 영속성 관리  (0) 2021.02.20
인텔리제이 자바 버전 변경  (0) 2021.02.16