관리 메뉴

KorSA

JPA 기본 정리 본문

Study Memos/JPA

JPA 기본 정리

Praiv. 2021. 3. 20. 18:44
320x100

1. JPA의 탄생
객체 지향 <-> 관계형 데이터베이스 이슈(패러다임 불일치 이슈)를 해결 하기 위한 ORM 표준 기술.
Hibernate는 JPA의 실제 구현체이다.

 

JPA의 역할



2. JPA에서 관리하는 객체는 @Entity 어노테이션을 적어주어야 한다.

3. JPA에서 Entity를 DB와 매핑하기 위해서는 @Id 어노테이션을 적어주어야 한다.

 

JPA의 Entity

4. JPA에서 가장 중요한 두 가지 축은 1) 영속성 컨텍스트, 2) 객체와 테이블 매핑 이다.

5. 영속성 컨텍스트
    1) persist()  => 1차 캐시에 Entity를 저장
    2) commit(), flush(), 쿼리를 날릴 때 => DB에 적용
    3) 쓰기 지연 SQL 저장소는 persist(), dirty check를 탐지해서 INSERT, UPDATE SQL을 보관한다. 나중에 flush() 될 때 실행되는 쿼리문들이다.

 

영속성 컨텍스트



6. 객체와 테이블 매핑
    1) 매핑에는 @OneToOne, @OneToMany, @ManyToOne, @ManyToMany 네 가지 방식이 있다.
    2) 연관관계를 설계할 때에는 처음에 단방향으로 설계하고, 필요에 따라 양방향은 되도록 사용 안하는 게 좋다. (꼭 써야하는 상황이라면 쓸 수도 있겠지만..)
    3) 양방향 연관관계를 써야하는 경우라면 무한 루프를 조심하자. (ToString() 이런거...)
    4) 연관관계의 주인은 Foreign Key를 가지고 있는 Entity로 설정해 주는 게 정신 건강에 좋다.
    5) 연관관계의 주인이 아닌 Entity는 @OneToMany(mappedBy = "") 에서 mappedBy 속성을 지정해주어야 한다.
    6) @ManyToMany는 실무에서 절대 쓰지 말자. 관계형 데이터베이스는 애초에 다대다 관계를 표현할 수 없다. 
   설사 @JoinTable 등의 어노테이션을 사용해서 가능하게 하더라도 유지 보수 헬이 펼쳐진다.

 

일대일 관계

 

일대다 관계

 

다대일

 

다대다 (그림이 너무 못생겼다..)



7. 관계형 데이터베이스는 애초에 양방향 연관관계라 방향이 의미가 없다. 그러나 객체 지향에서 객체 간에는 단방향, 양방향 관계가 생기기 때문에 mappedBy 속성으로 관계의 주인을 설정해주어야 한다. 

 

8. @Inheritance(strategy = InheritanceType.XXXX)  어노테이션을 사용하면 상속 관계를 매핑해줄 수도 있다. 이 경우 stategy에 따라 생성되는 테이블의 형태, 개수가 달라진다.

(JOINED, SINGLE_TABLE, TABLE_PER_CLASS - TABLE_PER_CLASS는 쓰지 말자)

9. @MappedSuperclass 어노테이션을 사용하면 Entity를 작성할 때마다 공통된 정보를 매번 중복 작성할 필요가 없다. (createdBy, lastModifiedTime 등)

10. JPA는 Proxy 객체 개념을 이용하여 지연 로딩을 지원한다.

    1) em.find()는 DB에서 실제 Entity 객체를 조회하고, em.getReference()는 조회를 미루는 Proxy 객체를 조회한다.
    2) 한 트랜잭션 내에서는 find()의 리턴값과 getReference()의 리턴값이 동일해야 함. 
   즉, 한 트랜잭션 내에서 find()가 먼저 호출되면 getReference()도 실제 Entity를 리턴하고, getReference()가 먼저 호출되면 find()도 Proxy 객체를 리턴한다.

