JPA 문제

파란상자·2024년 10월 23일
0
post-thumbnail

1.JPA와 Hibernate를 정의하고 차이점은 무엇인가요?

  • JPA : 자바 진영의 ORM 기술 표준, 데이터베이스 작업을 위한 규약을 정의한 인터페이스
  • Hibernate : ORM 프레임 워크, JPA의 구현체
  • 차이점 : JPA는 인터페이스이며, Hibernate는 JPA를 구현한 프레임워크이다.

2.Hibernate에서 Session과 Transaction의 역할은 무엇인가요?

a. Sesion

  • Hibernate에서 데이터베이스 작업(저장, 수정, 삭제, 조회 등)을 수행하는 주요 인터페이스 ( save(), get(), update(), delete() )
  • 데이터베이스 간의 연결을 나타내며, 일종의 데이터베이스 연결 핸들
  • CRUD 작업 수행
  • 1차 캐시 제공 : 동일한 엔티티를 여러 번 조회할 경우, 데이터베이스에 직접 접근하지 않고 캐시된 객체를 반환. 이는 성능을 향상시키는 데 기여함.
  • 라이프 사이클 관리 : 엔티티 객체의 상태를 관리하며, 엔티티를 Persistent, Transient, Detached 상태로 구분하고 이를 통해 객체의 라이프사이클을 제어함.
  • 플러싱(Flushing) : 엔티티의 변경 사항을 데이터베이스에 동기화하는 기능

b. Transaction

  • 데이터베이스 작업을 하나의 논리적 작업 단위로 묶어주는 역할
  • 데이터베이스 작업이 성공적으로 처리되었는지 혹은 실패했을 때 롤백해야 하는지를 관리
  • ACID 속성 보장
  • 커밋 및 롤백
  • 동시성 제어

3.JPA에서 Entity의 Life Cycle은 어떻게 이루어지나요?

  1. 비영속(new / transient) : 영속성 컨텍스트와 전혀 관계가 없는 상태
  2. 영속(managed) : 영속성 컨텍스트에 저장된 상태
  3. 준영속(detached) : 영속성 컨텍스트에 저장되었다가 분리된 상태
  4. 삭제(removed) : 삭제된 상태

4.JPA에서 Entity의 상태(State) 전이는 어떻게 이루어지나요?

  • Transient → Persistent: persist()를 호출하거나, merge()로 새 엔티티를 저장합니다.
  • Persistent → Removed: remove()를 호출하여 삭제 상태로 만듭니다.
  • Persistent → Detached: detach()를 호출하거나, EntityManager가 종료되면 Detached 상태가 됩니다.
  • Detached → Persistent: merge()를 호출하여 다시 영속성 컨텍스트로 가져옵니다.
  • Persistent → Persistent: 이미 영속성 컨텍스트에서 관리 중인 엔티티를 여러 번 조회하거나 변경할 수 있습니다.

5.JPA에서 Persist() 메서드는 무엇을 하는 메서드인가요?

  • EntityManager를 사용해서 엔티티를 영속성 컨텍스트에 저장한다.

6.JPA에서 Flush() 메서드는 무엇을 하는 메서드인가요?

  • 영속성 컨텍스트의 변경내용을 DB에 반영하는 것을 말한다.

7. JPA에서 Cascade Type을 지정하지 않으면 어떻게 될까요?

  1. JPA에서 Cascade Type을 지정하지 않으면, 부모 엔티티에 대한 상태 변경이 자식 엔티티에 자동으로 전파되지 않는다. 즉, 부모 엔티티에 대한 작업(저장, 삭제 등)을 수행할 때 연관된 자식 엔티티에 대해서는 별도로 명시적인 작업을 수행해야 한다.
  2. 게시글을 저장할 때 연관된 댓글은 자동으로 저장되지 않는다.
  3. 게시글을 저장하려면 먼저 댓글들을 각각 entityManager.persist()로 저장한 후, 게시글을 저장해야 한다.

