JPA

[JPA] update가 왜 안되는거지..? (detached to persistent)

코리늬 2020. 7. 21. 23:54

문제의 메소드

@PostMapping("/settings/profile")
    public String updateProfile(@CurrentUser Account account, @Valid Profile profile, Errors errors, Model model){
        if(errors.hasErrors()){
            model.addAttribute(account);

            return "/settings/profile";
        }

        //데이터 변경 작업은 서비스 쪽에 위임
        accountService.updateProfile(account, profile);

        //사용자의 form submit이 다시 일어나지 않도록 redirect
        return "redirect:/" + "settings/profile";
    }

분명히 updateProfile을 서비스단에서 @Transactional 의 관리하에 업데이트를 하였는데 실제로는 업데이트가 되지않았다..!!

왜냐하면, 현재 Account 파라미터는 앞서 세션에서 사용했던 Principal 인증정보이기 때문에 Detached 상태이다.

  • Transaction이 끝난지 오래다.

그래서 값을 올바르게 넣었음에도 변경이 되지 않는것이다.

 

정답을 알려줘..!

public void updateProfile(Account account, Profile profile) {
        account.setBio(profile.getBio());
        account.setUrl(profile.getUrl());
        account.setOccupation(profile.getOccupation());
        account.setLocation(profile.getLocation());
        accountRepository.save(account);
    }

Detached 상태인 account객체를 save를 해주면, 결과적으로 merge 가 동작하여 persistent 상태가 되기 때문에 업데이트가 정상적으로 된다.

save()메소드의 내부 로직을 보면

@Transactional
    public <S extends T> S save(S entity) {
        if (this.entityInformation.isNew(entity)) {
            this.em.persist(entity);
            return entity;
        } else {
            return this.em.merge(entity);
        }
    }

새로운 entity일 경우 해당 entity를 영속 상태로 만들고 그 엔티티를 리턴하지만, 그렇지 않을경우 merge를 하게된다.

그래서 업데이트가 정상적으로 되는것이다.

 

의문점

어,, 그럼 이게 결국 dirtyChecking이 된건가??

최종적으로 업데이트 된 모습이 나는 dirtyChecking과 상당히 유사하다고 생각이 되어서 매우 헷갈렸다.

한참을 찾아본 끝에 답을 얻을 수 있었다.

  • dirtyChecking은 persistent 상태에 있는 엔티티에 대해서만 적용된다.
  • 따라서 detached 상태인 account는 dirtyChecking으로 인한 업데이트가 아니고 merge에 의한 업데이트가 맞는것이다.