- 준영속Entity의 경우 (준영속Entity는 em.find를 한 것이 아닌, DB에는 존재하지만 순간적으로 자바에서 객체로만 관리되어져 EntitnyManager 1차캐시에 등록X 객체 > Update할 경우 2가지 방법이 존재한다.
- 변경 감지를 수동으로 사용하는 법 (영속 Entitny로 등록해줘서 dirtyChecking으로 수정되게 하는 법)
변경 감지는 따로 updateMethod를 파서 해당 id로 em.find를 시켜 영속성을 만들어 1차캐시에 저장하고, 이후 그 객체를 변경해주면 이후 tx 커밋시에 자동 등록된다.
merge도 이와 비슷한 맥락인데, 1차 캐시에서 찾다가 없으면 db에서 꺼내온다. 여기서 영속성이 생기고, 이후 찾아온 객체에 merge(parameter)로 받은 파라미터에서
모조리 set 해주어 변경감지가 되게 만들어준다.
그런데 문제는 param에 넘기는 객체에 특정 변수의 값이 없을 경우, null로 update한다. 따라서 merge에 보내는 변경을 원하는 param 객체에는
변경을 원하는 변수를 제외한 모든 기존 변수의 값이 setting 되어있는 상태여야 한다.
위험하니까 그냥 스스로 변경감지를 만들도록 하자. -
- 직접 merge()를 사용하는 법이다.
결론 : Update한답시고 어설프게 Controller에서 객체 new 하지말고, service에서 find해서 merge 대신 변경감지로 update치자.
트랜잭션이 있는 서비스 계층에 식별자 (id
)와 변경할 데이터를 명확하게 전달하자(파라미터 or dto)
- 수동 set set 으로 하는게 좋다.
@Test
public void updateTest() throws Exception {
Book book = em.find(Book.class, 1L);
book.setName("asdfasd");
//변경감지 == dirty checking
//em.find시에 해당 book을 영속성 관리하고,
//book의 Name 변경을 감지해서 추후 flush()시에 변경해준다.
/**
* but, 준영속 엔티티인경우 문제가 된다.
* 실재로 DB에 갔다 와서, 영속성 컨텍스트가 더는 관리하지 않는 엔티티
* DB에 한번 저장되어 식별자가 존재한다. 임의로 만들어낸 엔티티도 기존 식별자를 가지고
* 있으면 준영속 엔티티로 볼 수 있다.
* ex Book book = new Book();
* book.setId(form.getId(); 등으로 생성되면, DB엔 있지만 EntityManager가 모르는 아이
*/
/**
* 이런 준영속 Entiy를 수정하는 2가지 방법
* 1.변경 감지를 사용하는 법
* @Transactional
* public void updateItem(Long itemId, Book param) {
* Item findItem = itemRepository.findOne(itemId);
* findItem.setPrice(param.getPrice());
* findItem.setName(param.getName());
* findItem.setStockQuantity(param.getStockQuantity());
*
* }
* 2. merge를 사용하는것(비추)
* Book book = new Book();
* book.setId(form.getId();
* em.merge(item);
*
* merge 동작 순서
* 1.merge(파라미터) 파라미터로 넘어온 준영속엔티티 식별자 값으로 1차 캐시에서 Entity를 조회한다
* 2.만약 1차 캐시에서 엔티티가 없으면 db에서 조회, 1차캐시에 저장
* 3. 조회한 영속 엔티티에 파라미터로 받은 값을 채워 넣는다. (여기서 변경된 값 set)
* 4. 영속 상태인 채워진 엔티티를 return 한다.
* 5. 결과적으로 영속상태의 수정된 Entity가 나중에 flush될 때 dirtyChecking이 된다.
*
* * why? 사용하지 말라고 하는 것인가?
* 위에 변경감지법과 똑같은 코드다.
* 결국 id로 찾아서 parameter로 merge에 넘긴 값으로
* 찾아온 것의 값을 다 바꿔치기한 뒤, 변경 감지 시키는 법이다.
*
* 기존 merge의 Param으로 넣은 것과, merge()가 return한 객체는 다른 객체다.
* 주의 !// 병합을 사용하면 모든 속성을 교체한다.
* 병합시 param으로 넘긴 값으로 모두 갈아치워서 update치기 때문에,
* param으로 넘긴 객체에 값이 없으면 tx commit시에 null로도 교체가 된다.
* 안됨 안됨!!
*
* 결론 : Update한답시고 어설프게 Controller에서 객체 new 하지말고, service에서 find해서 merge 대신 변경감지로 update치자.
* 수동 set set 으로 하는게 좋다.
* /
}
'스프링 > JPA' 카테고리의 다른 글
[JPA 기초] API 사용시 DTO로 변환하는 이유 + 주의사항 (DTO 내부에 Entity객체가 없어야 한다) (0) | 2022.04.05 |
---|---|
[JPA 기초] OSIV (Transaction 생존 범위에 따른 성능, 대처에 대해) (0) | 2022.04.05 |
[JPA 기초] 기본 Entity 설정시 사용되는 annotation 정리 (0) | 2022.04.05 |
Query By Example 간단 예제, 사용 (0) | 2022.03.01 |
`SpringDataJPA` 기본 save 작동 과정 (merge와 persist를 중심으로 효율성 개선) (0) | 2022.02.27 |