JPA.

[우아한테크캠프 Pro] JPA Hands-on Part 1 정리

PI.314 2022. 11. 3. 21:19

해당 글은 우아한 테크캠프 PRO에서 진행된 JPA Hands-on Part 1 강의의 내용을 정리한 내용이다.


JPA 도입 배경

JdbcTemlapte, MyBatis는 객체 그래프를 완성하는게 쉽지 않다.

 

Spring Data JDBC는 도메인주도설계 영향을 굉장히 많이 받은 프레임워크다.

- 지연로딩, 더티체크 등 영속성과 관련된 기능이 없음

- 무조건 엔티티 save를 호출해야함

 

JPA

- 객체지향 패러다임과 RDB 패러다임을 중간에서 Java Persistence API 

- 현재는 Jakarta Persistence API

- Spring은 JPA의 구현체를 Hibernate 6로 사용

 

 

JPA 특징

1. JPA 데이터베이스 스키마 생성 전략 

- create, validate, none 등

2. 영속성 컨텍스트

- JVM 레벨의 Database

- 1차캐시

- 동일성보장

- 트랜잭션을 지원하는 쓰기지연

- 변경 감지

- 지연로딩

 

3. 동등성과 동일성

- 동등성은 equals, hashcode로 비교 가능

- 동일성은 jvm레벨에서의 주소로 비교 가능

    @Test
    void identity() {
        final Station station1 = stations.save(new Station("잠실역"));
        final Station station2 = stations.findByName("잠실역");
        assertThat(station1 == station2).isTrue();
    }

과연 동일함이 어떻게 보장이 되냐

영속 컨텍스트에서는 키값이 동일한것 2개이상있을 수 없음.

그렇기 때문에 영속성 캐시는 트랜잭션이 열릴때부터 닫힐때까지 유지

 

그리고 Service에 Transactional을 안붙어도 영속성 캐시가 되는데요? 라는 생각이들 수도 있다. 위에 보면 Repository에 @Transactional이 붙어있어서 그렇다. (추후 이 부분 때문에 트러블 슈팅이 필요할 때가 있음.)

 

 

JPA 동작 원리 개념

- 쓰기지연은 한번에 모아서 실행된다.

JPA 동작 원리 실습

 

1. Persistance

위에서는 왜 insert문이 실행되었을까?

-> ID가없으면 ID를 얻고자 쓰기지연과 상관없이 insert 쿼리 실행

-> 영속성 컨텍스트에서 관리하기위해서 ID는 필수이기 때문에

 

그러면 id @generate를 주석하고 생성자로 받으면?

insert는 안나가는데 select가 나가고 있다.

이유는? save할때, 영속성 컨텍스트에서 해당 값이 DB에 있는지 없는지 체크하려고 DB에 쿼리 날리것이다.

-> 있으면 Update, 없으면 Insert 실행을 위해서

isNew의 기준은? id가 null인지 아닌지

Persistable을 implement해서 isnew 사용하면 select query가 나가지 않도록 할 수 있다.

 

 

2. Dirty Checking

  • 영속성 컨텍스트에 저장되었을때 스냅샷을 저장하고 있음
  • 커밋됬을때 비교해서 달라진게 있으면 DB에 반영
@Test
void update() {
    final Station station1 = stations.save(new Station("잠실역"));
    station1.changeName("몽촌토성역");
    station1.changeName("잠실역");
    final Station station2 = stations.findByName("몽촌토성역");
    assertThat(station2).isNotNull();
}

위 처럼 코드짜면 업데이트 쿼리 안나간다 -> 스냅샷이 있기 때문

  • dbms마다 readonly가 다다르다.
  • 하이버네이트는 readonly 가 true면 스냅샷 생성 하지않는다.
  • JVM 메모리상에서 조금이라도 효율이 있을 수 있으나 사실 힙메모리 그렇게 차지하지 않고 절약은 가능하다. (덤프는 힙메모리가 터져야 의미가있다고 생각)

 

3. 그 외 Persistence Context에 대해 알아두면 좋은 것들

영속성컨텍스트는 id를 기반으로 관리

위 시점에서 이미 update & select query가 실행됨

why? -> ID기반으로 가져오는게 아니기 때문에 JPQL 작동 (JPQL 특징)

JPQL은 영속성 거치지않고 바로 db에 직접 query

-> JPQL이 동작할때는 무조건 flush 가 발생하는데, 이유는 영속성이 없으므로 flush를 발생하지 않으면 '몽촌토성역'을 찾을수가 없는상황이기 때문이다. (위 코드 참고)

 

참고로 @JpaTest 에는 @Transactional 이 포함되어있고, 똑똑해서 어차피 rollback할거라 쓰기지연을 하지 않는다.

  • 그래서 테스트를 진행할 때, 쓰기지연되는 쿼리로그를 확인하고 싶으면 flush()를 강제호출 할 수 있다.
  • (하지만, 우리가 가비지 컬렉션도 System.gc()를 선언하지 않는 것을 권장하듯이 flush 도 동일)
  • TransactionManager, EntityManager를 통해서 flush(), clear() 사용가능.

 

프록시객체인지 디버깅 파악하는 방법은?

간단한 방법으로는 getName()으로 프록시인지아닌지 파악 가능하다.

 

'JPA.' 카테고리의 다른 글

JPA 동시성 문제 해결하기 (feat. JMeter)  (0) 2022.09.13
[Spring] JPA 동작 방식  (0) 2022.02.02