TIL(Today I Learned)

JPA(Java Persistence API)의 이해 2

Happy._. 2024. 5. 10. 23:08

JPA의 Entity Manager

  • 영속성 컨텍스트(Persistence Context)를 두어 Entity를 관리하는 주체
  • @PersistenceContext 어노테이션을 통해 주입하거나 생성자 주입을 했을 시 자동적으로 주입 됨
import org.springframework.stereotype.Service
import jakarta.persistence.EntityManager
import jakarta.persistence.PersistenceContext

@Service
class MyService {

    @PersistenceContext
    private lateinit var entityManager: EntityManager

}
  • EntityManager는 여러 종류의 함수를 지원하여, Entity의 상태를 변경

  • persist() : Entity를 영속성 컨텍스트에 추가
  • merge() : Detached 상태의 Entity를 다시 영속성 컨텍스트에 추가, 변경이 감지되도록 함
  • remove() : Entity를 삭제
  • detach() : 영속성 컨텍스트에서 Entity를 분리, Detached 상태로 만듦
  • clear() : 영속성 컨텍스트를 초기화, 모든 Entity를 Detached 상태로 만듦
  • close() : EntityManager를 종료
  • flush() : 영속성 컨텍스트 내 변경된 Entity를 감지, DB와 동기화
  • find() : 주어진 Entity의 PK를 이용, Entity를 DB에서 조회, 영속성 컨텍스트에 추가

Spring Data JPA의 Repository

Spring Data JPA는 다음과 같은 Repository 인터페이스를 제공

 

  • CrudRepository는 기본적인 DB 대상 CRUD를 위한 method를 제공
  • PagingAndSortingRepository는 CrudRepository를 상속받아 기본 CRUD에 더해 페이징 및 정렬 기능 제공
  • JPARepository는 PagingAndSortingRepository를 상속받아 부가적인 기능을 추가로 제공

JPA 인터페이스 정의

interface PostRepository: JpaRepository<Post, Long> {}

 

Entity 작성 전 설정

  • Entity는 Entity Manager를 통해 상태가 변경되기도 하고 자체적으로 도메인의 요구사항을 갖기도 하므로 Data Class 보다 일반 Class가 적합
  • Hibernate에서는 지연로딩을 위한 Proxy를 설정하기 위해 Entity 클래스를 상속받아 사용
  • Kotlin에서 기본적으로 class는 Java로 변환시 final(immutable 형태)로 컴파일 되므로 open class 형태로 선언해야 하며 NoArgsConstructor가 필요
  • 플러그인(allopen, noarg)을 사용하면 위 설정(open class, NoArgsConstructor)을 직접하지 않아도 됨
  • allopen의 경우 plugin.spring에 포함되어 있음
plugins {
    kotlin("plugin.noarg") version "1.8.22"
}
  • Entity 관련 클래스들에 allopen, noarg 적용
noArg {
    annotation("jakarta.persistence.Entity")
    annotation("jakarta.persistence.MappedSuperclass")
    annotation("jakarta.persistence.Embeddable")
}

allOpen {
    annotation("jakarta.persistence.Entity")
    annotation("jakarta.persistence.MappedSuperclass")
    annotation("jakarta.persistence.Embeddable")
}
  • MappedSuperclass : 객체입장에서 상속이 필요할 때 사용
  • Embeddable : Table로 인식되진 않지만 하나의 객체로 표현, Entity 내부에서 사용하고 싶을 때 사용하는 어노테이션으로 VO(Value Object) 같은 형태, 이를 사용하는 측에 @Embedded를 표시

Entity 간 관계 연결

  • 관계는 양방향과 단방향이 존재
  • DB 내 테이블은 기본적으로 양방향 관계, JOIN을 통해 두 테이블을 참조할 수 있기 때문
  • 관계 설정은 @OneToMay, @ManyToOne, @OneToOne으로 설정(@ManyToMany도 있지만 사용 X)
  • M:N 관계는 테이블 관점에서 1:N 관계와 N:1 관계로 이루어진 세 테이블과 동일
  • JPA를 통해서 직접 테이블 생성 시 @ManyToMany를 사용할 수 있지만 중간의 N에 해당하는 테이블은 객체 상으로 표현되지 않기 때문에 의도치 않은 쿼리가 만들어질 수 있음(Mapping 정보만 넣을 수 있음, 추가 정보 X)
@OneToMany(mappedBy = "연관관계의 주인이 관계를 참조하는데 사용하는 멤버변수명")
  • mappedBy : 연관관계의 소유 주체가 누구인지를 JPA에 알려줌
  • 단방향 관계일 때는 필요 X, 양방향 관계일 때는 필요
  • JPA는 서로 관계가 있다는 것은 알지만 FK를 누가 소유하고 있는지 모르기 때문
  • 1:N 관계에서 보통 N쪽이 연관관계의 주인으로 1쪽에 mappedBy를 설정을 하여 연관관계의 주인을 JPA에 알려줌

@JoinColumn : 명시적으로 외래키가 무엇인지 JPA에 알려줌(JPA로 테이블을 직접 생성 해주는 경우 필요 X)