인프런 커뮤니티 질문&답변

jhwoo님의 프로필 이미지
jhwoo

작성한 질문수

자바 ORM 표준 JPA 프로그래밍 - 기본편

좋은 강좌 항상 감사드립니다. 추가로 질문이 있습니다

해결된 질문

작성

·

228

0

안녕하세요 영한님

이전에 드렸던 질문에 대한 친절한 답변 감사드립니다

추가로 2가지 질문이 있어 질문드립니다

1. 회원테이블(Member)과 부서테이블(Department)이 있고 회원테이블에서 부서테이블을 ManytoOne으로 매핑하였습니다

     회원의 부서정보 수정 시 Client에서 회원테이블의 ID값, 부서테이블의 ID값을 전송해 주는 것으로 설계하였습니다

     서버에서 수정 시 아래의 2가지 방법이 가능한 것으로 테스트 하였습니다

     편의상 EntityManager를 em이라고 하겠습니다

       1) 방법1

            Department findDepartment = em.find(Department.class, Client가 전달한 Department ID값);

            Member findMember = em.find(Member.class, Client가 전달한 Member ID값);

            findMember.set(findDepartment)

           이때는 Select 쿼리 2개 (Department, Member), Update 쿼리 1개 (Member)가 발생합니다

      2) 방법2

            Member findMember = em.find(Member.class, Client가 전달한 Member ID값);

            findMember.set(new Department(Client가 전달한 Department ID값));

           이때는 Select 쿼리 1개 (Member), Update 쿼리 1개 (Member)가 발생합니다

      2번 방법은 Select 쿼리 1개가 줄어드는 장점이 있는듯 하지만 JPA의 취지에는 맞지 않는 방법인듯 합니다

      FK 업데이트를 위한 용도로만 2번 방법을 사용하는 것에 대한 의견이 궁금합니다

2. persist 관련 질문

       트랜젹션 시작

        Member member = new Member();

        member.setName("Member-A");

        em.persist(member);

        member.setName("Change");

       트랜젹션 끝

     

     위 코드를 JPA 입장에서 보면 Insert문 생성 후 member의 name이 변경되었으니 Insert문의 name 값만

      "Member-A" -> Change"로 해서 한번의 insert만 실행할 것이라고 예상하였는데 테스트를 해보면 항상 insert 후

      update가 실행됩니다

      ID 전략문제인거 같아 여러가지 전략을 변경해 봐도 동작은 동일합니다

      위 내용에 대한 의견 부탁드립니다

     

     

     

      

                

답변 4

1

jhwoo님의 프로필 이미지
jhwoo
질문자

감사합니다 ^^

JPA, Hibernate에 대한 이해가 부족한 상태에서 Spring Data JPA 부터 시작해서 답답한게 많았는데 영한님 강의 들으면서 그동안 막혔던 내용이 해결되는 느낌입니다

항상 감사드리며 앞으로 더 좋은 강의 부탁드립니다

0

김영한님의 프로필 이미지
김영한
지식공유자

1번은 이미 공부를 하고 있으시니, 답을 드리자면, 프록시로 조회한 엔티티를 넣어주시면 됩니다.

Department findDepartment = em.getReference(Department.class, Client가 전달한 Department ID값);

-> 이렇게 프록시로 조회하면 됩니다. 프록시로 조회해도, id 값을 가지고 있기 때문에 연관관계를 설정할 때 DB를 조회하지 않고 해당 id 값을 그대로 사용합니다.

Member findMember = em.find(Member.class, Client가 전달한 Member ID값);

findMember.set(findDepartment)

그런데 공부를 할 때는 이렇게 하지만, 사실 실무에서는 정말 극한의 성능 요청이 있지 않는 이상, 더군다나 트래픽이 많은 조회 기능도 아니고, 비즈니스 데이터를 변경하는 기능이기 때문에, 사실 요청이 그렇게 많지 않습니다. 그래서 방법1에 적어주신 것 처럼 쿼리가 한번 더 나가도, 단순 PK 기준으로 데이터 한건을 조회하는 것이기 때문에, 성능에 주는 영향이 매우 적습니다. 그래서 저는 실무에서는 방법1에 적어주신 것 과 같은 방식을 주로 사용합니다. 방법2 스타일은 사실 사용하면 안되는 방법입니다.

활용편 2편은 10월말(벌써 다음주군요!)에 오픈하려고 열심히 준비중입니다^^! 기대해주세요.

감사합니다.

0

jhwoo님의 프로필 이미지
jhwoo
질문자

답변 감사드립니다

1번은 기초편의 프록시와 활용편의 병합과 변경감지 부분을 계속해서 보고 있습니다

조금 더 공부해 보고 다시 질문드리도록 하겠습니다

활용편 2편은 언제 나오는지요?

OSIV에 대한 의견과 API 성능 최적화, DTO 사용 팁 등을 빨리 듣고 싶습니다

0

김영한님의 프로필 이미지
김영한
지식공유자

안녕하세요 jhwoo님^^

1번의 궁금증은 바로 답변을 해드릴 수도 있지만^^ 조만간 만나시는 프록시 부분을 공부하시면, 원하시는 답을 얻을 수 있을꺼에요^^! (혹시 보시고도 잘 이해가 안되시면 문의주세요 :) )

2번의 궁금증은 하이버네이트 내부 메커니즘입니다. 부연 설명을 드리자면 persist 시점에 영속성 컨텍스트에 엔티티를 넣으면서, 동시에 이 엔티티를 비교할 수 있는 스냅샷이 만들어집니다. 추가로 하이버네이트는 PERSIST 시점에 이미 INSERT SQL을 만들어 둡니다. 그리고 트랜잭션 커밋 시점에는 원본 엔티티와 스냅샷을 비교해서 변경되는 엔티티의 UPDATE 쿼리가 만들어집니다. 따라서 FLUSH를 호출할 때 쿼리가 각각 총 2번 호출됩니다.

2번의 궁금증은 사실 하이버네이트 내부 메커니즘이라서 사실 명확한 답이 없습니다(저도 이정도로 추정합니다). 이런 부분을 잘 최적화 할 수 있게 JPA를 잘 구현하면 그런 부분도 사실 INSERT 한번으로 최적화 할 수 있을 것 같은데, 이런 최적화로 가져가는 이점 보다는 명확하게, INSERT UPDATE를 분리하는 것이 더 나은 결정이라고 판단한 것으로 생각합니다. 그리고 필요하면 개발자가 em.persist() 직전에 명확하게 모든 변경 내역을 반영하고난 다음에 em.persist()를 호출해도 문제는 해결되니까요.

감사합니다^^

jhwoo님의 프로필 이미지
jhwoo

작성한 질문수

질문하기