8. JPA에서 FetchType.LAZY로 설정했을 때 N+1 문제가 발생하는 이유는 무엇인가요?

  • FetchType.LAZY는 지연로딩 방식으로 엔티티의 연관 관계를 필요할 때 로드하도록 설정하는 것을 말하며 하나의 쿼리로 주 엔티티를 조회한 후 연관된 엔티티를 각각 조회하기 위해 추가로 N개의 쿼리가 발생하는 것을 N+1 문제라고 한다.

    예시로 설명

    1. 상황: Memeber 엔티티와 Team 엔티티가 다대일 관계를 가지고 있다고 가정

    2. Member 엔티티는 여러 명의 회원,Team 엔티티는 각 회원이 속한 팀을 나타냄

      @Entity
      public class Member {
          @Id
          @GeneratedValue
          private Long id;
      
          private String name;
      
          @ManyToOne(fetch = FetchType.LAZY)
          @JoinColumn(name = "team_id")
          private Team team;
      }
      
      @Entity
      public class Team {
          @Id
          @GeneratedValue
          private Long id;
      
          private String name;
      }
    3. 문제 발생 시나리오:

      • Member 엔티티 리스트를 조회하는 JPQL 쿼리를 실행

        List<Member> members = entityManager.createQuery("SELECT m FROM Member m", Member.class).getResultList();```
        
      • 이 쿼리로 인해 회원 데이터를 조회하는 쿼리가 실행

      • 예를 들어, 10명의 Member가 조회되었다면

        SELECT * FROM Member;
      • 이후, 각 Member 엔티티의 Team 필드가 사용될 때마다 추가적으로 Team 엔티티를 조회하는 쿼리가 실행

        sql
        SELECT * FROM Team WHERE id = ?; -- 첫 번째 회원의 팀 조회
        SELECT * FROM Team WHERE id = ?; -- 두 번째 회원의 팀 조회
        ...
        SELECT * FROM Team WHERE id = ?; -- 열 번째 회원의 팀 조회```
        
      • 결과적으로, 1개의 메인 쿼리 + 10개의 추가 쿼리로 총 11개의 쿼리가 실행되며 N+1 문제가 나타남

      해결 방법

      1. FetchType.EAGER로 설정: 연관된 엔티티를 즉시 로딩하여 Member 를 조회할 때 Team 엔티티도 함께 조회한다. 하지만, 무조건 즉시 로딩을 사용하는 것은 성능 문제를 야기할 수 있다.

      2. @EntityGraph() 사용: JPQL 쿼리 실행 시점에 즉시 로딩을 적용

        @EntityGraph(attributePaths = {"team"})
        List<Member> findAllWithTeam();
      3. Join Fetch 사용: JPQL에서 Join Fetch를 사용하여 N+1 문제를 해결

        List<Member> members = entityManager.createQuery(
            "SELECT m FROM Member m JOIN FETCH m.team", Member.class).getResultList();
      • 이렇게 하면 Member 와 연관된 Team 을 한 번의 쿼리로 모두 조회할 수 있음
  1. JPA에서 Entity를 캐시하는 방법은 무엇인가요?

    1. 1차 캐시: 1차 캐시는 영속성 컨텍스트 내부에 있다. 엔티티 매니저로 조회하거나 변경하는 모든 엔티티는 1차 캐시에 저장된다.
      트랜잭션을 커밋하거나 플러시를 호출하면 1차 캐시에 있는 엔티티의 변경 내역을 데이터베이스에 동기화한다.
      일반적으로 JPA는 스프링 프레임워크 같은 컨테이너 위에서 실행하면 트랜잭션을 시작하거나 종료할 때 영속성 컨텍스트도 시작하거나 종료한다.(OSIV 제외)

    위에 이미지처럼 find를 했을 때 해당 엔티티가 1차 캐시에 존재하면 1차 캐시에 저장된 엔티티를 반환하고 존재하지 않으면 DB를 조회한다.

    1. 2차 캐시: 애플리케이션에서 공유하는 캐시를 JPA는 공유 캐시라 하는데 일반적으로 2차 캐시라 부른다. 2차 캐시는 애플리케이션을 종료할 때까지 유지되고 분산 캐시나 클러스터링 환경의 캐시는 애플리케이션보다 더 오래 유지될 수도 있다. 2차 캐시를 적용하면 엔티티 매니저를 통해 데이터를 조회할 때 우선 2차 캐시에서 찾고 없으면 데이터 베이스에서 찾는다. 2차 캐시를 적절히 활용하면 데이베이스 조회 획수를 획기적으로 줄일 수 있다.
      1차 캐시에서 엔티티를 찾아보고 없으면 2차 캐시에서 엔티티를 찾는다. 찾는 엔티티가 존재하면 2차 캐시에서 반환하고 존재하지 않으면 DB를 조회해서 반환한다. 이때 2차 캐시는 동시성을 극대화하기 위해 캐시 한 객체를 직접 반환하지 않고 복사본을 만들어 반환한다.

10. JPA에서 쿼리 캐시가 무엇이고 이를를 사용하는 방법은 무엇인가요?

  • 쿼리 캐시란 쿼리와 파라미터 정보를 키로 사용해서 쿼리 결과를 캐시하는 방법이다. 쿼리 캐시를 적용하려면 영속성 유닛을 설정에 hibernate.cache.use_query_cache 옵션을 꼭 true로 설정해야한다. 그리고 쿼리 캐시를 적용하려는 쿼리마다 org.hibernate.cacheable을 true로 설정하는 힌트를 주면 된다.

