P4-1] Ch 06. 연관 관계

uuuu.jini·2022년 1월 26일
0
post-thumbnail

github 링크

목차

  1. 연관관계 개요, ERD 알아보기
  2. 1대1
  3. 1대N
  4. N대1
  5. N대N

1. 연관 관계, ERD

RDB는 관계형 데이터베이스이다. 관계형 데이터베이스는 관계와 조인을 통해 집합연산과 관계연산을 할 수 있다. DB생성시 relation을 생각하고 개발한다. 각 테이블이 연관성을 가지고 있다는 정보를 가지고 키를 공유한다. 수정,변경시에 용이하고 여러번 값을 작성해야 하는 불편함이 없어진다. 관계형 DB는 데이터 저장으로 대표적인 방법이다.

ERD 는 entity relation diagram이다. Entity는 양방향으로 참조하는 경우가 있다. 잘못하면 entity관계가 복잡해지고 잠재적 오류 발생할 수 있다. ERD로 개략적인 entity의 구성을 그려보는 것이 좋다. 우리가 만들어야 할 entity에 대한 구성도를 그려볼 수 있다. draw.io프로그램을 사용한다.

서로 분리된 테이블을 만들고 관계를 맺어야 관리하기가 편리할 것이다.


2. 1대 1 관계

현업에서 많이 활용된다. 책의 리뷰의 평균을 계산하기 위한 테이블을 생성하고 book과 1대1 연관관계를 맺게 작성하였다.

새로 만든 bookReviewInfo entity class의 예시이다. [ entity는 기본 생성자가 필요하며, 상속받은 객체의 필드를 컬럼으로 받아오기 위해서는 callspuer속성을 true로 변경해주어야 한다. ]

이전의 코드를 주석으로 처리 하여 남겨두는 것은 클린코드에 위배된다고 하였으며, 깃으로 버전을 관리하므로 이전 코드를 주석처리해놓은 것을 모두 삭제하는 것이 좋다고 하셧다.

@GenerateValue(strategy = GenerationType.IDENTITY) : 독립적으로 테이블 별 값을 생성하여 준다. 사용할 경우 hibernate_sequence 는 삭제해야 한다. 이 값은 DB에 저장시 id값을 개별적으로 생성하여 주므로 DB에 저장하기 이전인 경우에는 null값을 가지고 있을 것이다.

@OneToOne 어노테이션

1대1을 연관관계를 맺은 엔티티의 PK를 자동으로 받아와 외래키로 사용하게 해주는 어노테이션이다. 내가 관계를 맺을 필드위에 작성하며 해당 필드의 get 메소드를 통해 1대1연관을 맺은 엔티티를 가져올 수 있다.

  • optional = true : left outer 조인이 일어난다. optional 값의 defulat 가 true이기 때문에, 이 값이 존재할 수 도 있고 안할수도 있으므로 outer 조인으로 처리하게 된다. false일경우 해당 필드의 null을 허용하지 않겠다는 의미가 된다. 이렇게 false처리를 할경우 inner조인으로 변경이 된다.

  • mappedBy속성 : 연관키를 해당 테이블에서 더이상 가지지 않게 된다. entity relation 사용시 순환참조가 발생하게 된다. 특별히 필요한 경우가 아니라면, 단방향으로 걸거나, toString에서 제외하는 처리가 필요하다. @ToString.Exclude 작성하여 준다. 이렇게 하면 다른 entity에서도 연관관계가 맺어진 entity를 가져올수 있게 된다.


3. 1 대 N 관계

User와 UserHistory

user는 회원 정보를 저장하며 userHistory 는 수정정보를 저장하는 테이블이다. user_history 는 user의 id를 필드로 가지고 있다.

1대N관계에서 1측인 User 엔티티에서 관계를 맺어 가져올 것은 N개의 entity collection 이다.( List를 사용하였다. ) 해당 필드에는 @OneToMany 어노테이션과 @JoinColumn어노테이션을 붙여준다.

  • @OneToMany 어노테이션: 1대N관계의 엔티티들을 자동으로 매핑해준다.
  • @JoinColumn 어노테이션: 1측의 관계를 맺을 필드와 조인할 N측의 필드의 이름을 name 속성으로 작성하여 준다. 즉 어떤 컬럼으로 조인을 할지 지정해주는 어노테이션이다. insertable옵션과 updateable옵션이 있으며 각각 insert와 update 가능 여부를 지정해주는 속성이다. ( User 엔티티에서는 UserHistory 엔티티를 변경하면 안되므로 모두 false로 지정해주었다. )
 	@OneToMany(fetch = FetchType.EAGER)
    	@JoinColumn(name = "user_id",insertable = false,updatable = false)
    	private List<UserHistory> userHistories = new ArrayList<>(); 

