computer_study

[JPA] 05. 연관관계 매핑 기초 본문

스터디/자바 ORM표준 JPA 프로그래밍

[JPA] 05. 연관관계 매핑 기초

knowable 2022. 7. 25. 01:50

1. 단방향 연관관계

객체 연관관계 v.s 테이블 연관관계

  • 객체는 참조로 연관관계를 맺는다 (단방향이다)
    • member.getTeam() 조회가 가능하다고 tean.getMember()가 가능하진 않다.
    • 가능하게 하기 위해선 필드를 추가해야 한다 (양방향 보단 2개의 단방향이 만들어진다.)
  • 테이블은 외래 키로 연관관계를 맺는다 (양방향이다)
  • MEMBER JOIN TEAM이 가능하면 TEAM JOIN MEMBER도 가능하다.

객체 관계 매핑 예시

//**** 객체 ****//
public class Member{
    private String id;
    private String username;
    
    private Team team
    
    //getter, setter
}
public class Team{
    private String id;
    private Stirng name;
    // getter, setter
}

//**** 테이블 ****//
CREATE TABLE MEMBER(
    MEMBER_ID
    TEAM_ID
    USERNAME
    PRIMARY KEY (MEMBER_ID)
)
CREATE TABLE TEAM(
    TEAM_ID
    NAME
    PRIMARY KEY (TEAM_ID)
)

//**** 매핑 ****//
@Entity
public class Member{
    @Id
    @Column(name = "MEMBER_ID")
    private String id;
    
    private String username;
    
    // 연관관계 매핑
    @ManyToOne // 다중성 나타내는 애노테이션 사용이 필수
    @JoinColumn(name="TEAM_ID") // 외래 키를 매핑할 때 사용(name속성에 매핑할 외래 키 이름 지정)
    private Team team;
}

// Team에는 특별한거 없이

주요 속성은 책 참고

 

2. 연관관계 사용

저장

public void save(){
	// 팀1 저장
    Team team1 = new Team("team1", "팀1");
    em.persist(team1);
    
    // 회원1 저장
    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1) // 회원 -> 팀 참조, JPA가 Team의 식별자를 외래 키로 하여 적절한 쿼리를 생성
    eme.persist(member1) // 저장
    
    ...
}

조회

// 객체 그래프 탐색
Member member = em.find(Member.class, "member1");
Team team = member.getTeam(); // get사용해서 객체 그래프 탐색 후 조회 가능
// 자세한 내용은 8장

// 객체지향 쿼리 사용
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")
    .getResultList();

수정

// 팀1 소속이던 회원을 팀2에 소속되도록 수정한다면
private static void updateRelation(EntityManager em){
    // 새로운 팀 생성
    Team team2 = new("team2", "팀2");
    em.persist(team2);
    // 회원에 새로운 팀 설정
    Member member = em.find(Member.class, "member1");
    memberse3tTeam(team2);
}

연관관계 제거

// 회원1을 팀에 소속하지 않도록 변경
private static void deleteRelation(EntityManager em){
    Member member1 = em.find(Member.class, "member1");
    member.setTeam(null); // 연관관계 제거
}

연관된 엔티티 삭제

// 엔티티 삭제를 위해선 연관관계 제거 후 삭제 해야한다.
// team에 member1,2가 소속되어있다면
private static void deleteRelation(EntityManager em){
    member1.setTeam(null);
    member2.setTeam(null);
    em.remove(team);
}

 

3. 양방향 연관관계

위에서는 회원에서 팀으로만 접근했다면, 티멩서 회원으로 접근할 수 있도록

// 팀 엔티티에 추가
@Entity
public clas Team{
    @Id
    @Column(name="TEAM_ID")
    private String id;
    
    private String name;
    
    @OneToMany(mappedBy="team") // 일대 다 관계 (팀 하나에 여러 멤버), 반대쪽 매핑의 필드를 값으로, 연관관계의 주인에서 설명
    private List<Member> members = new ArrayList<Member>();
    // 팀과 회원이 일대다 관계이기 때문에 List를 추가
    ...
}

조회

public void biDirection(){
    Team team = em.find(Team.class, "team1");
    List<Member> members = team.getMembers(); // 객체 그래프 탐색
    ...
}

 

4. 연관관계의 주인

엔티티를 양방향으로 매핑하면 단방향 2개로 관리하기에 누가 외래키를 관리할지 모르게 된다.

이를 관리하기 위해 연관관계주인을 두고 외래키를 관리하도록 한다.

주인만이 DB연관관계와 매핑되고 외래 키를 관리한다.(주인이 아닌 쪽은 읽기만)

 

연관관계의 주인은 외래키가 있는 곳이다.

회원테이블이 FK를 가지고있으므로 주인이 되고, 주인이 아닌 team에 mappedBy="team"설정해서 주인이 아님을 설정한다.

(비즈니스 중요도에 따라 주인을 생각해서는 안된다.)

 

5. 양방향 연관관계 저장

// 팀 생성, 회원 저장은 단방향과 동일

team1.getMembers().add(member1) // team은 주인이 아니기에 외래키에 영향을 주지 않는다.

member1.setTeam(team1) // 주인만이 외래키를 관리할 수 있다.

 

6. 양방향 연관관계의 주의점

위 5번에서 언급한 것 처럼 주인이 아닌 곳에만 값을 입력하는 것을 주의해야 한다.

다만, 객체 관점에서 주인 뿐만 아니라 양쪽 방향에 모두 값을 입력해주는 것이 가장 안전하다.

(모두 입력하지 않고, JPA를 사용하지 않으면 문제가 발생할 수 있기 때문)

Team team1 = new Team("tean1", "팀1");
Member member1 = new Member("member1", "회원1");
Member member2 = new Member("member2", "회원2");

member1.setTeam(team1);
member2.setTeam(team1);

List<Member> members = team1.getMembers();
System.out.println(members.size()) // 0이 나온다.

 

이런 이유로 양방향을 한번에 설정하도록 하는 것이 안전(양방향 관계를 한 번에 설정, 연관관계 편의 메소드)

public class Member {
  private Team team;
  
  public void setTeam(Team team){
      
      // 기존 관계를 없애지 않으면 다른 팀으로 삭제를 해도 기존 관계가 유지되기에
      // set 전에 기존관계 확인 후 관계가 있다면 삭제를 우선 해주어야 한다.
      if(this.team != null){
          this.team.getMembers().remove(this);
      }
  
      this.team = team;
      team.getMembers().add(this);
  }
  ...
}

객체에서 양방향 연관 관계를 사용하려면 로직을 견고하게 작성 할 필요가 있다.

복잡하기에, 단방향 매핑을 우선 사용하다 필요 시 양방향을 추가하는 편이 좋다.

'스터디 > 자바 ORM표준 JPA 프로그래밍' 카테고리의 다른 글

[JPA] 07. 고급 매핑  (0) 2022.08.08
[JPA] 06. 다양한 연관관계 매핑  (0) 2022.07.31
[JPA] 04. 엔티티 매핑  (0) 2022.07.11
[JPA] 03. 영속성 관리  (0) 2022.06.29
[JPA] 02. JPA 시작  (0) 2022.06.29
Comments