ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JPA 에서 GeneratedValue 를 하지않는다는것은
    JPA 2022. 9. 3. 16:56
    test

    JPA 에서 Entity 를 생성하여

    PK 를 잡는 ID 를 지정할때

    @Id @GeneratedValue
    @Column(name = "order_id")
    private Long id;
    
    private String title;
    
    @ManyToOne(fetch = LAZY)
    @JoinColumn(name = "member_id")
    private Member member;
    

    위와같이 GeneratedValue 를 통해 자동생성을 둘수도있지만

    String 와 같은 다른 타입 PK 로지정이 가능하다.

    GeneratedValue 가 아닌 다른 타입으로 지정한다는것은 결국

    데이터를 추가할때 PK를 미리 지정해준다는 의미일것이다.

    GeneratedValue 의 동작

    GeneratedValue 는 Entity 객체를 생성후 Repository 에 저장하면

    Entity 객체에 ID 가 생긴다.

    JPA 의 구현체인 SimpleJpaRepository 의 save 로직을 확인해보면

    @Transactional
    @Override
    public <S extends T> S save(S entity) {
    
    	Assert.notNull(entity, "Entity must not be null.");
    
    	if (entityInformation.isNew(entity)) {
    		em.persist(entity);
    		return entity;
    	} else {
    		return em.merge(entity);
    	}
    }
    

    em.persist(entity); 가 동작시 Entity의 ID 에 정책으로 지정된 자동생성 값을 주입하게된다.

    persist 와 merge

    위의 save 로직을 보면 persist 와 merge 의 로직을 분기하는 기준은 isNew() 메소드를 통해 분기되고있음을

    확인할수있다.

    isNew 의 기준은 Entity의 ID 가 기본값 이거나 객체일경우 NULL 인경우 true 를 반환하게된다.

    그렇다는것은 GeneratedValue 로 지정된 ID 는 기본값이 거나 NULL 일것이기 때문에

    isNew() 가 동작하여 persist 가 동작하게 된다는것이다.

     

    하지만 문제는 merge 이다.

    만약 GeneratedValue 로 지정되지 않아 ID 값을 할당한 상태로 save를 하게된다면

    merge 는 기존의 Entity 와 변경된 Entity 를 하나로 병합하는과정으로

    해당 로직이 동작하게되면 기존의 Entity를 가지고 와서 병합하기위해 select 쿼리가 동작하게된다.

    즉 병합 상태(업데이트) 가 아닌데도 병합을 하기위해 기존의 데이터를 가지고 오려는 문제가 발생한다.

    정말로 업데이트 하려면?

    만약 정말 기존에 있던 Entity 를 업데이트 하기위해 merge 를 호출하게된다면

    업데이트할 객체를 가지고 오기위해 select 쿼리가 또 나가는거 아닌가 라는 의문에서는 merge 를 호출하면 또 select 쿼리가 발생하는것이 맞다.

    따라서 업데이트를 할때는 영속성 컨텍스트에서 더티체킹으로 업데이트를 처리해야한다.

    그럼에도 GeneratedValue 를 사용할수 없을때

    위에서 본것처럼 GeneratedValue 를 사용해야한다는것을 알아도 사용할수없을때에는

    Persistable 인터페이스를 구현해서 isNew 의 판단로직을 변경해야한다.

    @Entity
    public class Item implements Persistable<String> {
        @Id
        private String id;
    
        @Override
        public String getId() {
            return id;
        }
    
        @Override
        public boolean isNew() {
            return false;
        }
    }
    

    getId 에는 ID 를 반환하면 되고

    isNew 에는 새로 생기는 객체인지 (true), 업데이트 객체인지 (false) 를 반환하면된다.

    그런데 이렇게 하면 isNew 를 구현하기가 까다로울것이다

    public class JpaItem implements Persistable<String> {
        @Id
        private String id;
    
        @CreatedDate
        private LocalDateTime createdDate;
        
        @Override
        public String getId() {
            return id;
        }
    
        @Override
        public boolean isNew() {
            return createdDate == null;
        }
    }
    

    따라서 위와같이 CreatedDate 를 통해 판단로직을 넣을수있다.