본문 바로가기
기록/인프런

실전! 스프링 부트와 JPA 활용1 - 동적 쿼리 및 변경 감지와 병합

by 신발사야지 2023. 4. 29.

 

JPA 에서 동적 쿼리를 처리하는 방법

동적 쿼리를 해결하는 방법 .. 분기? 조건절? query string 동적 생성하기 -> 무식한 방법

JPA Criteria -> 권장하는 방법 아님 -> 실무에서 쓸만한 방법이 아님 -> 유지보수성이 0에 가까움

본인 책에도 적어놨지만 JPA Spec 에 있기 때문에 적어놓은거지 쓸만한건 아니다그

걸 바로 해결할 수 있는게 QueryDsl 이라는게 있습니다!

제가 실무할 때는 SpringBoot + Spring Data JAP + QueryDsl 은 꼭 항상 같이 가져갑니다-> 생산성 폭발

변경 감지와 병합(merge)

참고: 정말 중요한 내용이니 꼭! 완벽하게 이해하셔야 합니다.

준영속 엔티티?

영속성 엔티티는 값이 변경되면 JPA 가 알아서 commit 해주시는거 아시죠?

JPA 의 영속성 컨텍스트가 더이상 관리하지 않는 엔티티를 말해요

애매하긴 하지만, 데이터 베이스에 한 번 저장됬다가 온 객체를 준영속 엔티티라고 함DB 에서 식별할 수 있는 ID 가 있다

내가 new 로 해서 객체를 만들 수 있단 말이에요. 거기다가 ID를 넣었어요,

JPA 영속성 컨텍스트가 관리를 하지 않고 있어요.

그 객체의 값을 바꿔도 JPA 가 Update 칠 근거가 없는거에요

준영속 엔티티를 수정하는 2가지 방법

  1. 변경 감지 기능 사용
  2. 병합(merge) 사용
  • 변경 감지 기능 사용
@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());
        
        //itemRepository.save(findItem); -> 이걸 호출할 필요가 있을까요?
        // 아무것도 호출할 필요가 없어요, 찾아온건 이미 영속 상태에요
        // 값이 변경이 되면 JPA 가 Update를 해줘요
        
    }
  • 병합사용
public void save(Item item) {
        if (item.getId() == null) {
            em.persist(item);
        } else {
            em.merge(item); // update 비슷한건데 자세한건 뒤에서 자세히 설명해드릴게요
            // 머지가 대체 머지?
            // 제가 이걸 사실 잘 설명을 안 해요
            // 실무에서 이걸 쓸일이 거의 없어

        }
    }

Merge를 진짜 단순하게 설명을 드리자면

→ merge update 를 다 해주고, 영속성 엔티티를 반환해줘요

→ Merge(병합) 진짜 주의할 점, 엔티티의 모든 속성이 변경됩니다. 병합시 param 에 값이 없으면 null 로 업데이트 됩니다. → 엄청 위험합니

→ 변경감지를 써라

엔티티를 변경할 때는 항상! 변경감지! 를 사용하세요

컨트롤러에서 어설프게 Entity를 생성하지 마세요

  • 잘못된 예시
		@PostMapping("/items/{itemId}/edit")
    public String updateItem(@PathVariable(name = "itemId") Long itemId, @ModelAttribute("form") BookForm form) {

        // 이런 객체를 뭐라고 하냐면, 애매하긴 하지만, 데이터 베이스에 한 번 저장됬다가 온 객체를 준영속 엔티티라고 함
        // DB 에서 식별할 수 있는 ID 가 있다

        // 사실 업데이트는 이렇게 단발성으로 하면 안돼요
        // changePrice() 같은 update 의 기능을 담은 의미있는 메서드를 만들어야죠
        Book book = new Book();
        book.setId(form.getId());
        book.setName(form.getName());
        book.setPrice(form.getPrice());
        book.setStockQuantity(form.getStockQuantity());
        book.setAuthor(form.getAuthor());
        book.setIsbn(form.getIsbn());

        itemService.updateItem(itemId, book);

        return "redirect:/items";
    }
  • 올바른 예시
@PostMapping("/items/{itemId}/edit")
public String updateItem(@PathVariable(name = "itemId") Long itemId, @ModelAttribute("form") BookForm form) {
    itemService.updateItem(itemId, form.getName(), form.getPrice(), form.getStockQuantity());

    return "redirect:/items";
}
// 파라미터가 너무 많으면 dto를 만들어서 넘기세요

트랜잭션이 있는 서비스 계층에서 id 와 변경할 데이터를 명확하게 넘기세요

트랜잭션이 있는 서비스 계층에서 영속 상태의 엔티티를 조회하고, 엔티티의 데이터를 직접 변경하세요

트랜잭션 커밋 시점에 변경 감지가 실행됩니다.