글을 작성하게된 계기

member Entity를 생성하고 관련 테스트 코드를 작성하던 도중에,

JPA를 상속받은 MemberRepository 에서 하나의 데이터를 조회할때 하나의 함수가 있는것이 아닌,

findById  와 getReferenceById 라는 2개의 조회함수가 있다는 것을 알게되고, 어떤 상황에서 해당 함수들을 사용하는것이 좋을까 알아보기 위하여 작성하게 되었습니다.

FindById() 와 getReferenceById() 의 Method Signature와 데이터가져오는 방식

Optional<T> findById(ID id)
  • Optional<T>: 조회된 엔티티를 Optional로 감싼 반환 타입입니다. Optional은 엔티티가 존재하지 않을 수도 있는 경우에 사용됩니다.
  • ID: 조회할 엔티티의 식별자(ID) 타입입니다.
  • 조회된 엔티티 객체를 Optional로 감싸서 반환합니다.
  • CrudRepository와 JpaRepository 인터페이스에서 모두 제공되는 메소드입니다

 

public T getReferenceById(ID id)
  • T: 반환 타입으로 엔티티의 클래스 타입입니다.
  • ID: 조회할 엔티티의 식별자(ID) 타입입니다.
  • 엔티티가 영속성 컨텍스트에 없으면 EntityNotFoundException이 발생할 수 있음
  • JpaRepository 인터페이스에서만 제공됨
  • 데이터가져오는 방식
    • 지연로딩(Lazy Loading) :  엔티티를 실제로 사용할 때까지 데이터베이스 조회를 지연합니다
    • 실제 엔티티 객체가 필요한 시점에서는 프록시 객체가 아닌 실제 엔티티를 반환합니다. 처음에는 Proxy객체로 가지고 있습니다.

위 코드를 보면, 우선 getOne같은경우 단순히 T (Entity Type)만을 반환해주고, FindById()는 Optional<T> (Optional <Entity>) 타입으로 존재하는지 안하는지를 Optional 하게 제공해주고 있습니다.

테스트코드

findById()

@Test
public void testSelect(){
    Long member_no = 13L;
    Optional<Member> result = memberRepository.findById(member_no);
    System.out.println("=============================");
    if(result.isPresent()){
        Member member = result.get();
        System.out.println(member);
    }
}

결과값

    select
        m1_0.member_no,
        m1_0.member_from_social,
        m1_0.member_id,
        m1_0.member_nickname,
        m1_0.member_password 
    from
        member m1_0 
    where
        m1_0.member_no=?
=============================
Member(member_no=5, member_id=user5, member_password=123123123, member_nickname=사용자5, member_from_social=false)

 

 

getReferenceById() 

    @Test
    @Transactional
    public void testSelect(){
        Long member_no = 5L;
        Member result_get = memberRepository.getReferenceById(member_no);
        System.out.println("=============================");
        System.out.println(result_get);
    }

결과값

=============================
Hibernate: 
    select
        m1_0.member_no,
        m1_0.member_from_social,
        m1_0.member_id,
        m1_0.member_nickname,
        m1_0.member_password 
    from
        member m1_0 
    where
        m1_0.member_no=?
Member(member_no=5, member_id=user5, member_password=123123123, member_nickname=사용자5, member_from_social=false)

 

 

 

결과 차이점

 

FindById()

 

=============================

위의 "==========================" 를 보면, 

FindById()의 경우 "===================="가 findByID가 즉시 실행되고서 출력됩니다.

즉, findById가 먼저 실행됩니다.

 

getReferenceByID()

getReferenceByID() 의 경우

=============================
System.out.println(result_get);

를 호출하니, member가 필요하다는 것을 알고서 proxy 객체가 그제서야 Select문을 실행시키고 있습니다.

 

여기서 getReferenceById()의 특징이 완전하게 설명되지는 않았습니다.

만약, getReferenceById를 통해서 ID값을 가져오면 어떻게 될까요?

@Test
@Transactional
public void testSelect(){
    Long member_no = 5L;
    Member result_get = memberRepository.getReferenceById(member_no);
    System.out.println("=============================");

    System.out.println(result_get.getMember_no());
}

 

결과값

SQL이 실행되지 않았습니다.

=============================
5

이처럼, getReferenceById를 통해서 ID값만을 가져오고 해당 객체의 ID값만 접근하게 된다면, 해당 Proxy가 참조값을 가지고 있을 수 있습니다. 만약 해당 proxy가 존재하지 않는다면, EntityNotFoundException 이 발생하게 됩니다.

 

 

결론, 그래서 언제 어디서 사용하는것이 좋을까??? 

위의 차이점을 보면 알수 있듯이,

FindById() 는 무조건 해당 SQL을 곧바로 실행하고, 

getReferenceById()는 만약에 함수를 호출하고서, 실제로 그 데이터를 출력하거나 사용할때 SQL이 실행됩니다. (다만, ID값을 호출할경우 SQL이 필요하지 않습니다.)

일반적인 상황에서는 FindById()를 통해 Optional 한 값을 활용하여 예외처리를 진행하는것이 좋아보입니다.

단순히, Entity 의 ID값만 필요한 상황이라면,  getReferenceById()를 통해 Select문이 실행되는 처리를 제외할 수 있으므로 getReferenceById()를 통해 처리하는것도 고려하는것이 좋을 것 같습니다. 이러한 상황에서는, EntityNotFoundException을 피하기 위해 반드시 DB에 해당 데이터가 있는 상황이라는 것도 고려할 필요가 있습니다.

 

 

 

도움받은 글

스택오버플로우, https://stackoverflow.com/questions/24482117/when-use-getone-and-findone-methods-spring-data-jpa

스프링getReferenceById Method Signature, https://docs.spring.io/spring-data/data-jpa/docs/current/api/org/springframework/data/jpa/repository/support/SimpleJpaRepository.html#getReferenceById(ID) 

findById, getById 블로그, https://k3068.tistory.com/103

[JPA] 연관 관계를 가진 엔티티 저장 방식 개선 (불필요한 select문 제거)

https://jgrammer.tistory.com/entry/JPA-%EB%B9%84%ED%9A%A8%EC%9C%A8%EC%A0%81%EC%9D%B8-%EC%97%B0%EA%B4%80-%EA%B4%80%EA%B3%84-%EC%A0%80%EC%9E%A5-%EB%B0%A9%EC%8B%9D-%EA%B0%9C%EC%84%A0%EB%B6%88%ED%95%84%EC%9A%94%ED%95%9C-select%EB%AC%B8-%EC%A0%9C%EA%B1%B0

 

 

 

+ Recent posts