Development/Spring

[JPA] JPA 단방향 vs 양방향

hyelie 2022. 10. 4. 15:00

JPA를 이용해서 구성하고 있는데... 다대일 단방향과 양방향에 대한 고찰이다.

일반적으로 다대일을 쓰는 거라고 한다.

그러면 member - team 관계가 있다고 가정하자.. (member:team=n:1 관계)

 

여러 자료들을 찾아보니 다대일 관계를 가질 때, '다' 쪽에서 외래키를 가지고 다대일 단방향 관계를 구성한다고 배웠ㄷ다. 필요할 때 양방향 관계를 구성한다고 하고. 그런데 이 '필요할 때'를 잘 모르겠다. 정말 양방향 연결이 필요한가?

JPA는 '다'쪽에서 '1'쪽에 대한 정보를 가지고 있고, '1'쪽에서 '다'쪽에 대한 정보는 가지고 있지 않다. member-team의 관계에서 member는 어떤 team인지 알 수 있지만, team에 어떤 member가 있는지는 알 수 없기 때문이다. 그래서 다대일 양방향 매핑을 하면 아래와 같이 team entity 내부에 member entity list를 저장한다.

@Entity
public class Member {
  ...
  
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
  
  ...
}

@Entity
public class Team {
    ...
     @Id
    private String teamname;

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

이후에

entityManager.find(Team.class, "team1");

이렇게 하면 원하는 team1에 해당하는 member list를 가져올 수 있게 된다. 이 방식을 방법 1이라고 하자.

 

그런데 이렇게 구현하지 않고 다대일 단방향 관계만 만든 후에 아래와 같은 방식으로 구현할 수도 있다고 생각한다. 이 방식을 방법 2라고 하자.

em.createQuery("select m from Member m where m.team.teamname = team1"); // 또는  Team team1 = new Team("team1); em.createQuery("select m from Member m where m.team = :variable").parameter("variable", team1);

 

한편, jpa 영속성은 1차 캐시에서 먼저 찾고 없으면 mysql로 접근하는 것으로 알고 있다. 방법 1의 경우 pk로 영속성에서 검색하는 것이니 방법 2의 경우보다 영속성 검사 시간도 짧을 것이라 예상된다. 또한 java의 arraylist 구현체를 살펴보니 배열로 구현되어 있어서 random access 이슈는 생각하지 않아도 될 것이라 생각해서 양방향 매핑을 쓰는 게 entity 생성할 때는 제외하곤, 개발할 때 편할 것이라고 생각했다.

 

그런데 대부분 다대일 단방향만 구성한다고 한다.

 

고민해본 내용은 아래와 같다.

 

1) 다대일 양방향 관계를 잘 구성하지 않는 이유

양방향은 entity가 너무 복잡해진다. 무한루프 돌지 않게 검사도 해 줘야 하고. 그래서 잘 쓰지 않는다.

 

2) 방법 1, 방법 2의 성능 측면에서는 어느 쪽이 좀 더 우세한지

성능은, 어차피 영속성으로 query를 날리는 것이기 때문에 큰 차이 없다.

* 영속성이 좋은 이유

DB를 캐싱하는 것이라 생각하면 된다. DB를 캐싱하면 정보 읽기/쓰기가 빨라지는 건 당연하고, DB 내부에 query를 보내면 query optimizer가 수행시간 예측, 수행 방법 결정 등을 하는데, 이게 시간이 걸리기 때문이다. 그러나 JPA를 쓰면 query optimizer가 없기 때문에 더 빠르다.

 

3) 그래서 양방향 구성 언제 함?

JPA는 sql을 몰라도 object-oriented로 모든 것을 처리할 수 있게 된다. SQL을 몰라도 database를 spring에서 이용 할 수 있게 되는 것이다. (JPA를 사용하는 이유 중에 이것도 있다고 한다... 왜지? 너무 불편한데...) 그러나 object-orient로 team-member 관계에서 team에 속한 member를 출력하라, 는 명령을 처리하기 위해서는 양방향 관계가 필요하다.

그러나, 나는 SQL을 쓸 줄 안다... 그래서 양방향 관계 굳이 필요없이 team에 속한 member를 찾을 수 있다. 근데 객체로 찾으려면 이 한줄이 3줄로 바뀐다.

em.createQuery("select m from Member m where m.team.teamname = team1");

그래서, 나는 양방향 구성 안할거다.