ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Entity의 Id 생성 전략에 따른 EntityManager의 persist 동작 확인
    프레임워크/Spring Boot 2024. 2. 6. 19:28

     

    소스코드: https://github.com/blog-example/-JPA-_persist_with_how_to_generate_id

    (예제에서는 Spring boot web, jpa, lombok, connectorj를 의존성으로 사용하고 있습니다.)

     

    EntityManager의 persist의 동작이 예상하던 것과 다르게 작동하는 경우를 만나서 검증을 해보았습니다.

    예제 사용될 간단한 Entity를 소개합니다.

    import jakarta.persistence.*;
    import lombok.Getter;
    
    
    @Getter
    @Entity
    @Table(name = "artists")
    public class Artist {
    
      @GeneratedValue(strategy = GenerationType.IDENTITY)
      @Id
      @Column(name = "artist_id")
      private long artistId;
    
      @Column(name = "name")
      private String name;
    
      public Artist(String name) {
        this.name = name;
      }
    }

     


     

    1. 예상과 다르게 작동하는 persist 메소드

    Artist를 DB에 저장하는 작업을 하는 중, persist의 작동이 예상과 다른 것을 확인했습니다.

    @Slf4j
    @RequiredArgsConstructor
    @Service
    public class ArtistService {
    
      @PersistenceContext
      private final EntityManager entityManager;
    
      @Transactional
      public void createArtist() {
        Artist artist = new Artist("New Jeans");
        log.info("a_id1: >> " + artist.getArtistId());
        entityManager.persist(artist);
        log.info("a_id2: >> " + artist.getArtistId());
      }
    
    }

     

    persist는 영속화를 하는 것이고 실제 데이터베이스에 쿼리가 나가는 시점은 Transaction이 끝나는 시점 혹은 flush가 발생하는 시점이라고 이해하고 있었기 때문에 로그에는 a_id1 -> a_id2 -> insert  순으로 찍힐 것을 예상하였습니다.

     

    하지만 예상과 다른 순서로 찍히는 로그를 확인하게 되어서 글을 시작하였습니다 ㅎㅎ

     

     

    2. @GeneratedValue 

    원인은 Entity의 id 컬럼에 적용한 @GeneratedValue 어노테이션에 있었습니다.

    먼저 해당 어노테이션을 제거하고 확인해보겠습니다.

    @Getter
    @Entity
    @Table(name = "artists")
    public class Artist {
    
      @Id
      @Column(name = "artist_id", nullable = true)
      private long artistId;
    
      @Column(name = "name")
      private String name;
    
      public Artist(String name) {
        this.name = name;
      }
    }

     

    어노테이션을 제거하니 처음에 예상했던 순서로 작동하는 것을 확인할 수 있습니다.

    발생하는 원인은 찾았으니 이제 왜 그러는지를 확인해보겠습니다.

     

     

     

     

    3. Id 생성 전략

    엔티티의 id 쓰기 전략에 따라서 persist가 다르게 작동되도록 구현이 되어있습니다.

     

    • GenerationType.IDENTITY
      mysql에서 사용되는 이 전략은 데이터베이스에서 정해주는 id를 사용하도록 하기 때문에, '지연쓰기'를 하지 않고 '바로쓰기'를 통해서 persist가 실행되는 시점에 insert 쿼리를 요청해서 id를 받아옵니다.
      따라서 a_id2 쿼리가 찍히기 전에 insert 쿼리가 발생합니다.

     

    • GenerationType.SEQUENCE
      sequence 타입을 선택하는 경우에는 insert 쿼리가 flush 혹은 트랜잭션이 끝나는 시점에 나가기 때문에 '지연쓰기'가 유지됩니다. 하지만 persist를 하는 시점에 id를 미리 가져오기 위한 select 쿼리가 추가로 발생합니다.

     

     

    3.  마무리

    기본적으로 persist는 flush가 실행되는 시점이나 트랜잭션이 끝는 시점에 데이터베이스에 쿼리를 전달하는 '지연 쓰기' 방식을 택하고 있으나 id 생성 전략에 따라서 '바로 쓰기'를 수행하는 경우도 있습니다.

     

     

     

Designed by Tistory.