본문 바로가기
Data Base/JPA

[JPA] : 양방향 연관관계와 연관관계의 주인(Owner)에 대한 주의점

by 오주현 2022. 4. 8.
반응형
  • 양방향 매핑 시 가장 많이 하는 실수
//저장
Member member = new Member();
member.setUsername("member1");
em.persist(member);

Team team = new Team();
team.setName("TeamA");
team.getMembers().add(member);
em.persist(team);

em.flush();
em.clear();

tx.commit();
  • 이렇게 코드를 바꾸고 실행을 해 본다.

  • TEAM_ID가 null이 뜬다.
//저장
Team team = new Team();
team.setName("TeamA");
//team.getMembers().add(member); //<- 중요 포인트
em.persist(team);

Member member = new Member();
member.setUsername("member1");
member.setTeam(team); // <- 중요 포인트
em.persist(member);

em.flush();
em.clear();

tx.commit();
  • 이렇게 바꿔서 코드를 실행해 본다.
  • 연관관계의 주인에만 값을 넣고 연관관계의 주인이 아닌 곳에는 값을 뺴 본다.

  • 이제 TEAM_ID에 1이 들어갔다.
  • 양방향 매핑 시 가장 많이 하는 실수는 위와 같이 연관관계의 주인에 값을 넣지 않고 다른 곳에 값을 넣는 경우가 잦은 점이다.
  • 하지만, 양방향 매핑 시 가능하면 양쪽에 값을 다 넣어주는 게 좋다.
team.getMembers().add(member);
  • 둘 다 안 써줘도, 위 코드를 주석처리하고 돌려도 정상으로 돌아간다.
  • 하지만 문제가 있는데 em.flush() em.clear()를 하면 문제가 없다. 하지만 flush와 clear를 안 하면 1차 캐시에서 조회된 것이 그대로 튀어나온다.
Team findTeam = em.find(Team.class, team.getId());
List<Member> members = findTeam.getMembers();

for (Member m : members) {
    System.out.println("m.getUsername() = " + m.getUsername());
}
  • 코드로 확인을 해보면 select 쿼리가 안 나간다.
  • 때문에 객체 지향적으로 생각했을 때 양쪽에 다 값을 넣어주는 게 맞다.
  • 특히 테스트 케이스를 만들 때 양쪽을 사용해야 하기 때문에 양방향 연관관계를 셋팅할 때는 양쪽에 다 값을 셋팅해 줘야 하는 게 맞다.
    • 순수 객체 상태를 고려한다.
  • 연관관계 양방향 설정을 깜빡할 수 있기도 하고 편리하게 사용하기 위해서 연관관계 편의 메소드를 생성하는 게 좋다.
    • Member.class에서 설정한 부분을
    public void setTeam(Team team) {
            this.team = team;
            team.getMembers().add(this);
        }
    
    • 이렇게 바꿔준다.
    //저장
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);
    
    Member member = new Member();
    member.setUsername("member1");
    member.setTeam(team); //<- 중요 포인트
    em.persist(member);
    
    //team.getMembers().add(member); // <- 중요 포인트
    
    • 결과적으로 이렇게 setTeam을 호출해도 양쪽으로 값이 걸린다.
    • 추가 팁으로 이때, 메소드 명에 set을 사용하면 setter 관례에 따라 사용한 게 되므로 이 경우 메소드 명을 바꿔준다고 한다.
    public void changeTeam(Team team) {
            this.team = team;
            team.getMembers().add(this);
        }
    
    • 이렇게 setTeam → changeTeam으로 chage를 주로 넣는다고 한다.
    • 로직이 들어가서 chage 처럼 이름을 바꿔 넣으면 뭔가 중요하게 뭔가를 하는구나를 알 수 있어 권장한다고 한다.
      • 정식으로 사용하기 위해서는 더 복잡하다.
      • Null 체크 등..
      • 하지만 그렇게 깊게 사용할 일이 자주 없어서 평소에는 이렇게 간단하게 편의 메소드를 사용하면 된다.
      • 더 깊은 내용은 너무 복잡해서 JPA 책을 참고하면 된다. 보통 실무에서 이정도 사용해도 무리는 없다.
    public void addMember(Member member) {
            member.setTeam(this);
            members.add(member);
        }
    
    • 연관관계 편의 메소드를 반대로 할 수 도 있다.
      • 기준이 반대, Team 기준, Member 기준 이렇게 양쪽 다 사용이 가능하다.
        • 주의 점은 아래서 다룬다.
    team.addMember(member);
    
    • 이것을 대신 넣어 사용할 수 있다.
    • 대신 양쪽 다 연관관계 편의 메소드를 설정할 시 문제가 되므로 메소드 하나를 설정했으면 반대쪽 메소드는 삭제해야 한다.
    • 양방향 매핑 시 무한 루프도 조심해야 한다.
      • toString(), lombok, JSON 생성 라이브러리를 조심해야 한다.
        • 양쪽으로 toString()을 무한 생성하게 될 수도 있다.
          • StackOverflowError가 뜰 수 있다.
      • lombok에서는 toString()을 만들지 말자.
      • JSON 생성 라이브러리를 잘 알고 사용하면 된다.
        • Controller에는 Entity를 절대 반환하면 안 된다.
        • JSON으로 주로 통신해서 좋다고 사용하는데 무한루프가 생길 수 있고 Entity를 API에 반환해 버리면 Entity를 변경하는 순간 API 스펙이 바껴버린다. 때문에 문제가 생길 수 있다.
        • Entity를 DTO로 변환해서 반환하는 것을 추천한다. 그렇게 되면 JSON 생성 라이브러리를 통해 오류가 생기는 경우가 줄어들 것이다.
  • public void setTeam(Team team) { this.team = team; }
  • 단방향 매핑으로도 연관관계 매핑은 완료다.
    • 처음 설계에서는 단방향 매핑으로 끝내야한다.
    • 연관 테이블이 매우 많아도 JPA 사용하고 설계에 들어가면 일단 단방향 매핑으로 설계를 완료해야 한다.
    • 객체만으로 설계를 할 수는 없다. 테이블 설계와 동시에 들어가야 하는데 테이블 관계에 대한 FK가 어느 정도 구상되면 단방향으로 끝내야 한다.
    • 단방향을 잘 하고 양방향은 필요에 따라 추가하면 된다.
  • 비즈니스 로직을 기준으로 연관관계의 주인을 선택하지 말고 외래키를 기준으로 해야한다.
    • 성능이나 운영 관점에서 볼 때 이렇게 하는 게 이점이 많다. 그리고 헷갈릴 게 없다.
    • 외래키가 들어간다? → 너 Owner 이렇게 간단하게 정해주면 된다.
  • 양방향 연관관계가 형성되면?
    • 연관관계 편의 메소드를 사용한다.

자바 ORM 표준 JPA 프로그래밍 - 기본편을 참고하여 공부하였습니다.

반응형

'Data Base > JPA' 카테고리의 다른 글

[JPA] : 다대일, N:1  (0) 2022.04.11
[JPA] : 연관관계 매핑 시작하기  (0) 2022.04.09
[JPA] : 양방향 연관관계, 연관관계 주인(Owner)  (0) 2022.04.08
[JPA] : 연관관계 매핑 기초  (0) 2022.04.06
[JPA] : 기본 매핑하기  (0) 2022.04.06

댓글