11. JPA는 즉시 로딩, 지연 로딩 개념이 있지만 실무에서는 왠만하면 지연 로딩만 쓰자. 즉시 로딩을 쓰면 예상치 못한 SQL이 발생해서 속도 지연 등 운영에 영향을 미치는 경우가 많다.

12. JPA에서는 영속성 전이 개념과 고아 객체 개념을 통해 부모 Entity가 자식 Entity의 생명 주기를 조절할 수 있다.
    1) 영속성 전이(CASCADE)는 한 Entity를 영속화할 때 연관된 Entity들에게도 영속화가 전이되는 특성이다. (ALL, PERSIST, REMOVE 옵션 중 하나를 주로 사용)
    2) 고아 객체는 부모 Entity와 연관 관계가 끊어진 Entity로, JPA에서는 고아 객체 상태가 된 Entity가 자동으로 삭제되는 orphanRemoval 옵션을 제공한다.

 

13. JPA의 데이터 타입은 1) 엔티티 타입, 2) 값 타입이 있다.
    1) 엔티티 타입은 @Entity로 선언한 객체
    2) 값 타입은 a) 기본값 타입, b) 임베디드 타입, c) 컬렉션 값 타입이 있다.
        a) 기본값 타입은 String, int 등 우리가 흔히 아는 그 값들
        b) 임베디드 타입은 Entity를 좀 더 객체지향적으로 구현하여 공통된 속성을 또 하나의 클래스로 뺀 객체
    Entity를 선언한 부분에 @Embeddable 어노테이션을 적어주고, 임베디드 타입 Entity를 사용하는 쪽에서 @Embedded 어노테이션을 적어주어서 사용한다.
        c) 컬렉션 값 타입은 값을 하나 이상 저장할 때 사용한다.
   컬렉션 값 타입은 변경 상황이 발생하면 주인 엔티티와 연관된 모든 데이터를 삭제하고 현재 값을 다시 저장한다.
    3) 값 타입의 불변 객체를 만들면 equals(), hashCode() 함수도 적절하게 구현해주어야 한다.
    4) 실무에서는 상황에 따라 컬렉션 값 타입 대신 일대다 연관관계를 사용하는 게 좋다.

14. JPA는 검색을 효율적으로 하기 위해 SQL을 추상화한 JPQL 개념을 사용한다.

15. 실무에서 JPQL을 사용하려면 QueryDSL을 함께 쓰자. 쉽게 쓸 수 있고 디버깅, 동적 쿼리 작성등에 이점이 있다.

16. JPQL에서 묵시적 조인은 쓰지 말자. 묵시적 조인은 JOIN이 일어나는 상황을 프로그래머가 한 눈에 알 수가 없어서 쿼리 튜닝이 빡세다.

17. 실무에서는 JPQL의 fetch JOIN 정말정말정말정말 중요하다. "N + 1" 문제를 해결해주기 때문이다.
     
18. fetch JOIN은 지연 로딩보다 우선 순위가 높아서 연관 Entity를 모두 불러온다.
     fetch JOIN 하면 일대다 관계가 생겨서 데이터가 뻥튀기된다. 여기서 중복을 제거하려면 JPQL에서 지원해주는 DISTINCT 기능을 사용하면 된다.

19. JPA 성능 문제의 70 ~ 80%는 "N + 1" 문제이기 때문에, fetch JOIN만 잘 써도 성능이 많이 향상된다.

20. JPA는 @NamedQuery 어노테이션을 제공해서 query문을 미리 지정하여 사용하게 해준다.
    @NamedQuery의 큰 장점 중 하나는 애플리케이션 로딩 시에 sql query 문의 타당성을 평가해주기 때문에 버그의 소지를 많이 줄여준다.

21. JPA는 SQL query 한 번으로 여러 테이블 Row를 변경하는 벌크 연산을 지원한다.
    벌크연산은 영속성 컨텍스트를 지나치지 않고 바로 DB에 접근하기 때문에 벌크 연산 수행 후 영속성 컨텍스트를 초기화(em.clear()) 해주는 게 좋다.

728x90
728x90
Comments