JPA(Java Persistence API) 정리

GyuHyeong·2021년 5월 21일
0

JPA시리즈

목록 보기
1/2

JPA?

  1. Java Persistence API의 약자
  2. 자바 표준 스펙(JPA자체는 Interface의 집합)
  3. Java 진영의 ORM 프레임워크

ORM = Object-Relational Mapping 의 약자. 즉, 객체와 관계형DB간의 매핑을 해주는 프레임워크

특징

  1. RDB의 데이터와 Object간의 매핑을 개발자가 신경 쓰지 않아도 동작원리만 파악하면 자동으로 해줍니다.
  2. SQL문에서 완전히 벗어날 수는 없지만, 상당부분의 SQL문을 대신 작성해주기 때문에 개발효율이 늘어납니다.
  3. Tranjaction을 이용한 쓰기지연, 지연로딩등을 이용해 성능 최적화가 가능합니다.
  4. 같은 Tranjaction이라면, 동일한 Id값을 가진 객체는 같은 객체를 보장해줍니다.
  5. 객체의 상속에 대한 데이터 조회도 RDB에서 조회하기 쉽습니다.

핵심개념

JPA 를 이용해 엔티티 CRUD하고 싶으면 반드시 영속성 컨텍스트에 엔티티가 저장된 상태에서 해야합니다.
즉, 영속성 컨텍스트를 거치지 않은 엔티티는 JPA에 의해 관리되지 않습니다.

  1. 영속성 컨텍스트
    엔티티를 영구 저장할수 있는 환경을 제공하는 논리적인 개념.
    JPA의 핵심이며 영속성 컨텍스트를 거쳐야만 해당 엔티티의 CRUD를 JPA가 관리할 수 있습니다.

  2. 엔티티 영속상태
    영속상태: 영속성 컨텍스트에 Persist되어 관리되는 상태
    (ex: JPA로 DB 에서 엔티티조회, 엔티티 생성 후 영속성 컨텍스트에 persist한 경우)
    준영속상태: 이전에 영속성 컨텍스트에 Persist되었으나, 현재는 관리되지 않는상태(detached)
    비영속상태: 영속성 컨텍스트와 관련없는 상태 (ex: new Member()등으로 객체만 생성해둔상태)
    삭제: 데이터를 삭제하기 위해 영속성 컨텍스트에 저장된 상태

  3. Tranjaction에 기반한 쓰기지연
    영속성 컨텍스트에서 관리하는 모든 엔티티의 CRUD 동작은 반드시 Tranjaction이 동작하는 범위 내에서 수행해야하며, Tranjaction이 종료되거나 JPQL, flush()등을 수행하기 전까지 엔티티의 CRUD를 하더라도 쿼리가 즉시 전송되지 않으며, 위 세가지 경우를 만났을 때 영속성 컨텍스트에 저장된 엔티티를 갖고 DB쿼리를 발생시킵니다.

  4. 더티체크(변경감지)
    영속성 컨텍스트에는 1차캐시가 존재하는데, 이 1차캐시에는 해당 엔티티의 @Id 선언한 값, 엔티티, 스냅샷을 갖고있습니다. 스냅샷의 경우 영속성 컨텍스트에 최초로 저장될때의 엔티티상태를 말하는데, 3번의 DB쿼리를 발생시키는 타이밍을 만나면 이 스냅샷과 현재 엔티티의 필드 값을 비교하여 다를경우 해당 값을 자동으로 감지하여 DB 로 Update쿼리를 발생시킵니다.

  5. 즉시로딩, 지연로딩
    n:1, 1:n 등의 연관관계 맵핑시 즉시로딩, 지연로딩을 설정하여 성능을 최적화 시킬 수 있습니다.
    즉시로딩의 경우 어떤 엔티티를 조회할때 그와 연관된 엔티티를 항상 같이 join fetch해서 가져온다는 의미이고,
    지연로딩의 경우 어떤 엔티티를 조회할때 우선 그 엔티티만 가져오고 연관된 엔티티는 우선 프록시객체로 반환해뒀다가 나중에 필요할 때 DB에서 데이터를 조회해 불러오겠다는 의미입니다.
    이는 매우 중요한 의미를 갖는데,
    연관된 엔티티를 같이 쓰는경우가 잦은경우 즉시로딩을 통해 쿼리 발생자체를 줄이는 것이 성능향상에 좋지만, 항상 조회시 Join이 발생한다는 단점이 있고 Collection엔티티가 내부에 있다면 N+1문제가 발생,
    연관된 엔티티를 함께 쓰는 경우가 그리 많지 않다면 지연로딩을 통해 불필요한 Join을 피할 수 있지만,
    여전히 반복문에서의 Collection객체를 조회할 시 N+1문제에 직면하게됩니다.
    우선 지연로딩을 기본으로 채택하고, 필요할시 지연로딩을 하는 것이 권장됩니다.
    Collection은 JPQL에서 join fetch하여 조회하는것이 권장됩니다.

  6. join fetch
    JPA에서 없어선 안될 엄청나게 중요한 기능입니다.
    특정 엔티티와 연관된 엔티티의 데이터까지 '한 방'쿼리로 가져올 수 있는 기능입니다.
    다만, Collection의 join fetch는 1개까지만 가능하고, paging이 불가능하다는 단점이 있습니다.
    (hibernate를 이용하면 아주 간단하게 paging도 가능합니다)

  7. JPQL
    Java Persistence Query Language 약자로 JPA버전의 SQL이라 생각하시면 간단할 듯 싶습니다.
    사용법은 SQL과 매우 유사하며, join시 fetch키워드를 별도로 선언하여 조인하는 외부테이블에 대해서도 그 엔티티 데이터를 같이 가져올 수 있습니다.
    서브쿼리는 from절의 서브쿼리만 빼고 작성 가능합니다.
    동적쿼리도 Criteria를 이용하면 작성할 수 있지만, 너무 복잡하고 이해하기도 어렵기때문에, QueryDSL을 사용하세요
    (QueryDsl 정말 사랑합니다.)

  8. 프로젝션
    혹시 엔티티를 엔티티 객체로 반환받는 것이 아닌 특정 DTO에 값을 바인딩해서 반환받고 싶다면 이 기능을 사용하면 됩니다. JPQL 선언시 Select단 다음에 생성자 연산자 new 를 붙이고, 해당 DTO의 경로.생성자메소드 를 정의해준뒤 from 다음의 테이블에서 가져올 값을 생성자메소드 인자로 넣어주면 해당 DTO객체로 바인딩해서 반환해줍니다.


마치며

JPA는 인터페이스의 집합이기때문에, JPA자체로만 사용할 수는 없습니다.
따라서 Hibernate나 Eclipse Link같은 구현체를 이용하여 사용해야 하는데, 저는 Spring Data JPA를 사용하기 때문에, Hibernate만 사용한 경험이 있습니다.

Spring Data JPA는 메소드명으로 쿼리를 생성할 수 있는 기능과 정렬, Paging, Paging객체, Collection fetch join시 Paging문제를 해결할 수 있는 기능등의 JPA를 더욱 편리하고 유용하게 사용할 수 있는 기능들을 제공해주고 있습니다.
다음 글에선 이 Spring Data JPA에 대해서도 정리하는 글을 작성하겠습니다.

profile
잘하고 싶은 주니어 백엔드 개발자 입니다.

0개의 댓글