JPA

[JPA] 관계 매핑 기초 정리

Jeong Jeon
반응형

연관관계 매핑을 정리해두고, JPA에서는 어떻게 사용하는지 정리해 두려고 한다.

 

 

  • 방향 : 회원/팀 기준
    • 단방향 : 회원 → 팀 or 팀 → 회원 둘 중 한 쪽만 다른쪽을 참조하는 관계
    • 양방향 : 회원 → 팀, 팀 → 회원 둘이 서로를 참조하는 관계
  • 다중성
    • 1:1 (일대일)
    • 1:N (일대다 혹은 다대일)
    • N:M (다대다)
  • 연관관계의 주인 : 양방향 연관관계를 만들 때 연관 관계의 주인을 정해야 한다. => 누가 주인인지 꼭 필요.

 

단방향 : 

회원은 하나의 팀에만 소속될수 있다. => 회원과 팀은 다대일 관계

쿼리를 한번 확인해보자.

  • 회원 중심 조회
SELECT * 
FROM MEMBER M 
INNER JOIN TEAM T ON M.TEAM_ID = T.TEAM_ID
  • 팀 중심 조회
SELECT * 
FROM TEAM T 
INNER JOIN MEMBER M ON M.TEAM_ID = T.TEAM_ID

 

 

이걸 자바객체로 만들어보자.

  • 회원중심
class Team { 
	Long id;
	String teamName;
    
    ..getter ..setter
}

class Member { 
	Long id; 
    String memberName;
    Team team;
    
    ..getter ..setter
}
// 참조
Member a = new Member(); 
a.getTeam.getId();
  • 팀중심
class Team { 
	Long id;
    String teamName;
    Member id;  
    
    ...getter setter
} 

class Member { 
	Long id;
    String memberName;
    
    ...getter setter
}

// 참조
Team a = new Team(); 
a.getMember.getId();

 

객체는 양방향 참조를위해 두 객채에 각각 참조할 대상을 필수로 가지고있어야한다.. 당연..!

쿼리와 객체에서의 차이점만 살짝보고~

 

객체 관계 매핑을 JPA활용하여 만들어보면서 이해하도록 하자.

 

1). Team Class

@Entity
 @Getter
 @Setter
 public class Team {

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

     private String teamName;
 }

2). Member Class

 @Entity
 @Getter
 @Setter
 public class Member {

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

     private String memberName;

     @ManyToOne
     @JoinColumn (name = "TEAM_ID")
     private Team team;
 }

@ManyToOne 과 @JoinColumn을 통해 다대일 관계를 나타냈다.

하나씩 이해해보자.

 

@ManyToOne

  • 속성
    • optional (default true) : false로 설정하면 연관된 엔티티가 반드시 존재해야한다.
    • fetch : 글로벌 패치 전략 설정 (Lazy or Eager)
    • cascade : 영속성 전이 기능 사용
    • targetEntity : 연관된 엔티티의 타입 정보 설정 (targetEntity = Member.class 식으로 사용)

 

@JoinColumn(name="TEAM_ID")

  • 외래 키 매핑
  • name 속성은 매핑할 외래키의 이름
  • 어노테이션을 생략해도 외래 키 생성
    • 생략 시 외래키의 이름이 기본 전략을 활용하여 생성
  • 속성
    • name : 매핑할 외래 키의 이름
    • referencedColumnName : 외래 키가 참조하는 대상 테이블의 컬럼명
    • foreignKey : 외래 키 제약조건 지정 (테이블 생성 시에만 적용됨)
    • unique/nullable/insertable/updateable/columnDefinition/table : @Column의 속성과 같음

사용할때는 member에 team을 넣어주는 방식으로 사용하는데, 내부적으로 entityManager가 persist(); 할때 team객체에 id가 부여된다 => 중요

 

양방향 : 

양방향 관계는 양쪽에서 서로를 참조하고 있는 상태이다.

양방향 관계를 가지는 엔티티를 만들어보자.

 

1). Team Class

@Entity
 @Getter
 @Setter
 public class Team {

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

     private String teamName;

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

 

2). Member Class

@Entity
 @Getter
 @Setter
 public class Member {

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

     private String memberName;

     @ManyToOne
     @JoinColumn (name = "TEAM_ID")
     private Team team;
 }

Team에 @OneToMany 어노테이션을 추가해줬다.

Team1개에 여러명의 회원이 있을수 있으니, List<Member> List로 필드를 추가한다.

mappedBy 속성은 반대 클래스의 매핑 값(MemberClass에 Team team) 을 부여하면 된다. => 양방향이기 때문에 고유키를 서로 외래키로 하지 않아도 되지만 외래키의 위치를 잡기 위해 사용한다.

 

연관관계의 주인이란?

연관관계의 주인은 외래키를 가지고있는 엔티티가 주인이다 !!!!

즉 mappedBy는 주인을 지칭하는거로 생각하면 된다.

==> Team.members은 mappedBy = "team" => Member.team을 사용한다고 보면된다.

 

양방향 연관관계 저장시 주의사항

주인이 아닌 필드는 변경해도 변경저장이 되지않는다.

코드를 한번보자.

private void updateEntity(EntityManager em) {

    Team team = em.find(Team.class, team_id);

	// Member 내용은 저장되지 않음
    team.getMembers.add(
        Member.builder()
            .name("member3")
            .build();
    ); 
    
   	
	// 저장됨
    Member member = em.find(Member.class, member1_id);
    member1.setTeam(team2); 
    
    
}

왜 이런지 확인해보자.

물리적인 DB Team 에는 Member관련 컬럼이 없다!! 그래서, member에 Team을 등록할때는 Team정보가 저장되지만 Member관련 정보는 저장되지 않는다.

 

천천히 한걸음씩~

 

반응형