본문 바로가기

JPA

JPA 에서 GeneratedValue 를 하지않는다는것은

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 를 통해 판단로직을 넣을수있다.