본문 바로가기
Programming/JPA & Spring Data JPA 기초

22. 리포지터리 메서드 작성 규칙

by JKROH 2023. 4. 7.
반응형
해당 강의는 코드 위주로 진행됩니다. 실질적인 객체 처리를 담당하는 코드는 링크에서 확인하시길 바랍니다.

* DB 연동 코드 구현 방법

  • Repository 인터페이스를 상속한다.
  • 정해진 규칙에 따라 메서드를 추가한다.

 

* 식별자로 엔티티 조회

  • findById
    • T findById(Id id)
      • 없으면 null을 반환한다.
    • Optional<T> findById(ID id)
      • 없으면 empty Optional을 반환한다.
public interface UserRepository extends Repository<User, String> {
    Optional<User> findById(String email);
}

 

* 엔티티 삭제

  • delete
    • void delete(T entity)
    • void deleteById(Id id)
      • 내부적으로 findById()로 엔티티를 조회한 뒤 delete()로 삭제
  • 삭제할 대상이 존재하지 않으면 예외 발생
public interface UserRepository extends Repository<User, String> {
    Optional<User> findById(String email);
    void delete(User user);
}

Optional<User> userOpt = userRepository.findById("email@email.com");
userOpt.ifPresent(user -> {
    userRepository.delete(user);
});

 

* 엔티티 저장 메서드

  • save
    • void save(T entity)
    • T save(T entity)
public interface UserRepository extends Repository<User, String> {
    User save(User user);
    or
    void save(User user);
}

User savedUser = userRepository.save(new User(...));

User user = new User(...);
userRepository.save(user);

 

* save() 호출 시 실행 쿼리

  • save()를 호출할 때 실행되는 쿼리를 보면, 넘겨준 User 엔티티를 기반으로 select가 먼저 실행된다.
  • 그럼 select는 왜 실행될까? 이는 Repository의 구현 때문에 그런다.

 

* save() 동작 방식

  • 위 코드는 Spring Data JPA가 제공하는 save()메서드의 기본적인 구현이다.
  • 조건절에서, 해당 엔티티가 새 엔티티인지를 찾는다.
    • 새 엔티티면 persist를 한다.
    • 그렇지 않으면 merge 한다. 이 merge 때문에 select를 먼저 하게 된다.
  • 새 엔티티인지를 판단하는 기준은 다음과 같다.
    • Persistable을 구현한 엔티티는 isNew()로 판단한다.
    • @Version 속성이 있는 경우 버전 값이 null이면 새 엔티티로 판단한다.
    • 식별자가 참조 타입이면 식별자가 null 이면 새 엔티티로 판단한다.
    • 식별자가 숫자 타입이면 0이면 새 엔티티로 판단한다.

 

* Persistable 구현 예

  • 간혹 불필요한 select 쿼리를 실행하고 싶지 않거나, 원치 않는 merge가 되는 경우도 있을 수 있다.
  • 이를 방지하기 위해서는 Persistable을 구현하고 select 쿼리가 실행되지 않게 방지해야한다.
  • isNew()를 의 값에 따라서, 새로운 엔티티인지, 아닌지를 구분할 수 있다.
  • 위의 예시 코드에서는 markNotNew()에 @PrePersist 애너테이션을 붙여, 엔티티가 저장될 때, boolean isNew를 false로 바꿔 중복 저장을 방지하고 있다.
  • 새로운 엔티티를 저장할때는 isNew()에서 항상 true를 반환하기 때문에 select쿼리와 merge과정을 생략하고 즉시 persist를 진행할 수 있다.

 

* 특정 조건으로 찾기

  • findBy프로퍼티(값) : 프로퍼티가 특정 값인 대상
    • List<User> findByName(String name); : 유저 엔티티 중 name 속성 값이 파라미터 name과 같은 엔티티들의 리스트를 찾는다.
    • List<Hotel> findByGradeAndName(Grade G, String name) : AND 연산도 처리할 수 있다. 
  • 조건 비교
    • List<User> findByNameLike(String keyword)
    • List<User> findByCreatedAtAfter(LocalDateTime time)
    • List<Hotel> findByYearBetween(int from, int to)
    • LessThan, isNull, Containing, In 등 다양한 조건 비교를 사용할 수 있다.
  • findAll() : 모두 조회한다.

 

* 정리

  • 정해진 규칙에 따라 인터페이스만 작성하면 된다.
  • 주의
    • findBy 메서드를 남용하면 안된다.
    • 검색 조건이 단순하지 않으면 findBy 대신 @Query, SQL, 스펙 / QueryDSL을 사용한다.
반응형

댓글