JPA02. 관계
포스트
취소

JPA02. 관계

1:N 관계

@ManyToOne 단방향

1
2
3
4
5
6
7
8
9
10
11
12
@Getter @Setter
@Entity
public class Account {

    @Id @GeneratedValue
    private Long id;

    @Column(nullable=false, unique=true)
    private String username;

    private String password;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
@Getter @Setter
@Entity
public class Study {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @ManyToOne // <- FK
    @JoinColumn(name="owner_id")
    private Account owner;
}

Study에서는 Account를 참조할 수 있지만, Account에서는 Study를 알 수 없음.

@ManyToOne : Study에 Account 관계를 맺어줬음. 단방향임.

@JoinColumn : 생략 가능함. ‘필드명_참조하는필드명’ 으로 생성됨. 위의 경우 생략하면 컬럼 이름은 owner_id

 

깨알팁) @ManyToOne@OneToMany가 헷갈린다면..

끝쪽을 보세요! 끝이 One으로 끝나면 단일, Many로 끝나면 컬렉션인 레퍼런스를 쓴다고 생각!

현재 Reference가 Collection이 아니라 Account니까 One으로 끝나는거다!

결과

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE TABLE account (
    id int8 not null, 
    password varchar(255), 
    username varchar(255) not null, 
    primary key (id)
)

CREATE TABLE study (
    id int8 not null, 
    name varchar(255), 
    owner_id int8, 
    primary key (id)
)

ALTER TABLE IF EXISTS account 
ADD CNSTRAINT UK_gex1lmaqpg0ir5g1f5eftyaa1 UNIQUE (username)

ALTER TABLE IF EXISTS study 
ADD CONSTRAINT FK210g5r7wftvloq2ics531e6e4 
FOREIGN KEY (owner_id) REFERENCES account

Study는 onwer의 PK인 ower_id를 FK로 가지고 있음.

   

@OneToMany 단방향

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Getter @Setter
@Entity
public class Account {

    @Id @GeneratedValue
    private Long id;

    @Column(nullable=false, unique=true)
    private String username;
    private String password;

    @OneToMany
    private Set<Study> studies;
}
1
2
3
4
5
6
7
8
9
@Getter @Setter
@Entity
public class Study {

    @Id @GeneratedValue
    private Long id;

    private String name;
}

@OneToMany : 이번에는 Account에서 Study 관계를 맺어줬음. 단방향임.

조인테이블이 만들어짐.

결과

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
create table account (
    id int8 not null, 
    password varchar(255), 
    username varchar(255) not null, 
    primary key (id)
)

create table account_studies (
    account_id int8 not null, 
    studies_id int8 not null, 
    primary key (account_id, studies_id)
)

create table study (
    id int8 not null, 
    name varchar(255), 
    primary key (id)
)


alter table if exists account 
add constraint UK_gex1lmaqpg0ir5g1f5eftyaa1 
unique (username)

alter table if exists account_studies 
add constraint UK_tevcop76y9etp9vx5vce7gns6 
unique (studies_id)

alter table if exists account_studies 
add constraint FKem9ae62rreqwn7sv2efcphluk 
foreign key (studies_id) references study

alter table if exists account_studies 
add constraint FK4h3r1x3qcsugrps8vc6dgnn25 
foreign key (account_id) references account

이건 바꿀 수있음. 위는 빙산의 일각에 불과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Account {
    // ...
    
    // 어떤 한 유저에 스터디를 추가할 때, 해당 스터디에서의 정보도 함께 변동되어야 함
    public void addStudy(Study study) {
        this.getStudies().add(study);
        study.setOwner(this);
    }

    public void removeStudy(Study study) {
        this.getStudies().remove(study);
        study.setOwner(null);
    }
}

   

@OneToMany, @ManyToOne 양방향

RDB에는 FK 한개로 양방향 관계를 가짐.

그런데 객체에서의 양방향은 각 클래스에서 참조를 하게 됨.

이 때 생각해야할 것이 있음. 한쪽에 업데이트를 하면 반대쪽도 업데이트 되야 한다는 것임.

=> 그럼 둘중에 한군데를 주인으로 만들고, 주인이 아닌 쪽은 읽기만 가능하도록 하자!

 

주인이 아닌 쪽은 읽기만 가능. -> DB에 영향을 주지 않음. 양방향은 단순히 조회를 편하게 하기 위한 부가기능!? 나중에 DDL보면 @ManyToOne 단방향이랑 같을거임.

누구를 주인으로 할까? FK 가지고 있는 쪽을 관계의 주인으로 하자. 이렇게 안하면 어떤 테이블 수정헀는데 다른테이블에 쿼리가 날라가면서 멘붕에 빠질 수 있음.

주인이 아닌 쪽에서는 mappedBy 설정을 해줘야 함. 안하면 양방향이 아닌, 두개의 단방향 관계가 되서 꼬여버림.

mappedBy는 양방향 관계일 때 사용. 매핑된 엔터티의 필드명을 적으면됨.

주인이 아닌쪽에서 추가해버리면 DB에 반영 안됨.

1
2
3
4
5
6
7
@Entity
public class Account {
    // ...

    @OneToMany(mappedBy = "owner")
    private Set<Study> studies = new HashSet<>();
}
1
2
3
4
5
6
7
public class Study {
    // ...

    @ManyToOne
    @JoinColumn(name="owner_id")
    private Account owner;
}

결과

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE TABLE account (
    id int8 not null, 
    password varchar(255), 
    username varchar(255) not null, 
    primary key (id)
)

CREATE TABLE study (
    id int8 not null, 
    name varchar(255), 
    owner_id int8, 
    primary key (id)
)

ALTER TABLE IF EXISTS account 
ADD CNSTRAINT UK_gex1lmaqpg0ir5g1f5eftyaa1 UNIQUE (username)

ALTER TABLE IF EXISTS study 
ADD CONSTRAINT FK210g5r7wftvloq2ics531e6e4 
FOREIGN KEY (owner_id) REFERENCES account

-> @ManyToOne 단방향이랑 스키마가 똑같다!!

 

김영한님 팁!

  • 설계할 때는 무조건 단방향으로 설계! (양방향은 좋은것이 아님. 순환참조라는게..)
  • 개발 도중에 양방향이 필요하다고 생각할 경우 추가. (DB에 영향 안주니까 나중에 추가되도 괜찮음)
  • 양방향은 조회하기 편하려고 부가기능이 들어간 것!
  • 아리까리하면 JPA책 볼 것ㅋㅋ
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.

JPA01. 엔터티

JPA03. 엔터티 생명주기

Comments powered by Disqus.