ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA Entity 연관관계 구현하기(양방향 연관관계 List Empty 문제 해결)
    프로젝트/WebRTC 화상통화 프로젝트 2022. 7. 29. 00:04
    728x90

    개요

    JPA의 다양한 연관관계를 구현해보려고 합니다.

    1: N, 1:1, N:M을 다뤄보겠습니다.

    어느 정도 JPA에 대한 지식이 있다는 전제하에 진행됩니다.

     

    N : M 연관관계 구현하기(회원 - 그룹)

    현재 로그인기능만 구현한 상황이기 때문에 Member Entity만 존재합니다.

     

    회원은 여러개의 그룹에 가입할 수 있습니다. (1 : N)

    그룹은 여러명의 회원을 가질 수 있습니다. (1 : N)

     

    두 엔티티가 모두 1:N 관계를 가지기 때문에 N:M 연관관계입니다.

     

    @ManyToMany라는 어노테이션이 존재하지만 중간 테이블을 묵시적으로 생성해주기 때문에 필요한 추가 칼럼을 사용할 수 없는 문제가 발생합니다.

     

    Member_Group이라는 중간 테이블을 두어 N:M을 1:N, N:1으로 풀어내고자 합니다.

     

    위의 ERD와 필드는 조금씩 다를 수 있으나 전반적인 관계와 흐름은 동일합니다.

     

    또한 연관관계에 중점을 두기위해 최소한의 Entity를 보여드리고자 합니다.

     

     

    주의! Group은 MySQl의 GROUP BY 예약어가 잡혀있어서 Team으로 대체하였습니다.

     

    Member Entity 코드

    @Entity
    public class Member {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "member_id")
        private Long id;
    
        @Column(name = "name", nullable = false)
        private String name;
    }

     

    Team Entity 코드

    @Entity
    public class Team {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "team_id")
        Long id;
    
        @Column(name = "name")
        String name;
    }

     

    Registration Entity 코드(MemberTeam을 의미 있는 이름으로 빼기 위해 Registration으로 사용했습니다.)

    @Entity
    public class Registration {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = registration_id")
        Long id;
    
        @ManyToOne
        @JoinColumn(name = "member_id")
        private Member member;
    
        @ManyToOne
        @JoinColumn(name = "team_id")
        private Team team;
    }

    @ManyToOne을 사용하여 N:1 연관관계를 설정했습니다.

    또한 일차적으로 단방향 연관관계로 설정하고 추후에 필요하다면 양방향으로 바꾸려고 합니다.

     

    나중에 양방향을 사용하더라도 1:N 연관관계에서 외래키는 항상 DB의 N 쪽에 생기기 때문에 @ManyToOne을 사용하여 N:1으로 연관관계의 주인으로 사용합니다.

     

    @Enumrated는 EnumType.String으로 지정했습니다.

     

    이렇게 하면 단방향 연관관계는 끝입니다.


    양방향 연관관계

    비즈니스 로직상 팀이 가지는 회원의 수가 필요했습니다.

     

    하지만 위의 Entity에서 볼 수 있듯이 Team은 Member로 바로 참조할 수 없습니다.

     

    하지만 중간 테이블을 통하여 group_id로 조인하면 팀이 가지는 회원의 수를 알아낼 수 있습니다.

     

    하지만 이미 로직을 실행하는 도중 group_id를 조인하는 경우는 비효율적이라고 판단했습니다.

     

    group에서 바로 registraionList를 가지고 size를 호출하면 팀이 가지는 회원의 수를 알아낼 수 있습니다.

     

    즉, 특별한 조회를 위해 양방향 연관관계를 세팅하고자 했습니다.

     

    Team Entity에  @OneToMany를 통해 양방향으로 연결하고 연관관계의 주인을 Registration Entity의 team으로 설정하면 끝입니다.

    @OneToMany(mappedBy = "team",fetch = FetchType.LAZY)
    List<Registration> registrationList = new ArrayList<>();

    발생할 수 있는 문제

    이렇게 세팅해두면 조회할때 자동으로 registrationList에 팀에 소속된 중간 테이블의 정보를 알 수 있을 것이라고 예상했습니다.

     

    하지만 실제로 테스트했을때는 registrationList에 아무 값도 들어가지 않고 비어있는 상황이 연출되었습니다.

     

    원인

    우리가 Entity를 저장할때 실제로 registrationList에는 넣어주는 작업을 수행하지 않아도 외래 키를 가진 중간 테이블(Registration 엔티티)만 업데이트되면 Member와 Team 테이블에는 자동으로 반영됩니다.

     

    영속성 컨텍스트가 flush and clear 된 이후에 값을 조회한다면 registraionList에도 적절한 값들이 들어가겠지만 영속성 콘텍스트가 비워지지 않은 상황에서 Entity를 저장하고 Team의 registraionList를 조회한다면 아무 값도 얻을 수 없습니다.

     

    1차 캐시의 역할을 하여 registrationList가 비어있는 객체를 계속 조회하는 이유입니다.

     

    해결법

    1. 연관관계 편의 메서드 작성

    2. Entity를 저장할 때 양방향으로 추가해주기

     

     

     

    댓글

Designed by Tistory.