• 카테고리

    질문 & 답변
  • 세부 분야

    백엔드

  • 해결 여부

    미해결

연관관계 편의메서드 .

21.06.30 14:12 작성 조회수 320

2

안녕하세요 JPA 활용 1편을보고 넘어왔습니다..

1)질문

그렇나 현재 아직 이해못한점이

연관관계 편의메서드를 사용하면 어느면서에서

이점이 발생하는지 잘모르겠습니다..

양방향관계시 연관관계메서드 에서

public void setTeam(Team team){
this.team = team;
team.getMembers().add(this);
}

team.getMembers().add(this);

이부분이 team 엔티티에서도 Member의 값을 가질수있도록 하는것으로 보여집니다만

team.getMembers().add(this); 이부분이없어도

 Team team = em.find(Team.class, teamA.getId());
            System.out.println("연관관계"+team.getMembers());

team.getMembers() 를해도 값이 잘 보여집니다..

어느면에서 편의가 발생하는지 감이잘 안잡힙니다 ㅠ..

2)질문

일대 다 관계에서 페치조인 쿼리로 페이징을하면 데이터 뻥튀기 때문에 위험하다고 하셨습니다.

그래서 직접 확인을해보고있는데욥

 

           Team teamA = new Team();
            teamA.setName("팀A");
            em.persist(teamA);

            Member member1 = new Member();
            member1.setUsername("회원1");
            member1.setAge(10);
            member1.setTeam(teamA);
            em.persist(member1);

            Member member7 = new Member();
            member7.setUsername("회원7");
            member7.setAge(10);
            member7.setTeam(teamA);
            em.persist(member7);

  
            String query = "select t from Team t join fetch t.members";
            List<Team> result = em.createQuery(query,Team.class)
                    .setFirstResult(0)
                    .setMaxResults(1)
                    .getResultList();

            for (Team team : result) {
                System.out.println("팀이름 = " + team.getName());
                for (Member member  : team.getMembers()){
                    System.out.println("------------> member = " + member);
                }
            }

결과

딱이렇게 나왔는데 이것이 뻥튀기가 된결과인가요?

어느부분에서 위험한것인지 잘 모르겠습니다 ..

(제생각으로는 0번인덱스 페이지에 팀의값 1개가 잘 출력됫다고 생각되거든요 아니라면 팀에 맴버의 값을 1개만 출력해야하는데 두개가 출력이되어서 문제인건가요?)

답변 2

·

답변을 작성해보세요.

3

임세준님의 프로필

임세준

2021.07.01

2. 일대다에서 fetch join 을 하면 데이터가 뻥튀기되는 이유는 fetch join 때문이 아니고 SQL 문 실행 자체가 그렇게 되기 때문입니다.

위의 fetch join 는 결국 inner join SQL문을 실행시키고 다음과 같은 결과물을 뽑습니다.

(TeamA의 ID는 1, member의 id는 각각 1, 2라 가정)

TEAM_ID, MEMBER_ID

1, 1

1, 2

즉 team은 하나밖에 없는데 멤버수만큼 row수가 뻥튀기되서 결과물이 나오게 됩니다.

일반적인 페이징 방식으로 첫 번째 레코드만 뽑는다고 하면, 위의 결과물에 첫번째 레코드만 반환을 하게 되는데 그렇게 되면 teamA에 멤버1밖에 딸려나오지 않습니다. 사용자의 의도와는 다른 결과물이 나오게 되지요. 따라서 DB단계에서의 최적화를 기대할 수 없기 때문에 DB에서 모든 데이터를 다 조회하고 메모리에서 추려내는 수밖에 없게 됩니다.

위에서는 멤버가 둘 밖에 없어서 괜찮았지만 멤버가 1000만명이었다면, 첫 1개만 뽑고싶어도 DB에서는 1000만개의 레코드를 전부 조회해서 가져온다음에 메모리 상에서 첫 1개만 추려내게 되기 때문에 의도와는 다르게 굉장히 무거운 작업이 되어버립니다.

결과적으로 말하자면, 위험하다는 기준이 의도와는 다른 잘못된 데이터를 가져올까봐 위험하다는것이 아닌 내 의도와는 다른 매우 무거운 작업이 될 확률이 매우 농후하다는 점에서 위험한 코드가 되겠습니다

2

임세준님의 프로필

임세준

2021.07.01

1. 당연히 member에 team을 연결하고 나면, 추후 em.find()로 team을 가져왔을 시 team.getMembers()에 잘 반영이 되게 됩니다.

강의에서 설명하고자 했던 부분은 한 트랜잭션이 끝나기 전에는, 논리적으로는 member에 team이 들어가면 자동적으로 team에 member도 추가되어야 정상이지만 그런 효과를 단순 setter로만 누릴수는 없기 때문에 매번 member.setTeam(team), team.addMember(member) 하는게 귀찮다면 두 개 기능을 합친 메서드를 만들면 편하다는 얘기를 하신 것 같습니다