양방향 연관관계와 연관관계의 주인2 - 주의점, 정리

JPA 기준으로 보면 주인 쪽에서만 데이터 수정해주면 되지만 실제 코드에서는 양쪽 다 수정을 해주는 것이 좋다.

⭐️지연로딩 : Member.setTeam(team); 으로 주인 쪽에서만 데이터 넣어줘도 JPA에 의해서 Team의 getMembers로 리스트 조회하면 이 리스트에도 데이터가 들어가있다!

문제점

  1. 영속성 컨텍스트 1차캐시에 있던 값이 없는 team 객체를 가져온다.

    member.setTeam(team);//1.주인이 왜래키 등록
    em.persist(member);
    
    //team.getMembers().add(member);//2.가짜 주인도 등록해줘야함!
    
    Team findnTeam = em.find(Team.class,team.getId());
    List<Member> members = findTeam.getMembers();
  2. 테스트케이스 작성시에도 JPA 없이 하기 때문에 주인 쪽에만 데이터를 넣으면 데이터 불일치성이 발생한다!

양방향 관계는 양쪽 다 값을 셋팅해줘야한다!

실제로 1번과 2번을 함께 유지해줘야하는데 떨어져있으면 깜빡하기 쉽다! =>연관관계 편의 메서드를 생성하자! 주인 쪽에서 FK를 수정하는 setter 부분에 가짜 주인의 해당 필드를 수정하는 것을 추가한다! =>원자적으로 하나만 수정해도 둘 다 동시에 수정할 수 있다! =>연관관계 편의 메서드나 JPA 상태를 변경할 때, 로직이 들어갈 때는 단순 setter를 사용하지 않고 의미에 맞게 메서드이름을 변경해준다!!! 그러면 단순히 값을 셋팅하는 것이 아니라 뭔가 의미있는 동작을 한다는 것을 명시적으로 알 수 있다!

=>Member를 기준으로 수정할지, Team을 기준으로 수정할지 둘 중 하나만 선택할 수 있다. =>1 에 할지 에 할지는 상황마다 다르다.

  1. Member 클래스 기준으로 수정 : setTeam이 아니라 changeTeam으로 이름 바꾸고, team도 현재의 member로 변경

public void changeTeam(Team team) {
    this.team = team;//팀을 셋팅하고
    team.getMembers().add(this);//현재 회원을 팀 members에도 추가!
}

2. Team 클래스 기준으로 수정 : team.addMember(member1);

public void addMember(Member member){
        member.changeTeam(this);
        members.add(member);
    }

양방향 매핑 시 무한 루프 조심하자!=>연관 관계 편의 메서드를 양쪽 다 실행함으로써 나오는 문제인가?

  1. lombok의 toString() 사용❌

  2. 절대 컨트롤러가 엔티티를 직접 반환❌

이유

  1. toString은 현재 클래스의 모든 필드들을 String으로 출력한다. Member = id, username, Team : Team의 toString 호출 Team = id, name, List<Member> : Member 컬렉션의 Member 하나하나 모두 toString 호출하고, 각 Member 내부의 toString은 다시 Team.toString()을 호출 결국 양쪽 번갈아 무한 호출&실행하는 순환참조가 일어나 스택오버플로우가 발생!

  2. 컨트롤러에서 엔티티를 response로 직접 보낼 때 엔티티가 가진 양방향 관계일 때 엔티티를 JSON으로 바꾸는 순간 발생 컨트롤러에서 엔티티반환할 때 스프링에서 엔티티를 JSON으로 뽑는다. JSON으로 API 반환해버리면 무한루프 발생 가능, 엔티티는 변경가능한데 엔티티가 변경되면 그 API 스펙이 바뀌어버린다! 그러면 API 받는 입장에서 API 변경되면 황당. 그래서 엔티티는 값만 있는 "DTO" 로 만들어서 전송한다!

  1. em.flush() : 영속성 컨텍스트를 비우지 않고, 영속성 컨텍스트의 변경내용을 DB에 동기화 em.clear(): 영속성 컨텍스트 초기화! em.flush() + em.clear() : 영속성 컨텍스트(1차 캐시)에 아무것도 없기 때문에 DB에 다시 조회해야함! =>영속성 컨텍스트를 DB에 반영하고, 영속성 컨텍스트를 초기화하면 JPA는 DB에서 데이터를 조회하게 되고, 내부 매커니즘(지연 로딩)으로 Team의 members 리스트에도 데이터가 들어가있다! (FK값이 변경됐다는 것을 알고 members를 조회한다)

Last updated