-
JPA 에서 GeneratedValue 를 하지않는다는것은JPA 2022. 9. 3. 16:56test
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 를 통해 판단로직을 넣을수있다.
'JPA' 카테고리의 다른 글
@Transactional(readOnly = true) 는 Flush 가 동작안할꺼라 믿었다 (0) 2024.04.23 QueryDSL에서 SubQuery 에는 limit 를 사용 할 수 없다. (0) 2023.11.15 QueryDSL-JPA에서 인라인뷰를 써야 한다면 도망치자 (0) 2023.10.18 JPA Column의 null 제약 조건 (0) 2023.02.07 [JPA] JPA 란 (0) 2020.12.08