김영한님이 지음, [자바 ORM 표준 JPA 프로그래밍] 책을 읽고 정리한 필기입니다.📢

일대다

  • 일대다 관계는 다대일 관계의 반대 방향이다.
  • 일대다 관계는 엔티티를 하나 이상 참조할 수 있으므로 컬렉션(Collection, List, Set, Map) 중 하나를 사용해야 한다.

일대다 단방향[1:N]

하나의 팀은 여러 회원을 참조할 수 있는데 팀은 회원들을 참조하지만 반대로 회원은 팀을 참조하지 않는 경우 : 일대다

img-name
일대다 단방향

  • 보통 자신이 매핑한 테이블의 외래 키를 관리하는데, 이 매핑은 반대쪽 테이블에 있는 외래 키를 관리하여 특이한 모습이 나타남
  • @JoinColumn을 명시해야 함, 그렇지 않으면 JPA는 연결 테이블을 중간에 두고 연관관계를 관리하는 조인 테이블 전략을 사용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
public class Team{
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    @OneToMany
    @JoinColumn(name = "TEAM_ID") // MEMBER 테이블의 TEAM_ID (FK)
    private List<Memeber> members = new ArrayList<Member>();

    //Getter, Setter ...
}
1
2
3
4
5
6
7
8
9
10
@Entity
public class Member{
    @Id @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    private String username;

    //Getter, Setter ...
}

단점

매핑한 객체가 관리하는 외래 키가 다른 테이블에 존재하기 때문에 연관관계 처리를 위한 **추가 UPDATE SQL이 실행 됨**

1
2
3
4
5
6
7
8
9
10
11
12
13
public static void testSave(EntityManager em){
    Member member1 = new Member("member1", "회원1");
    Member member2 = new Member("member2", "회원2");

    Team team1 = new Team("team1", "팀1");
    team1.getMembers().add(member1);
    team1.getMembers().add(member2);

    em.persist(member1);//INSERT-member1
    em.persist(member2);//INSERT-member2
    em.persist(team1);  //INSERT-team1, UPDATE-member1.fk,
                        //UPDATE-member2.fk
}
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
36
37
38
Hibernate: 
    /* insert org.example.Member
        */ insert 
        into
            Member
            (age, createdDate, description, lastModifiedDate, roleType, NAME, MEMBER_ID) 
        values
            (?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    /* insert org.example.Member
        */ insert 
        into
            Member
            (age, createdDate, description, lastModifiedDate, roleType, NAME, MEMBER_ID) 
        values
            (?, ?, ?, ?, ?, ?, ?)
Hibernate: 
    /* insert org.example.Team
        */ insert 
        into
            Team
            (name, TEAM_ID) 
        values
            (?, ?)
Hibernate: 
    /* create one-to-many row org.example.Team.members */ update
        Member 
    set
        TEAM_ID=? 
    where
        MEMBER_ID=?
Hibernate: 
    /* create one-to-many row org.example.Team.members */ update
        Member 
    set
        TEAM_ID=? 
    where
        MEMBER_ID=?
  • Member 엔티티는 Team 엔티티를 모르고, 연관관계에 대한 정보는 Team엔티티의 members가 관리하므로 Member 엔티티를 저장할 때는 MEMBER 테이블의 TEAM_ID 외래 키에는 아무 값도 저장되지 않음
  • Team 엔티티를 저장할 때 Team.members의 참조 값을 확인해서 회원 테이블의 있는 TEAM_ID 외래 키를 업데이트
  • 일대다 단방향 매핑보다는 다대일 양방향 매핑을 사용해야 함
    • 성능 문제도 있고 관리 포인트도 늘어 남
    • 매핑 테이블의 모양또한 완전히 동일하고 엔티티만 약간 수정 해당 문제를 해결할 수 있는 다대일 양방향 매핑을 사용 권장

일대다 양방향[1:N, N:1]

  • 일대다 양방향은 존재하지 않음
    • @OneToMany는 연관관계의 주인이 될 수 없음
    • 관계형 데이터베이스 특성상 일대다, 다대일 관계는 항상 다 쪽에 외래 키 존재하기 때문에 @OneToMany, @ManyToOne 둘 중에 연관관계의 주인은 항상 다 쪽인 @ManyToOne을 사용
    • 때문에 @ManyToOne 에는 mappedBy 속성이 존재하지 않음

그럼에도 양방향을 구현해야 할 경우엔 다대일 단방향 매핑을 읽기 전용으로 추가하면 가능, But. 일대다 단방향 매핑이 가지는 단점은 해결 불가능

img-name
일대다 양방향

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Entity
public class Team {
    @Id @GeneratedValue
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    @OneToMany
    @JoinColumn(name = "TEAM_ID")
    private List<Member> members = new ArrayList<Member>();

    //Getter, Setter ...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class Member {
    @Id @generatedValue
    @Column(name = "MEMBER_ID")
    private Long id;
    private String username;

    @ManyToOne
    @JoinColumn(name = "TEAM_ID", insertable = false, updatable = false)
    private Team team;

    //Getter, Setter ...
}
  • 일대다 단방향 매핑과 같은 TEAM_ID 외래 키 컬럼을 매핑했지만 이렇게 되면 둘 다 같은 키를 관리하므로 문제 발생 가능
    • 다대일 쪽은 insertable = false, updatable = false 로 설정해서 읽기 전용으로 설정 해야 함

JPA 카테고리 내 다른 글 보러가기

댓글남기기