Jpa 연관관계

Woozy9ucci·2023년 12월 17일

Java와 DB는 패러다임이 다르다.
때문에 우리는 RDB의 테이블을 객체 형태로 다루기 위해 JPA(java 진영 표준 ORM)를 통해 테이블과 Entity를 매핑하여 사용한다.
즉, DB의 데이터를 객체지향적으로 다룰 수 있게 되는 것이다.

테이블 간의 연관관계를 객체에서는 어떻게 해석 할 지 알아보자

POST 테이블과 COMMENT 테이블이 1:N 으로 연관관계를 가지고 있다고 해보자
이 때 COMMENT 테이블이 POST_ID를 FK컬럼으로 가지고 join을 통해 서로를 참조할 수 있다

객체의 입장에서 바라보면 어떨까?
Comment가 필드로 PostId를 가지고 있으면 되지않을까?
해당 Comment의 Post를 조회 할 때 postId를 가지고 Post를 조회할 수 있다.
이는 데이터 지향적이지 객체지향적이라고 볼 수 없다
Comment가 필드로 Post를 가지고 참조하여 무언가를 할 수있도록 하기위해 우리는 맵핑을 해줘야한다.

@JoinColumn

  • 외래키와 객체를 맵핑
    Comment가 참조하고 있는 Post(객체)를 테이블 상에서 POST_ID(외래키)로 인식 할 수있도록 @JoinColumn 어노테이션을 사용하여 Comment.post와 COMMENT.POST_ID 를 맵핑 할 수 있다.

  • 일반적이지는 않지만 OneToMany에서 단방향 맵핑을 할 경우 @JoinColumn 어노테이션을 사용하지 않는다면 맵핑테이블이 하나 더 생성된다.(OneToMany 단방향 에서 JoinColumn을 생략하면 JoinTable이 우선)
    (그 반대는 JoinColumn 생략하면 아래에서 서술하는 referenced.. 기본설정으로 맵핑)
    또한 One쪽에서 @JoinColumn을 사용하더라도 외래키는 Many쪽에 생기게 된다.
    OneToMany 단방향설정을 하기 보다는 양방향 관계를 설정하자.
    (mappedBy를 통해 주인 설정 해주지 않으면 맵핑테이블이 추가생성된다)
    (주인은 외래키를 가지고 있는 쪽으로 정해주자)
    JPA - One To Many 단방향의 문제점 - https://dublin-java.tistory.com/51

  • OneToOne 에서 JoinColumn을 생략할 시 양방향일 때는 모두 fk를 갖게 되고 단방향일 때는 생략해도 무방하겠다. 양방향 일 때는 mappedBy를 통해 주인을 정해주자

  • 주요 속성

    • name
      FK컬럼의 컬럼명 지정(post_id)
    • referencedCoulumnName
      해당 외래키가 대상 테이블의 어떤 컬럼을 참조하는 지를 지정
      (post의 pk 컬럼명)
    • 생략 시 deafault
      name = 필드 명
      referenscedCoulumnName = 참조하는 테이블의 기본 키 컬럼
      ManyToOne 단방향에서는 생략해도 되긴 함
      (https://hyeon9mak.github.io/omit-join-column-when-using-many-to-one/)

@OneToMany, ManyToOne, ...

@JoinColumn 만으로는 맵핑을 완료할 수 없다.
@Entity 클래스 안에서 필드로 객체를 선언하면 컴파일 에러가 발생한다.
객체 간 어떤 연관관계를 가지고 있는지 모르기 때문이다.

  • 객체 간 연관관계를 지정
    DB는 외래키 하나로 양쪽 테이블 조인이 가능하기 때문에 방향을 구분 할 필요가 없다. 따라서 우리는 객체의 연관관계의 방향을 설정해야한다.
  • 연관관계의 주인 지정(mappedBy)
    양방향 설정 시 JPA에게 연관관계의 주인을 알려줘야한다.
    (실제로 mappedBy 지정 안했을 때 양쪽 모두의 fk를 갖는 테이블을 추가 생성)

연관관계, 방향설정 너무나 자세히 설명되어있는 블로그
https://jeong-pro.tistory.com/231
https://ttl-blog.tistory.com/129#%F0%9F%90%B3%20mappedBy%EB%A5%BC%20%EC%82%AC%EC%9A%A9%ED%95%98%EC%A7%80%20%EC%95%8A%EC%9C%BC%EB%A9%B4...-2

  • 양방향 설정시 순수객체 입장에서는 양쪽 모두 값을 세팅해주는것이 좋음
    ex) Comment의 init블록에 post.commentList.add(this) 등등
	val post = postRepository.findByIdOrNull(postId) ?: throw CustomException("post", ErrorCode.MODEL_NOT_FOUND)
    val user = userRepository.findByIdOrNull(userPrincipal.id) ?: throw CustomException("user", ErrorCode.MODEL_NOT_FOUND)
    println(post.commentList.size)
    var comment = Comment(
        content = commentRequest.content,
        author = user.userName,
        user = user,
        post = post
    )
    println(post.commentList.size)  // 이런식으로 사용할 수 있으니
    commentRepository.save(comment)

0개의 댓글