Jpa OneToMany 정리

Simple is Best·2021년 7월 13일
0

Jpa

목록 보기
7/7

source 는 Github 에 있습니다.

단방향 OneToMany JoinColumn 없음

  • 아래와 같이 JoinColumn 없이 OneToMany 단방향을 설정하면 team, team_member 테이블 간 mapping 테이블이 생성 됩니다. (team_list 테이블)
  • 테이블이 새로 생성되고 해당 테이블에 insert 쿼리가 발생되기에 비효율적입니다.
insert into team (team_name) values (?)

insert into team_member (name) values (?)
insert into team_member (name) values (?)

insert into team_list (team_team_id, list_member_id) values (?, ?)
insert into team_list (team_team_id, list_member_id) values (?, ?)

@Test
public void 단방향_OneToMany_테스트() throws Exception {
    // given
    List<TeamMember> teamMembers = new ArrayList<>();
    TeamMember teamMemer = TeamMember.builder()
            .name("testMember1")
            .build();
    
    teamMembers.add(teamMemer);
    
    
    TeamMember teamMemer2 = TeamMember.builder()
            .name("testMember2")
            .build();
    teamMembers.add(teamMemer2);
    
    Team team = Team.builder()
            .teamName("testName")
            .teamMembers(teamMembers)
            .build();
    
    // when
    Team result = teamRepository.save(team);
    
    // then
    Assert.assertThat(result.getTeamMembers().size(), is(2));
}


@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {

    @Id @Column(name = "team_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String teamName;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    List<TeamMember> teamMembers = new ArrayList<>();

    @Builder
    public Team(Long id, String teamName, List<TeamMember> teamMembers) {
        this.id = id;
        this.teamName = teamName;
        this.teamMembers = teamMembers;
    }
}

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class TeamMember {
    @Id @Column(name = "member_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String name;

    @Builder
    public TeamMember(Long id, String name) {
        this.id = id;
        this.name = name;
    }
}

단방향 OneToMany JoinColumn 존재

  • JoinColumn 을 설정해주면 맵핑테이블이 사라지고, 맵핑 테이블에 데이터를 넣는 쿼리가 빠집니다.
  • 문제는 아래와 같이 테스트 했을 때, update 쿼리가 추가로 발생합니다.
  • 아래와 같이 업데이트 쿼리가 발생하는 이유는 team_member 에서는 team 에 대한 정보가 없기에 전부 insert 한 후, team_member_id 로 데이터를 찾아서 team_id 를 넣어줍니다.

insert into team (team_name) values (?)

insert into team_member (name) values (?)
insert into team_member (name) values (?)

update team_member set team_id=? where team_member_id=?
update team_member set team_id=? where team_member_id=?

@Test
public void 단방향_OneToMany_테스트() throws Exception {
    // given
    List<TeamMember> teamMembers = new ArrayList<>();
    TeamMember teamMemer = TeamMember.builder()
            .name("testMember1")
            .build();
    
    teamMembers.add(teamMemer);
    
    
    TeamMember teamMemer2 = TeamMember.builder()
            .name("testMember2")
            .build();
    teamMembers.add(teamMemer2);
    
    Team team = Team.builder()
            .teamName("testName")
            .teamMembers(teamMembers)
            .build();
    
    // when
    Team result = teamRepository.save(team);
    
    // then
    Assert.assertThat(result.getTeamMembers().size(), is(2));
}

public class Team {

    @Id @Column(name = "team_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column
    private String teamName;

    // 조인 컬럼 단방향 매핑
    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    @JoinColumn(name = "team_id")
    List<TeamMember> teamMembers = new ArrayList<>();

}

양방향 OneToMany

  • team, team_member 에 대한 insert 쿼리만 발생합니다.
insert into team (team_name) values (?)

insert into team_member (name) values (?)
insert into team_member (name) values (?)
@Test
@Transactional
public void 양방향_OneToMany_테스트() throws Exception {

    // given
    Team team = Team.builder()
            .teamName("testName")
            .build();
    team.addTeamMember("teamMember11");
    team.addTeamMember("teamMember22");

    // when
    Team result = teamRepository.save(team);

    // then
    Assert.assertThat(result.getTeamMembers().size(), is(2));
}



@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Team {
    @OneToMany(mappedBy = "team", cascade = CascadeType.ALL, orphanRemoval = true)
    List<TeamMember> teamMembers = new ArrayList<>();

    ...
    ...
}


@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@ToString
public class TeamMember {
    @ManyToOne(optional = false)
    @JoinColumn(name = "team_id")
    private Team team;
    
    ...
    ...

}

결론

  • 양방향보단 단방향을 쓰는게 좋다고 생각합니다.
  • 양방향을 쓰는 순간 로직의 복잡도가 올라가고 어떤 문제가 생길지 모릅니다.
  • 그렇기에 될 수 있으면 단방향을 쓰고 필요할 경우 양방향을 쓰는게 좋습니다.
  • 단방향으로 쓰는게 좋으며, JoinColumn 을 써주면 좋습니다. (별도 맵핑 테이블이 안생기기에)
  • 단방향으로 썼는데 불필요한 쿼리가 발생할 때는 양방향을 고려해보는 것이 좋다.
  • 위와는 별개로 List 초기화를 해주는게 좋습니다. 데이터가 없어서 null 인지 의도적으로 null 로 선언한 것인지 구분 할 수가 없습니다.
  • 또한, Jpa OneToMany 와 같은 것을 사용할 때, 단위테스트를 해서 어떻게 동작하는지 파악하면 좋다고 생각합니다.

reference

profile
Simple is best

0개의 댓글