Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- Application Event
- webjar
- HttpMessageConverters
- OAuth2
- 브루트포스
- 알고리즘
- Application Runner
- 다익스트라
- @Profile
- 백트래킹
- WebApplication Type
- 스프링부트
- JPA
- Application Argument
- HATEOAS
- 리소스 서버
- application.properties
- @ConfigurationProperties
- 백기선
- 정적 리소스
- Spring Security
- 백준
- EnableAutoConfiguration
- 리소스핸들러
- JsonSerializer
- 스프링 부트
- cors
- rest api
- 외부설정
- AuthenticationPrincipal
Archives
- Today
- Total
아카이브
[스프링 데이터 JPA] 스프링 데이터 JPA 2. Entity.save() 본문
JpaRepository의 save()는 단순히 새 엔티티를 추가하는 메서드가 아닙니다
- Transient(새로운) 상태의 객체라면 EntityManager.persist()
- Detached(이미 있는) 상태의 객체라면 EntityManager.merge()
Transient인지 Detached 인지 어떻게 판단하는가?
- 엔티티의 @Id 프로퍼티가 null이면 Transient 상태로 판단하고 id가 null이 아니면 Detached 상태로 판단한다.
- @Id == null? Transient : Detached
- 엔티티가 Persistable 인터페이스를 구현하고 있다면 isNew() 메서드에 위임한다
- JpaRepositoryFactory를 상속받는 클래스를 만들고 getEntityInfomration()을 오버 라이딩해서 자신이 원하는 판단 로직을 구현할 수도 있습니다
EntityManager.persist()
- Transient : 새로 만들어진 객체
- ID가 없고
- ID 맵핑되는 데이터베이스 레코드가 전혀 없는 상태
- Hibernate, Database 둘 다 아무도 모름
- Persistent: persistenceContext 가 관리를 하는 상태
- Transient 상태의 객체를. persist() 했을 때
- 상태변화를 감지하거나 필요할 때마다 데이터베이스 sync를 하거나
EntityManager.merge()
- Detached : 한 번이라도 데이터베이스에 Persistent 상태가 됐던 객체
- 이 객체에 맵핑이 되는 레코드가 테이블에 있는 경우
- 이 객체는 ID가 있음
- ID에 맵핑이 되는 테이블 데이터가 있을 수도 있고 없으면 새로 추가
테스트 코드
@PersistenceContext
EntityManager entityManager;
/**
* save()
* 새로운 객체라면 persist()를 호출한다.
* 새로운 객체가 아닐 경우는 merge()를 호출한.
*/
@Test
public void saveAndUpdateTest() {
/**
* @Id가 null이면 Transient 상태로 판단
* */
Post post = new Post();
post.setTitle("JPA");
Post save = postRepository.save(post);// id가 없는 transient 상태이므로 persist -> insert
// post가 PersistenceContext에서 관리(캐싱)되는 상태이기 때문
assertThat(entityManager.contains(post)).isTrue();
// entityManager가 save 인스턴스도 갖고있기 때문
assertThat(entityManager.contains(save)).isTrue();
assertThat(post == save); // 같음
/**
* @Id가 not null이면 Detached 상태로 판단
* */
Post postUpdate = new Post();
postUpdate.setId(post.getId());
postUpdate.setTitle("HIBERNATE");
// 리턴받은 updatedPost가 영속화된다.
// merge에 넘긴 entity의 복사본을 만들고, 이를 persistent 상태로 변경하고 이 복사본을 리턴한다
// DB에 sync를 하기에 상태변화를 DB에 update 한다 (Id가 DB에 해당하는게 없다면 INSERT)
Post updatedPost = postRepository.save(postUpdate);// id가 있는 detached 상태이므로 merge -> update
// postUpdate.setTitle("isTransient"); // postUpdate는 Detached 상태라 PersistentContext가 관리대상이 아니라서, 실제 save가 발생하는 시점(Write Behind)에 반영되지 않는다.
updatedPost.setTitle("isPersistent"); // updatedPost는 persistent 상태이기에, write behind 시점에 이 부분이 반영된다.
assertThat(entityManager.contains(updatedPost)).isTrue(); // 영속화 O
assertThat(entityManager.contains(postUpdate)).isFalse(); // 영속화 X
assertThat(post == save); // 다름
List<Post> all = postRepository.findAll();
assertThat(all.size()).isEqualTo(1);
/**
* Best practice는 parameter로 전달한 instance 대신에
* return받은 instance를 항상 사용하는 것이다.
* */
}
save()를 호출하면
1. ID가 없는 경우에는 persist()
- 새로운 객체이기에 Transient 상태이고, persist()에 객체를 넘긴다.
- persist()에 넘겨준 객체 자체가 영속화
- save()는 항상 영속화된 객체를 리턴해준다.
2. ID가 있는 경우에는 merge()
- detached 상태이고, 전달받은 객체의 복사본을 만들어서, 이를 통해서 DB 영속화를 하고
- save()는 항상 영속화 된 객체를 리턴해준다.
Managed 객체의 장점
Hibernate, JPA가 알아서 상태변화를 감지하다가 DB Sync를 하고 가져오면 최신 상태를 가져올 수 있다
'Spring > 스프링 데이터 JPA' 카테고리의 다른 글
[스프링 데이터 JPA] 스프링 데이터 JPA 3. 쿼리 메서드 (0) | 2021.01.16 |
---|---|
[스프링 데이터 JPA] 스프링 데이터 JPA 3. 쿼리 메서드 (0) | 2021.01.16 |
[스프링 데이터 JPA] 스프링 데이터 JPA 1. JPA Repository (0) | 2021.01.16 |
[스프링 데이터 JPA] 스프링 데이터 Common 10. QueryDSL (0) | 2021.01.16 |
[스프링 데이터 JPA] 스프링 데이터 Common 9. 도메인 이벤트 (0) | 2021.01.16 |