ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 데이터 JPA
    김영한(인프런 강의)/실전! 스프링 데이터 JPA 2021. 2. 15. 11:58
    반응형

    코드

    github.com/rlawls1991/Study_practice_RestAPI

     

     

    스프링 데이터 JPA 전 강의를 보게 된다면 Repository를 직접 상황에 맞게 구현을 해야 한다. 하지만 스프링 JPA는 이와같은 역할을 대신해준다. 어떤식으로 대신해줄까 한번 알아보자

     

    스프링 데이터 JPA

    스프링 데이터 JPA 코드

    아래는 기본적인 스프링 JPA DATA의 코드이다. 코드를 보게 된다면  JpaRepository 인터페이스를 상속받았다.  하지만 구현체가 없는데 어떤식으로 동작이 될까? 

    import com.jpa.practice.domain.entity.Member;
    import org.springframework.data.jpa.repository.JpaRepository;
    
    public interface MemberRepository extends JpaRepository<Member, Long> {
    }

    위와같은 궁금증을 해결하기위해서 Test코드와 안에 print f을 찍어 보았다.

    @SpringBootTest
    @Transactional
    @Rollback(false)
    public class MemberRepositoryTest {
        @Autowired
        MemberRepository memberRepository;
    
    
        @Test
        public void testMember() {
    
            System.out.println("memberRepository =:>  "+ memberRepository.getClass());
            Member member = new Member("memberA");
            Member savedMember = memberRepository.save(member);
            Member findMember = memberRepository.findById(savedMember.getId()).get();
            Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
            Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
            Assertions.assertThat(findMember).isEqualTo(member); //JPA 엔티티 동일성 보장
        }
    }

    결과

    printf 결과

    위의 결과를 보게 된다면 memberRepository는 Proxy가 생성이 된다. 즉 스프링 데이터 JPA가 인터페이스만 보고 구현체를 생성한 것이다.

    해당 인터페이스의 구조는 어떻게 생겼을가? 구조를 확인해보자.

     

     

    스프링 데이터 JPA 구조

    JpaRepository는 PagingAndSortingRepository, QueryByExampleExecutor 이 두개를 상속받는다. 

    JpaRepository

    PagingAndSortingRepository는 CrudRepository를 상속받고 CrudRepository는 Repository를 상속 받는다.

    PagingAndSortingRepository
    CrudRepository

    그리고 QueryByExampleExecutor는 안쪽에 아래와같이 구현이 되어있다.

     

    public interface QueryByExampleExecutor<T> {
    
    	/**
    	 * Returns a single entity matching the given {@link Example} or {@literal null} if none was found.
    	 *
    	 * @param example must not be {@literal null}.
    	 * @return a single entity matching the given {@link Example} or {@link Optional#empty()} if none was found.
    	 * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if the Example yields more than one result.
    	 */
    	<S extends T> Optional<S> findOne(Example<S> example);
    
    	/**
    	 * Returns all entities matching the given {@link Example}. In case no match could be found an empty {@link Iterable}
    	 * is returned.
    	 *
    	 * @param example must not be {@literal null}.
    	 * @return all entities matching the given {@link Example}.
    	 */
    	<S extends T> Iterable<S> findAll(Example<S> example);
    
    	/**
    	 * Returns all entities matching the given {@link Example} applying the given {@link Sort}. In case no match could be
    	 * found an empty {@link Iterable} is returned.
    	 *
    	 * @param example must not be {@literal null}.
    	 * @param sort the {@link Sort} specification to sort the results by, must not be {@literal null}.
    	 * @return all entities matching the given {@link Example}.
    	 * @since 1.10
    	 */
    	<S extends T> Iterable<S> findAll(Example<S> example, Sort sort);
    
    	/**
    	 * Returns a {@link Page} of entities matching the given {@link Example}. In case no match could be found, an empty
    	 * {@link Page} is returned.
    	 *
    	 * @param example must not be {@literal null}.
    	 * @param pageable can be {@literal null}.
    	 * @return a {@link Page} of entities matching the given {@link Example}.
    	 */
    	<S extends T> Page<S> findAll(Example<S> example, Pageable pageable);
    
    	/**
    	 * Returns the number of instances matching the given {@link Example}.
    	 *
    	 * @param example the {@link Example} to count instances for. Must not be {@literal null}.
    	 * @return the number of instances matching the {@link Example}.
    	 */
    	<S extends T> long count(Example<S> example);
    
    	/**
    	 * Checks whether the data store contains elements that match the given {@link Example}.
    	 *
    	 * @param example the {@link Example} to use for the existence check. Must not be {@literal null}.
    	 * @return {@literal true} if the data store contains elements that match the given {@link Example}.
    	 */
    	<S extends T> boolean exists(Example<S> example);
    }

    JpaReposotory를 기본적인 필요한 기능이 구현이 되어있다는 것을 알 수 있다.

     

    이 위의 부분을 다이어그램으로 표현을 하게되면 아래와 같다.

    스프링 JPA 구조

    메소드 설명

    save(S) 

    • 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합한다.

    delete(T)

    • 엔티티 하나를 삭제한다. 내부에서 EntityManager.remove() 호출

    findById(ID)

    • 엔티티 하나를 조회한다. 내부에서 EntityManager.find() 호출

    getOne(ID)

    • 엔티티를 프록시로 조회한다. 내부에서 EntityManager.getReference() 호출

    findAll(…)

    • 모든 엔티티를 조회한다. 정렬( Sort )이나 페이징( Pageable ) 조건을 파라미터로 제공할 수 있다.

    참고

    JpaRepository 대부분의 공통 메서드를 제공한다

     

    다음은 스프링 데이터 JPA가 어떻게 구현는지 확인해 보자.

     

    스프링 데이터 JPA가 구현 클래스 대신 생성 설명

    Repository구현

     

    위의 그림을 보게 된다면 스프링 데이터 JPA 구조 및 코드에서 print 결과를 연관시켜 보자. 

    스프링 데이터 JPA는 inteface형태의 Repository를 읽어 프록시 구현클래스를 생성해주는 것이다.

    더 쉽게 말하면 

    import org.springframework.data.jpa.repository.JpaRepository;

     

    위의 코드를 import를 하게 되면 장점이 몇가지가 더있는데 아래와 같다.

    • 컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리
    • JPA 예외를 스프링 예외로 변환하는 과정도 자동으로 처리

     

     

     

    반응형
Designed by Tistory.