쌓고 쌓다

연관관계 (단방향, 양방향) 본문

프로그래밍/JPA

연관관계 (단방향, 양방향)

승민아 2023. 8. 1. 01:24

게시판을 만들어보다보니 연관관계 부분을 공부할 필요가 느껴졌다! 파일 업로드 부분을 위해...!

그래서 JPA 책을 펼쳐 일부 읽고 조금 아하했다. 다음 챕터에 더 상세한 내용은 다음에 읽고 이해한 부분까지 정리한다.

 

엔티티는 다른 엔티티와의 관계를 맺는다.

선수는 팀과의 관계를 맺는 예시가 있다.

여기서 객체는 참조(주소)로 관계를 맺고, 테이블은 외래 키를 사용해 관계를 맺는다.

 

아래의 설명들을 보기전에 Member와 Team의 구조를 보자.

 

Team

(1) 테이블

CREATE TABLE team (
    id VARCHAR(255) NOT NULL,
    name VARCHAR(255),
    PRIMARY KEY (id)
);

 

(2) 클래스

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Team {

    @Id
    private String id;
    private String name;
    
}

간단히 요약하면 팀에 PK와 이름을 갖는다.

 

Member

(1) 테이블

CREATE TABLE MEMBER (
    id VARCHAR(255),
    username VARCHAR(255),
    team_id VARCHAR(255),
    PRIMARY KEY (id),
    FOREIGN KEY (team_id) REFERENCES team(id)
);

 

(2) 클래스

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Member {

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

    @Column(name="username", nullable = false, length = 10)
    private String username;

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

@ManyToOne : 회원과 팀은 다대일 관계이다. 연관관계를 매핑할 때 다중성을 나타내야함.

*다중성: 일대다, 다대일, 일대일, 다대다

 

@JoinColumn : 외래 키를 매핑할 때 사용한다. Member 테이블의 team_id 컬럼과 매핑됨! 즉, 매핑할 외래 키의 이름. 단순히 컬럼명을 만드는것임. 상대 테이블의 PK가 아니라 자기 자신 테이블의 외래키 컬럼명임!

 

단방향 연관관계

객체 연관관계에서 보자.

Member는 team 필드를 가져 Team 객체와 연관관계를 가진다.

member는 team 필드로 team을 알 수 있지만, team은 member 필드가 없어 접근할 수 없다.

이것을 회원 객체와 팀 객체는 단방향 연관관계라고한다.

=> Team 객체에도 List<Member>로 참조한다면 양방향 연관관계라고 볼 수 있다.

(정확히는 서로 다른 단방향 관계가 2개)

 

테이블 연관관계에서 보자.

Member는 Team 테이블의 team_id 외래 키를 갖는다.

이것으로 Member과 Team을 조인할 수 있다.

반대로 Team 입장에서도 id(PK)를 가지고 Member와 조인할 수 있다.

이것을 회원 테이블과 팀 테이블은 양방향 관계라고 한다.

=> 테이블은 외래 키 하나로 양방향 조인이 가능해 항상 

 

연관관계 사용 (저장)

저장

Team team1 = new Team("team1", "팀1");
em.persist(team1);

Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
em.persist(member1);

Member member2 = new Member("member2", "회원2");
member2.setTeam(team1);
em.persist(member2);

Member 클래스 일부

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

team 필드는 외래키 매핑하는 @JoinColum을 사용했다. 이때 이름은 "team_id"이므로

Member 테이블의 team_id 컬럼에는 Team의 id와 관계를 맺는다.

 

아니? 그런데 Team 테이블의 어떤 컬럼과 매핑할지 아는거죠!?!?

@JoinColumn의 속성 referencedColumnName이 있다.

 

@JoinColumn

속성 기능 기본값
name 외래 키 컬럼명 필드명_참조하는 테이블 기본키 컬럼명
referencedColumnName 외래 키가 참조하는 대상 테이블의 컬럼명 참조하는 테이블의 기본 키 컬럼명

referencedColumnName 속성의 기본값이 참조하는 테이블의 기본 키 컬럼명이라 Team의 기본 키인 id와 매핑이 된다.

 

양방향 연관관계

Team에서도 Member를 접근할 수 있는 관계를 추가한다.

@Entity
@Getter
@Setter
@NoArgsConstructor
public class Team {
    ...
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
}

일대다 관계에서 여러개와 관계를 맺을때 컬렉션을 사용해야한다.

*컬렉션: 자바가 지원하는 Collection, Set, Map, List

 

그럼 데이터베이스에는 뭘 추가해주지??

테이블 연관관계는 그대로이다. 외래 키 하나로 양방향 조회가 가능하므로

데이터베이스에 추가할 내용은 없다.

List<Member>가 생겼다고해서 Team 테이블에는 변화가 있는건 아니다. 외래키로 항상 양방향이기 때문.

 

 

@mappedBy

양방향 매핑일때 사용하는 어노테이션이다.

엔티티를 양방향 연관관계로 설정하면 객체의 참조는 둘인데 외래 키는 하나다.

이 차이를 없애고자 테이블의 외래 키를 관리하는 연관관계의 주인을 정한다.

연관관계의 주인만 외래 키를 관리(등록, 수정, 삭제)할 수 있다.

주인은 외래 키를 관리하므로 테이블에 외래 키를 가지는 곳으로 정한다.

외래 키가 없는 테이블이 외래 키를 관리하는 주인이 될 수 없지 않은가..

 

주인이 아닌쪽에 @mappedBy 속성으로 연관관계의 주인을 지정한다.

반대쪽 매핑의 필드 이름을 값으로 준다.

 

연관관계 사용 (저장 및 일대다 컬렉션 조회)

저장

Team team1 = new Team("team1", "팀1");
em.persist(team1);

Member member1 = new Member("member1", "회원1");
member1.setTeam(team1);
em.persist(member1);

Member member2 = new Member("member2", "회원2");
member2.setTeam(team1);
em.persist(member2);

저장 코드는 동일하다.

team1.getMembers().add(member1);

team1.getMembers().add(member2);

와 같은 코드가 있어야할 것 같지만 연관관계의 주인이 아니기에 외래 키에 영향을 줄 수 없어 무시된다.

그러면 Team의 members에서 어떻게 조회한단 말인가?

 

일대다 컬렉션 조회

Team team = em.find(Team.class, "team1");
List<Member> members = team.getMembers();

for(Member member : members) {
    System.out.println("member = " + member.getUsername());
}

em.find에서 Team을 조회하고.

Hibernate: select t1_0.id,t1_0.name from team t1_0 where t1_0.id=?

 

Team의 id와 일치하는 Member를 찾는다.

Hibernate: select m1_0.team_id,m1_0.id,m1_0.username from member m1_0 where m1_0.team_id=?

Comments