11. Hibernate에서 1차 캐시와 2차 캐시의 차이점은 무엇인가요?

  • 1차 캐시: 영속성 컨텍스트 내부에 엔티티를 보관하는 저장소. 일반적인 웹 애플리케이션 환경은 트랜잭션을 시작하고 종료할 때까지만 1차 캐시가 유효하다. 엔티티 매니저로 조회하거나 변경하는 모든 엔티티는 1차 캐시에 저장된다. 트랜잭션을 커밋하거나 플러시를 호출하면 1차 캐시에 있는 엔티티의 변경 내역을 데이터베이스에 동기화 한다.
  • 2차 캐시: 애플리케이션 범위의 캐시. 애플리케이션을 종료할 때까지 캐시가 유지된다. 분산 캐시나 클러스터링 환경의 캐시는 애플리케이션보다 더 오래 유지될 수도 있다.2차 캐시를 적용하면 엔티티 매니저를 통해 데이터를 조회할 때 우선 2차 캐시에서 찾고 없으면 데이터베이스에서 찾는다. 이를 적절히 활용하면 데이터베이스 조회 횟수를 획기적으로 줄일 수 있다.

12. JPA에서 변경감지(Dirty Checking)이란 무엇인가요?

  • 엔티티의 변경 사항을 데이터베이스에 자동으로 반영하는 기능

13. Hibernate에서 Dirty Checking이 어떻게 이루어지나요?

  1. 트랜잭션을 커밋하면 엔티티 매니저 내부에서 먼저 플러시(flush())가 호출된다.
  2. 엔티티와 스냅샷을 비교해서 변경된 엔티티를 찾는다.
  3. 변경된 엔티티가 있으면 수정 쿼리를 생성해서 쓰기 지연 SQL 저장소에 보낸다.
  4. 쓰기 지연 저장소의 SQL을 데이터베이스에 보낸다.
  5. 데이터베이스 트랜잭션을 커밋한다.

14. JPA에서 Proxy란 무엇인가요?

  • 프록시는 지연 로딩을 구현하기 위해 사용되는 기술이다. 실제 엔티티 객체를 대신하여 데이터베이스에서 해당 데이터를 가져오기 전까지 대리 역할을 하는 객체이다. JPA는 데이터베이스에서 필요한 데이터를 실제로 로딩하지 않고, 나중에 필요할 때 로딩하는 방식을 제공하기 위해 프록시 객체를 생성한다.

15. Hibernate에서 Proxy는 어떻게 생성되고 사용되나요?

  • 책 p.293 참고
    1. 프록시 객체에 member.getName()을 호출해서 실제 데이터를 조회한다.
    2. 프록시 객체는 실제 엔티티가 생성되어 있지 않으면 영속성 컨텍스트에 실제 엔티티 생성을 요청하는데 이것을 초기화라 한다.
    3. 영속성 컨텍스트는 데이터베이스를 조회해서 실제 엔티티 객체를 생성한다.
    4. 프록시 객체는 생성된 실제 엔티티 객체의 참조를 Member target 멤버 변수에 저장한다.
    5. 프록시 객체는 실제 엔티티 객체의 getName()을 호출해서 결과를 반환한다.

16. JPA에서 Query Language는 무엇인가요?

  • JPA에서 데이터를 조회하거나 조작하기 위해 사용하는 쿼리 언어입니다. 대표적으로 JPQL(Java Persistence Query Language)이 있으며, SQL과 유사하지만 엔티티 객체를 대상으로 쿼리를 작성합니다.

17. JPA에서 JPQL(JPA Query Language)이란 무엇인가요?

  • JPA에서 제공하는 객체 지향 쿼리 언어로, SQL과 유사하지만 엔티티 객체를 대상으로 동작합니다. JPQL은 데이터베이스의 테이블이 아니라, 엔티티와 그 관계를 대상으로 하기 때문에, 객체 지향적인 방식으로 데이터를 검색하거나 조작할 수 있습니다.

18. JPA에서 Named Query란 무엇인가요?

  • JPQL 쿼리를 미리 정의해놓고, 이름을 통해 재사용할 수 있는 기능입니다. 보통 @NamedQuery 어노테이션을 사용하여 엔티티 클래스에 정의되며, 특정한 쿼리를 반복적으로 사용할 때 성능 최적화 및 가독성을 높이기 위해 사용됩니다.