N측인 UserHistory 엔티티에서는 관계를 맺은 필드(외래키)에 @Column(nam=?) 으로 해당 @JoinColumn의 name속성으로 작성한 이름과 동일하게 맞추어 준다.

@Column(name="user_id")
private Long userId;

이렇게 관계를 맺어주면 userRepository의 get 메소드를 이용하여 user를 가져온뒤 관계를 맺고있는 userHistory 의 정보를 바로 불러올수 있다.(기존에는 해당 user의 id를 사용하여 userHistoryRepository 를 이용하여 id에 맞는 userHistory 의 정보를 받아와야 했다. )

List<UserHistory> result = userRepository.getByemail("yoojin@google.com").getUserHistories();
// yoojin@google.com이라는 이메일을 가진 user의 history 를 가져온다. 

4. N 대 1 관계

User 와 UserHistory

History 의 값을 가지고 현재 user의 값을 조회하는 것은 드물다. 그래서 User와 UserHistory에 대한 내용은 1대N으로 사용하였다. 반대로 User에서 userHistoryId를 가져야 한다면 값이 1,2,3,4,5,6... 이런식으로 어떤 값의 배열의 형태를 가져야 한다. 그래서 OneToMany에서 참조하는 값은 1측에 해당하는 PK ID를 N측에서 foreign key로 가지게 된다. ( user(1) - userHistory(N) : user의 id를 userhistory 에서 foriegn key 로 가진다.) 일반적인 상황에서는 @ManyToOne이 더욱 깔끔하다. 해당 엔티티가 필요로하는 foreign key 값을 엔티티가 함께 가지고 있기 때문이다. ( 외래키는 N이 가진다. 이때 1대N관계에서는 조인할 외래키를 따로 설정하여 줫지만 N대1관계에서는 엔티티가 외래키를 가지고 있다. )

Foreign key 를 가지는 쪽: 1대N관계라면 N측이 가져야한다. 1쪽에서 N개의 데이터를 수용할수 없기 때문이다.
@ToString.Exclude 를 붙인 필드는 ToString에서 제외된다.

어느 엔티티에서 연관엔티티가 필요한지를 생각해보고 연결 방법을 정한다. User에서 userHistory를 조회하는 것이 많이 발생하므로 user에서 @OneToMany를 사용하여 관계를 맺어주는것이 훨씬 나은 방법이다. (만약 userHistory에서 user를 조회하고 싶은 경우 userHistory 에서 @ManyToOne을 사용한다. )

Book과 Publisher

여러개의 책과 하나의 출판사의 관계이다. Book은 @ManyToOne을 관계를 맺는 필드에 붙여주고 Publisher는 @OneToMany@JoinColumn(name=?) 을 관계를 맺는 필드에 붙여준다.

Review와 Book

여러개의 리뷰와 하나의 책의 관계이다. Review 는 @ManyToOne, Book은 @OneToMany와 @JoinColumn(name=) 을 붙여준다.

Review와 User

여러개의 리뷰와 하나의 사용자의 관계다. 위와 동일하다.


5. N대N 관계

Book과 Author 의 관계이다. 현업에서 많이 사용하지 않는다. 어떤 작가는 여러 책을 쓰고 어떤 책은 공동저자가 존재 할 수 있다. Many to Many 의 관계이다.

1쪽의 pk를 many쪽에서 가지고 있는다. many to many 이면 외래키를 구하기 어렵다. 저장할수있는 단일값이 없다. many to many인 경우에는 중간 테이블을 구성하여 각 pk를 매핑 하게 된다. 1대N에서는 중간테이블을 제거하였지만 n대n에서는 제거할수 없다.

@ManyToMany 를 사용하여 관계를 맺어준다. 중간테이블이 생기며 이를 제거할수 없다. 현업에서는 이런 매핑을 거의 다루지 않는다. 꼭 필요한 경우에만 사용한다. 피해가려고 설계를 한다.

ECommerce 의 관계

  • User 와 Product 는 N대N 관계이다. 중간 테이블로 user_product가 생성이 될것이며 두 엔티티의 Primary Key를 모두 가지게 된다. 이 중간테이블(또다른 엔티티)로 order를 만들어서 1대N N대1의 형식으로 설계하게 된다. 매핑하는 테이블을 사용하여 many to many 형식의 관계를 제거하도록 설계를 하는 방식이 주로 사용된다.

Author 와 Book 의 1대N,N대1 변경

별도의 네이밍을 통해 중간 매핑 도메인을 만들긴 어렵다. 네이밍을 신중하게 결정해야겠지만 지금은 예제로서 간단하게 작성해보았다. 1대N과 N대1의 위의 작성과정과 동일하게 작성한다고 생각하면 된다. 중간테이블을 내가 만드는 것이다.

profile
멋쟁이 토마토

0개의 댓글