- 새로운 엔티티를 판단하는 기본 전략
- 식별자가 객체일 때 `null`로 판단
- 식별자가 자바 기본 타입일 때 `0`으로 판단
- `Persistable` 인터페이스를 구현해서 판단 로직 변경이 가능
Item Class를 하나 만들고
// Item Class
package study.datajpa.entity;
import lombok.Getter;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@Getter
public class Item {
@Id
@GeneratedValue
private Long id;
}
ItemRepository Interface도 만들어주고
package study.datajpa.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import study.datajpa.entity.Item;
public interface ItemRepository extends JpaRepository<Item, Long> {
}
MemberController에서 아래 PostConstruct를 주석처리 해주고
// MemberController Class
// @PostConstruct
public void init() {
for (int i = 0; i < 100; i++) {
memberRepository.save(new Member("user" + i, i));
}
}
}
test를 돌리면 영상과 같이 debug결과가 나오지 않는다.
해결 SimpleJpaRepository에서 suspend point를 찍어줘야 한다.
// ItemRepositoryTest
package study.datajpa.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import study.datajpa.entity.Item;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
@Test
public void save() {
Item item = new Item();
itemRepository.save(item);
}
}
// MemberTest에서도 아래 3개를 주석처리 해주고
// then
System.out.println("findMember.createdDate = " + findMember.getCreatedDate());
// System.out.println("findMember.updatedDate = " + findMember.getLastModifiedDate());
// System.out.println("findMember.createdBy = " + findMember.getCreatedBy());
// System.out.println("findMember.updatedBy = " + findMember.getLastModifiedBy());
}
그러면 debug 결과값에서 id가 null인 것을 확인할 수 있다.
persist하고 entity를 return 하면 이제 id 값이 1이 되는 것을 확인 할 수 있다.
item Class를 작성해주고
// item Class
package study.datajpa.entity;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item {
@Id
private String id;
// pk의 값이 존재한다.
public Item(String id) {
this.id = id;
}
}
ItemRepositoryTest를 작성해주고 돌리게되면
// ItemRepositoryTest
package study.datajpa.repository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import study.datajpa.entity.Item;
import static org.junit.jupiter.api.Assertions.*;
@SpringBootTest
class ItemRepositoryTest {
@Autowired
ItemRepository itemRepository;
@Test
public void save() {
// Item쪽으로 가보면 pk의 값이 존재
Item item = new Item("A");
itemRepository.save(item);
}
}
ItemRepositoryTest에서 Test를 돌리고
@Test
public void save() {
Item item = new Item("A");
itemRepository.save(item);
}
SimpleRepository에서 suspend point를 찍은 점에서 step over를 한번 누르게 되면 new Entity가 아니므로 id값이 a로 나오게 된다.
db에서 a를 찾고(없으면 새거라 판단하고 넣게된다.) insert문을 넣게 된다.
아무튼 merge는 최대한 쓰지말자
- 데이터에 대한 변경은 변경감지로
- 데이터에 대한 저장은 persist를 쓰자.
select item0_.id as id1_0_0_ from item item0_ where item0_.id='A';
insert
into
item
(id)
values
(?)
Persistable이라는 implementation을 써보자.
// Item Class
package study.datajpa.entity;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.domain.Persistable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.time.LocalDateTime;
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Item implements Persistable<String> {
@Id
private String id;
// JPA insert persist가 되기전에 호출이 된다.
@CreatedDate
private LocalDateTime createdDate;
public Item(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public boolean isNew() {
// 새거에 대한 로직을 직접 짜야한다.
// return false;
return createdDate == null;
}
}
return entity할때 createdDate가 들어온다.(Jpa event안에서 동작)
정리를 하면
- JPA 식별자 생성 전략이 @GeneratedValue면 save()호출 시점에 식별자가 없고 새로운 엔티티로 인식해서 정상 동작을 한다.
- 그런데 JPA 식별자 생선 전략이 @Id만 사용해서 직접할당이 되면
- 식별자 값이 있는 상태로 save()를 호출
- 이 경우 merge()가 호출
- merge()는 우선 DB를 호출해서 값을 확인 => DB에 값이 없다 그러면 새로운 엔티티로 인지 => 매우 비효율적
- Persistable을 사용해서 엔티티 확인 여부를 직접 구현하는게 효과적
- 등록시간@CreatedDate을 조합해 사용하면 여기 필드로 새로운 엔티티 여부를 편리하게 확인이 가능(@CreatedDate에 값이 없으면 새 엔티티로 판단.)
<출처 김영한: 실전! 스프링 데이터 JPA >
'Spring > SpringDataJPA' 카테고리의 다른 글
Query By Example (0) | 2022.04.19 |
---|---|
Specifications(명세) (0) | 2022.04.19 |
스프링 데이터 JPA 구현체 분석 (0) | 2022.04.18 |
Web 확장 - 도메인 클래스 컨버터 & 페이징과 정렬 (0) | 2022.04.18 |
사용자 정의 리포지토리 구현 (0) | 2022.04.18 |