19. JPA에서 Criteria API란 무엇인가요?

  • JPA에서 제공하는 동적 쿼리를 생성하기 위한 API입니다. JPQL처럼 문자열을 사용하는 것이 아니라, 자바 코드로 쿼리를 생성할 수 있기 때문에 타입 안전성을 보장하며, IDE에서 코드 컴파일 시점에 문법 오류를 확인할 수 있습니다. 이를 통해 JPQL보다 유연하게 쿼리를 생성할 수 있습니다.

20. JPA에서 Criteria Query를 사용하는 이유는 무엇인가요?

  • JPQL의 문자열 기반 쿼리보다 더 유연하고 타입 안전한 쿼리를 작성할 수 있기 때문입니다. 특히, 복잡한 동적 쿼리를 작성해야 할 경우에 유용합니다. Criteria API는 자바 코드로 쿼리를 작성하므로, 컴파일 타임에 쿼리 오류를 검출할 수 있고, IDE의 자동 완성 기능을 활용할 수 있습니다. 또한, 쿼리 조건을 동적으로 구성해야 하는 상황에서 Criteria API는 매우 편리하게 적용될 수 있습니다.

21. Hibernate에서 SQL Query를 작성하는 방법은 무엇인가요?

  • Hibernate 프레임워크에서 사용하는 객체 지향 쿼리 언어인 HQL(Hibernate Query Language)를 통해 쿼리를 작성한다. HQL은 DB 테이블이 아닌 Hibernate 에서 매핑된 엔티티 객체와 필드를 대상으로 쿼리를 작성한다. Java 클래스와 속성에 기반하여 SQL과는 구문적으로 다르다. Hibernate가 해당 쿼리를 SQL로 변환하여 실행한다.

22. Hibernate에서 Native SQL Query를 작성하는 방법은 무엇인가요?

  • Native SQL Query는 일반적으로 특정 데이터베이스 시스템에 특화된 SQL 쿼리로 기존 SQL 문법을 사용하며, 직접 DB 테이블 및 필드를 대상으로 쿼리를 작성한다.

23. JPA에서 Transaction의 기본 동작 방식은 무엇인가요?

  • Dirty Checking: 트랜잭션이 끝나는 시점에 변화가 생긴 모든 엔티티들을 데이터베이스에 자동으로 반영한다. JPA에서 영속성 컨텍스트가 관리하고 있는 엔티티를 조회하면 해당 엔티티의 조회 상태로 스냅샷을 만들고 트랜잭션이 끝나는 시점에 스냅샷과 비교하여 변화가 있다면 데이터베이스에 update 쿼리를 한다.

24. JPA에서 Transaction의 Isolation Level은 무엇인가요?

  • 트랜잭션은 ACID라고 하는 원자성, 일관성, 격리성, 지속성을 보장해야 하는데, 이 중 격리성을 완벽히 보장하려면 트랜잭션을 거의 차례대로 실행해야 한다. 이렇게 하면 동시성 처리 성능이 매우 나빠지기 때문에 ANSI 표준은 트랜잭션의 격리 수준을 4단계로 나누어 정의했다.
    • READ_UNCOMMITTED: 커밋되지 않은 데이터도 읽을 수 있다.
    • READ_COMMITTED: 커밋된 데이터만 불러온다.
    • REPEATABLE_READ: 트랜잭션 동안에는 한번 조회한 데이터를 계속 조회해도 같은 데이터가 나오지만, 만약 다른 트랜잭션에서 데이터를 추가한 경우 기존 트랜잭션에서 반복 조회하면 결과 집합이 새로 추가된 데이터를 포함한 결과를 가져온다.
    • SERIALIZABLE: 모든 트랜잭션을 순서대로 실행한다.

25. JPA에서 Transaction의 Propagation은 무엇인가요?

  • 이미 트랜잭션이 진행 중일 때 추가 트랜잭션 진행을 어떻게 하지 결정하는 것을 전파 속성(Propagation)이라고 한다. 스프링은 총 7가지 전파 속성(REQUIRED, SUPPORTS, MANDATORY, REQUIRES_NEW, NOT_SUPPORTED, NEVER, NESTED) 을 제공한다.
    • NEVER: 트랜잭션이 존재하는 경우 예외를 발생시키고, 트랜잭션이 없는 상태로 처리를 수행한다.
    • NESTED: 트랜잭션이 존재할 경우 중첩된 트랜잭션을 생성하여 처리를 수행하고, 존재하지 않는다면 REQUIRED와 동일하게 동작한다.
    • REQUIRED: 트랜잭션이 존재하는 경우 해당 트랜잭션을 그대로 사용하고, 트랜잭션이 없는 경우 트랜잭션을 생성한다.
profile
프로그래머 생산중....

0개의 댓글