본문 바로가기

🤔Troubleshooting

JPA 양방향 맵핑에서 주인이 정상적으로 동작하지 않는 이슈

🧐JPA 양방향 맵핑에서 주인이 정상적으로 동작하지 않는 이슈

jpa 공부를 하면서 양방향 맵핑을 간단하게 구현하고 테스트 해보았습니다.

👉👈양방향 맵핑 구현

Member.java

@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PUBLIC)
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;

    @Builder
    public Member(String name, Team team) {
        this.name = name;
        this.team = team;
    }
}

Team.java

@Getter
@Setter
@NoArgsConstructor
@Entity
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    public Team(String name) {
        this.name = name;
    }

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

Member는 Team을 가지고 있고, Team은 List를 가지고 있습니다.

그리고 둘의 관계에서 'mappedBy'가 달려있지 않은 Member가 관계의 주인입니다.

즉, 주인인 Member(A 멤버라고 하겠습니다)에 team을 셋팅해주면 따로 team에 member를 add해주고 save를 하지 않아도 db로부터 해당 team을 불러왔을 때 그 팀의 list 안에는 A 멤버가 포함되어 있습니다.

해당 내용을 테스트 코드로 구현해보았습니다.

🕹Test코드

@RunWith(SpringRunner.class)
@SpringBootTest
public class MemberRepositorySupportTest {
    @Autowired
    private MemberRepository memberRepository;

    @Autowired
    private TeamRepository teamRepository;

    @Test
    @Transactional
    public void 양방향맵핑테스트() {

          Team team = new Team("team");
        teamRepository.save(team);

        Member member1 = Member.builder()
                .name("member1")
                .team(team)
                .build();
        Member member2 = Member.builder()
                .name("member2")
                .team(team)
                .build();

        memberRepository.save(member1);
        memberRepository.save(member2);

        Team findTeam = teamRepository.findById(team.getId()).get();
        assertThat(findTeam.getMembers()).hasSize(2);
    }
}

하지만 어쩐지 실패하는 모습입니다.

💡원인 및 해결

원인은 영속성 컨텍스트의 1차 캐시였습니다.

teamRepository.findById 를 할때 1차 캐시에 있는'멤머가 없는팀'을 가져오는 것이었습니다.

그래서 캐시를 제거하기 위해 EntityManager를 만들고 clear메서드 호출을 통해 캐시를 지워보았습니다.

@RunWith(SpringRunner.class)
@SpringBootTest
public class MemberRepositorySupportTest {
    @Autowired
    private MemberRepository memberRepository;

    @Autowired
    private TeamRepository teamRepository;

    //추가된 부분
    @Autowired
    private EntityManager entityManager;

    @Test
    @Transactional
    public void 양방향맵핑테스트() {
        Team team = new Team("team");
        teamRepository.save(team);

        Member member1 = Member.builder()
                .name("member1")
                .team(team)
                .build();
        Member member2 = Member.builder()
                .name("member2")
                .team(team)
                .build();
        memberRepository.save(member1);
        memberRepository.save(member2);
          //추가된 부분
        entityManager.clear();

        Team findTeam = teamRepository.findById(team.getId()).get();
        assertThat(findTeam.getMembers()).hasSize(2);
    }
}

결과는 테스트 통과!

끗~

반응형