카테고리 없음

JpaRepository.class 코드 분석(삭제 예정)

EVO. 2023. 7. 13. 23:32

코드는 다음과 같습니다.

package org.springframework.data.jpa.repository;

import java.util.List;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Sort;
import org.springframework.data.repository.ListCrudRepository;
import org.springframework.data.repository.ListPagingAndSortingRepository;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.query.QueryByExampleExecutor;

@NoRepositoryBean
public interface JpaRepository<T, ID> extends ListCrudRepository<T, ID>, ListPagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
    void flush();

    <S extends T> S saveAndFlush(S entity);

    <S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

    /** @deprecated */
    @Deprecated
    default void deleteInBatch(Iterable<T> entities) {
        this.deleteAllInBatch(entities);
    }

    void deleteAllInBatch(Iterable<T> entities);

    void deleteAllByIdInBatch(Iterable<ID> ids);

    void deleteAllInBatch();

    /** @deprecated */
    @Deprecated
    T getOne(ID id);

    /** @deprecated */
    @Deprecated
    T getById(ID id);

    T getReferenceById(ID id);

    <S extends T> List<S> findAll(Example<S> example);

    <S extends T> List<S> findAll(Example<S> example, Sort sort);
}

 

이 인터페이스는 JpaRepository라는 이름의 인터페이스 입니다. 이 인터페이스는 일반적으로 Spring Data JPA에서 사용하는 주요 인터페이스 중 하나입니다.

ListCrudRepository

ListPagingAndSortingRepository

QueryByExampleExecutor

이 세가지 인터페이스를 확장하고 있습니다. 

코드 설명


void flush()

영속성 컨텍스트의 변경사항을 즉시 데이터베이스에 동기화 합니다. 이 작업은 보통 트랜잭션 종료 (commit)시 자동으로 발생하지만 때로는 중간에 수동으로 동기화해야 하는 경우가 있습니다. 


<S extends T> S saveAndFlush(S entity)

먼저 <S extends T>의 의미 부터 알아야 합니다.

 

 

https://st-lab.tistory.com/153

 

자바 [JAVA] - 제네릭(Generic)의 이해

정적언어(C, C++, C#, Java)을 다뤄보신 분이라면 제네릭(Generic)에 대해 잘 알지는 못하더라도 한 번쯤은 들어봤을 것이다. 특히 자료구조 같이 구조체를 직접 만들어 사용할 때 많이 쓰이기도 하고

st-lab.tistory.com

상단 블로그에서 해당 키워드(제한된 Generic(제너릭)과 와일드 카드)로 검색하시면 자세히 <S extends T>에 대해서 배울 수가 있습니다.  아무튼 다음 내용을 배우고 파악한 바로는 

T => 보통은 제가 만든 Domain이 전달됩니다. (예를들면 Employee) 

S => T와 T의 자손 타입만 가능하므로 Employee와 Employee의 자손 즉 상속받은 클래스들만 가능합니다. 

그렇다면 

해당 T또는 T의 자식클래스의 타입을 받고 그 자식 클래스를 반환하는 선언이라고 볼수 있습니다. 

 

JDK17에서는 해당 메서드가 주석으로 잘 설명이 되어있습니다.

 

Saves an entity and flushes changes instantly.(엔티티를 저장하고 변경 사항을 즉시 플러시합니다.)
Params:
entity – entity to be saved. Must not be null. (저장할 엔티티입니다. null이 아니어야 합니다.)
Returns:
the saved entity (저장된 엔티티)

 

여기에 대해 구체적인 구현방식은 JPA내부에 동작원리이므로 복잡하기도하고 지금당장 알필요가 없으므로 넘기겠습니다.

Params와 리턴값만 알고있으면 됩니다.

 

 


<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);

모든 엔티티들을 저장하고 바로 flush시킵니다. 그리고 저장된 엔티티들을 다시 리스트형태로 반환합니다. 


void deleteAllInBatch(Iterable<T> entities);

주석 설명 먼저 보면 

지정된 엔티티를 일괄 삭제하여 단일 쿼리를 생성합니다. 
이러한 종류의 작업은 JPA의 첫 번째 레벨 캐시와 데이터베이스를 동기화하지 않게 만듭니다. 이 메서드를 호출하기 전에 EntityManager를 플러시하는 것이 좋습니다.

 

설명을 보면 EntityManager에 있는 1차 캐시에 저장된 엔티티들을 모두 제거하라고 만든 메서드 인 것 같습니다. 

하지만 정확히 잘 모르겠으니 이 인터페이스를 구현한 SimpleJpaRepository에서 좀더 살펴보겠습니다.

 

@Override
@Transactional
public void deleteAllInBatch(Iterable<T> entities) {

   Assert.notNull(entities, "Entities must not be null");

   if (!entities.iterator().hasNext()) {
      return;
   }

   applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em)
         .executeUpdate();
}

 

먼저 삭제할 엔티티들이 있는지 Assert.notNull을 통해서 null인지 확인 합니다.

 

두번째로 보통 인자에 리스트로 들어온다 가정하면  iterator로 들어있는 인자를 확인하는데 만약 존재하지 않으면 그냥 리턴시킵니다.

 

세번째로 조금 복잡해보이니 쪼개서 생각해 봅시다.

getQueryString은 String을 포멧 시켜주고 첫번째 인자에는 형식 지정자가 들어간 template, 두번째 인자에는 엔티티를 받고 있습니다. 

 

첫번째 인자에는 

public static final String DELETE_ALL_QUERY_STRING = "delete from %s x"

 

두번째 인자에는

entityInformation.getEntityName()

이 들어가는데 엔티티이름을 반환합니다. 어떤걸 반환하는지 정확히는 모르고 나중에 배울기회가 있으면 다시 설명해보도록 하겠습니다.