[항해 취업코스] 개발자 취준 기록 3일차-영속성 컨텍스트

Godtaek·2024년 3월 7일
0

Spring

목록 보기
7/9

1. 서론

JPA를 풀어쓰면 Java Persistence API다.
Persistence(영속성)는 무슨 뜻일까?

  1. 오래 계속되는 성질
  2. 데이터나 객체가 프로그램이 종료되거나 메모리에서 삭제되더라도 그 상태가 유지되는 것을 의미

위키백과에서는 다음과 같이 정의한다.

컴퓨터 공학에서 지속성(Persistence)은 프로세스가 생성했지만 별개로 유지되는 상태의 특징 중 한 가지이며, 별도의 기억 장치에 데이터를 보존하는 것을 목적으로 한다. 이 특징으로 인해 프로그래머는 저장 장치로부터 데이터를 전송하는 작업 및 자료 구조 등을 이용해 데이터를 보존하는 것이 가능하다.

이 특징 없이는 상태는 오직 RAM에만 존재할 수 있고, 컴퓨터가 종료되어 RAM이 전력을 잃어버린다면 상태도 같이 사라져 버리게 된다

영속성이란 의미를 봤을 때, DB는 고유의 방법을 통해 데이터가 영속성을 가지도록 한다.
단순 SQL만 사용한다면, DB에 반영하기 위한 객체가 영속성을 가져야할 필요가 없지만, ORM은 다르다.

영속성 컨텍스트는 엔티티를 영구저장하는 개념이며 JPA가 영속성을 보장하는 장치이다.

2. 영속성 컨텍스트 기능 및 장점

1. 1차 캐시

영속 상태의 엔티티 객체를 캐싱

2일차에서 엔티티 생명주기를 공부할 때, 영속 상태, 준영속 상태에서 엔티티 객체는 식별자를 가진다는 언급을 했다.

영속 상태가 된다면, 1차 캐시에 Map형태로 저장된다. 이 때 key는 식별자(primary key), value는 엔티티 객체가 된다.

사진으로는 위와 같다. 영속 상태가 되면 1차 캐시에 엔티티 객체를 캐싱한다. 그 후 캐싱 데이터에 대한 요청이 존재한다면 1차 캐시에서 응답하여 객체를 반환한다. 없다면 DB에서 가져와 1차 캐시에 캐싱후 반환한다.

코드로 보자

    @Transactional
    public Book createBook(String name, String content) {
    	// 비영속 상태
        Book book = new Book();
        book.setName(name);
        book.setContent(content);
        // 영속 상태 전환
        bookRepository.save(book);
        
        // 1차 캐시 확인
        Book findBook = bookRepository.findById(book.getId()).orElseThrow(RuntimeException::new);
        System.out.println(findBook);
        System.out.println("___________________________");
        return book;
    }

1차 캐시가 존재한다면, select문이 나가지 않아야 될 거고 존재하지 않는다면 select문이 나갈 것이다.

select문이 나가지 않은 것을 볼 수 있다. 1차 캐시 덕분이다.

다만, native query 등 쿼리문을 직접 개발하여 실행시킨다면, 1차 캐시를 무시한다. 이 때 DB와 영속성 컨텍스트간 불일치가 생긴다. query를 직접 개발했다면, 반드시 clear() 등을 통해서 동기화 시키는 등의 노력을 하자

2. 동일성 보장

반복가능한 읽기 수준의 트랜잭션 격리 수준을 어플리케이션 단위에서 보장

쉽게 설명하자면 같은 트랜잭션 범위에서 1차 캐시가 관리하는 pk가 같은 객체를 찾을 때, 동일한 객체를 보장한다는 것이다. 새로운 객체를 생성하지 않는다.

메모리를 절약할 수 있으며, 데이터 일관성을 보장하고, DB I/O를 줄일 수 있다.

    @Transactional
    public Book getBook(Long id) {
        Book bookA = bookRepository.findById(1L).orElseThrow(RuntimeException::new);

        Book bookB = bookRepository.findById(1L).orElseThrow(RuntimeException::new);
        
        System.out.println("Check Same Object: " + (bookA == bookB));
        return bookRepository.findById(id).orElseThrow(RuntimeException::new);
    }

bookA와 bookB는 같은 pk를 가진 엔티티 객체를 찾아온다. 결과는?

같은 메모리 주소에서 참조하고 있다는 것을 볼 수 있다. (create 메서드를 다른 메서드에서 불러 insert와 select가 동시에 나가는 것이다.)

3. 쓰기 지연(SQL)

같은 트랜잭션 레벨에서 변경이 감지될 때 바로 쿼리를 날리는 것이 아니라, 일괄적으로 처리하여 성능을 향상시키는 메커니즘이다.

네트워크 비용을 줄일 수 있고, DB 연산을 최적화할 수 있다는 장점이 있다.

Book book1 = Book.of("1번책", "1번책내용");
Book book2 = Book.of("2번책", "2번책내용");

// 엔티티 객체 영속화
em.persist(book1);
em.persist(book2);

// 커밋이나 flush 시점에서 모아뒀던 sql문이 나간다.
transaction.commit();

이 때, 쓰기 지연 저장소에서 sql을 몇 개까지 저장할 건지 지정할 수 있다.

application.yml에서

spring:
  jpa:
    properties:
      hibernate:
        jdbc:
          batch_size: 20

4. 변경 감지(Dirty Checking)

Dirty Checking

전에 썼던 글을 참고하자

5. 지연 로딩

말 그대로 로딩을 지연한다는 뜻으로, 데이터를 로드할 때, 필요한 시점에 데이터를 가져온다는 뜻이다. 준영속 상태는 지연 로딩을 할 수 없다!

이 부분! 다음에 적겠다. 생각보다 지연 로딩에 대해서 몰랐던 것도 있고, 설명할 내용도 많아보이기 때문.... 프록시 객체를 로딩한다고?

3. 마치며

영속성 컨텍스트로 JPA와 관련된 글은 마무리 지으려고 했으나.... Lazy Loading을 숙제처럼 남겨두게 되었다. 이왕 이렇게 된 거 쓰기 지연도 한 번 공부해보고 싶은데...

어쨌든 영속성 컨텍스트는 JPA에서 가장 중요한 개념 중 하나이다. 연관관계 매핑을 쉽게 할 수 있는 것과 더불어서 JPA를 사용하는 이유 중 하나다.

사실 논리적인 개념이라 처음 봤을 때, 이해가 되지 않았다. JPA와 관련하여 간단한 프로젝트라도 경험한 뒤 (적어도 CRUD, 연관관계를 다 하는) 다시 영속성 컨텍스트를 다시 봤을 때 이해가 훨씬 쉬웠다.


항해 개발자 취업 리부트 코스를 수강하고 작성한 콘텐츠 입니다
해당 강의를 듣고 작성했습니다. 자바 ORM 표준 JPA 프로그래밍 - 기본편

profile
성장하는 개발자가 되겠습니다

0개의 댓글