ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • @ManyToMany, @OneToMany, @ManyToOne관계 작성하기
    Spring 2021. 9. 5. 21:33

     

     

    엔티티 매핑을 하다보면 엔티티와의 관계가 1:N, N:1이 아닌 N:N의 관계를 가진 경우가 존재한다. (ex. 팀-회원의 관계)

    이러한 경우 다:다 의 관계로 매핑되는데, 이럴경우 조회 쿼리, 외래키를 어디에 위치시키는 가에 대한 여부를 알아본다. 

     

     

    @ManyToMany 관계

    조인테이블에서 데이터정보의 추가와 부가기능추가는 불가능해지기 때문에, 다대다 관계는 권유되지 않는다. 

    실무에서는 일대다, 다대일-다대일, 일대다의 관계로 풀도록 가운데 엔티티를 추가해서 풀어주도록 한다.

     

     

    엔티티 설정

    유저와 팀의 엔티티에 다음과 같이 작성해준다. 멤버와 팀은 서로 다대다 관계이다. 외래키는 user가 가지고 있기 때문에 다대다 관계의 매핑정보를 수정하기 위해서는 user측에서 다음과 같은 addTeam의 함수를 이용해서 수정해주어야한다. cascade를 통해서 user의 정보가 수정되면 user-team테이블에서의 정보도 수정되도록 설정해준다. 

    @Entity
    @Table(name="user")
    @Getter
    @Setter
    public class User {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name="user_id")
        private Integer id;
        
    	@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
        @JoinTable(
                name = "member_team",
                joinColumns = @JoinColumn(name = "member_id"),
                inverseJoinColumns = @JoinColumn(name = "team_id")
        )
        private Set<Team> teams = new HashSet<>();
        
        public void addTeam(Team team){
            this.teams.add(team);
        }
    
    }

    team에서는 cascadeTypa.ALL을 통해서 team정보가 삭제, 수정등이 이루어지면 user-team관계에서도 변경된 정보가 반영되도록 설정한다. team이 삭제시에는 삭제된 team과 관계된 user-team의 관계도 해제된다. 여기서 mappedBy는 user엔티티에서 저장된 team의 이름에 매핑되도록 설정해준다. 

    @Entity
    @Table(name = "team")
    @Getter
    @Setter
    public class Team {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "team_id")
        private Integer id;
        
        
        @ManyToMany(mappedBy = "teams", cascade = CascadeType.ALL)
        private Set<User> users= new HashSet<>();
    }

     

    Repository 다대다 관계 조회시

     

    다대다 관계로 매핑된 정보의 아이디를 가져와서 정보를 조회할떄는 다음과 같이 join해서 가져오도록 한다. (JPQL)

        @Query(value = "SELECT DISTINCT t FROM Team t LEFT JOIN FETCH t.users u LEFT JOIN FETCH t.company c WHERE u.id=:id")
        List<Team> findByUserId(Integer id);

    user에서는 모든 유저를 가져올때 미리 join해서 매핑된 정보들까지 가져오도록 설정할 수 있다. 

        @Query(value = "SELECT u FROM User u INNER JOIN FETCH u.role r LEFT JOIN FETCH u.teams t LEFT JOIN FETCH t.company c")
        List<User> findAllUsers();

     

     

    @ManyToOne, @OneToMany 관계

    @ManyToOne과 @OneToOne의 관계는(-One) fetch타입이 eager로 되어있기 때문에 N+1쿼리가 발생할수 있어서 모두 lazy로 변경해준다. 

    @ManyToOne, @OneToMany의 관계에서는 many쪽에 외래키를 두어서 정보변경이 용이할 수 있도록 해준다.

    데이터를 추가할 때는 (외래키 x)one->(외래키 o)many의 순서로 진행하고, 데이터를 삭제할때는 (외래키 o)many->(외래키 x)one의 순서로 진행해준다. 순서가 어긋나게 되면 ConstraintViolationException이 발생한다. 

     

    엔티티 설정

    메세지와 conversation(대화방)의 관계로 예시를 들어보자. 

    @Entity
    @Table(name = "message")
    @Getter
    @Setter
    public class Message extends BaseTimeEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "message_id")
        private Integer id;
    
       @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "conversation_id")
        private Conversation conversation;
        }
    }

    여기서 conversation은 one의 입장으로 외래키가 없으므로 mappedBy를 설정해주고 cascadeType을 이용해서 정보변경내용이 반영되도록 해준다. 여기서 conversation이 사라지게 되면 관련된 메세지들도 모두 삭제되어야 하므로 orphanRemoval을 설정해주었다. 

    @Entity
    @Table(name = "conversation")
    @Getter
    @Setter
    public class Conversation extends BaseTimeEntity {
    
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        @Column(name = "conversation_id")
        private Integer id;
    
        @OneToMany(mappedBy = "conversation" , cascade = CascadeType.ALL, orphanRemoval = true)
        private List<Message> messages = new ArrayList<>();
    
    }

     

Designed by Tistory.