리포지토리 인터페이스 설계
Spring Data JPA는 JpaRepository를 기반으로 더욱 쉽게 데이터베이스를 사용할 수 있는 아키텍처를 제공합니다. 스프링 부트로 JpaRepository를 상속하는 인터페이스를 생성하면 기존의 다양한 메서드를 손쉽게 활용할 수 있습니다.
리포지토리 인터페이스 생성
여기서 이야기하는 리포지토리(Repository)는 Spring Data JPA가 제공하는 인터페이스입니다. 엔티티를 데이터베이스의 테이블과 구조를 생성하는 데 사용했다면, 리포지토리는 엔티티가 생성한 데이터베이스에 접근하는 데 사용됩니다.
리포지토리를 생성하기 위해서는 접근하려는 테이블과 매핑되는 엔티티에 대한 인터페이스를 생성하고, 아래 예제와 같이 JpaRepository를 상속받으면 됩니다.
public interface ProductRepository extends JpaRepository<Product, Long> {
}
이때 리포지토리 인터페이스는 data.repository 패키지를 생성한 후 아래와 같이 해당 패키지에 생성하면 됩니다.
ProductRepository가 JpaRepository를 상속받을 때는 반드시 대상 엔티티와 기본키값(pk)의 타입을 적어주면 됩니다.
생성된 리포지토리는 아래예제와 같이 JpaRepository를 상속받으면서 별도의 메서드 구현 없이도 많은 기능을 제공합니다. 즉, 따로 작성없이 바로 사용가능한 기본 메소드들이라고 보면 됩니다.
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>,QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort sort);
List<T> findAllById(Iterable<ID> ids);
<S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
<S extends T> List<S> saveAllAndFlush(Iterable<S> entities);
@Deprecate
default void deleteInBatch(Iterable<T> entities) {
this.deleteAllInBatch(entities);
}
void deleteAllInBatch(Iterable<T> entities);
void deleteAllByIdInBatch(Iterable<ID> ids);
void deleteAllInBatch();
@Deprecated
T getOne(ID id);
T getById(ID id);
<S extends T> List<S> findAll(Example<S> example);
<S extends T> List<S> findAll(Example<S> example, Sort sort);
}
JpaRepository의 상속 구조를 보면 아래와 같습니다.
위 그림에서 타 리포지토리를 상속받고 있는 리포지토리 인터페이스에는 위 예제와 같이 각 메서드가 구현돼 있습니다. 타 리포지토리에 만들어진 메서드는 모두 앞에서 생성한 ProductRepository에서도 사용할 수 있습니다.
리포지토리 메서드의 생성 규칙
리포지토리에서는 몇 가지 명명규칙에 따라 커스텀 메서드도 생성할 수 있습니다. 일반적으로 CRUD(Create, Read, Update, Delete)에서 따로 생성해서 사용하는 메서드는 대부분 Read 부분에 해당하는 Select 쿼리 밖에 없습니다. 엔티티를 저장하거나 갱신 또는 삭제할 때는 별도의 규칙이 필요하지 않기 때문입니다. 다만 리포지토리에서 기본적으로 제공하는 조회 메서드는 기본값으로 단일 조회하거나 전체 엔티티를 조회하는 것만 지원하고 있기 때문에 필요에 따라 다른 조회 메서드가 필요합니다.
메서드에 이름을 붙일 때는 첫 단어를 제외한 이후 단어들의 첫 글자를 대문자로 설정해야 JPA에서 정상적으로 인식하고 쿼리를 자동으로 만들어줍니다. 조회 메서드(find)에 조건으로 붙일 수 있는 몇 가지 기능을 소개하면 다음과 같습니다.
FindBy : SQL문의 where 절 역할을 수행하는 구문입니다. findBy 뒤에 엔티티의 필드값을 입력해서 사용합니다.
// findBy : 엔티티 필드 값으로 조회
List<User> findByName(String name);
// 사용 예제
List<User> users = userRepository.findByName("John");
AND, OR : 조건을 여러 개 설정하기 위해 사용합니다.
// AND, OR : 조건을 여러 개 설정
List<User> findByNameAndEmail(String name, String email);
List<User> findByNameOrEmail(String name, String email);
// 사용 예제
List<User> usersAnd = userRepository.findByNameAndEmail("John", "john@example.com");
List<User> usersOr = userRepository.findByNameOrEmail("John", "john@example.com");
List/NotLike : SQL문의 like와 동일한 기능을 수행하며, 특정 문자를 포함하는지 여부를 조건으로 추가합니다. 비슷한 키워드로 Containing, Contains, isContaing이 있습니다.
// Like/NotLike : 특정 문자열 포함 여부 조회
List<User> findByNameLike(String pattern);
List<User> findByNameNotLike(String pattern);
// 사용 예제
List<User> usersLike = userRepository.findByNameLike("%John%");
List<User> usersNotLike = userRepository.findByNameNotLike("%John%");
StartsWith/StartingWith : 특정 키워드로 시작하는 문자열 조건을 설정합니다.
// StartsWith/StartingWith : 특정 키워드로 시작
List<User> findByNameStartingWith(String prefix);
// 사용 예제
List<User> usersStarting = userRepository.findByNameStartingWith("Jo");
EndsWith/EndingWith : 특정 키워드로 끝나는 문자열 조건을 설정합니다.
// EndsWith/EndingWith : 특정 키워드로 끝남
List<User> findByNameEndingWith(String suffix);
// 사용 예제
List<User> usersEnding = userRepository.findByNameEndingWith("n");
IsNull/IsNotNull : 레코드 값이 Null이거나 Null이 아닌 값을 검색합니다.
// IsNull/IsNotNull : Null 여부 확인
List<User> findByEmailIsNull();
List<User> findByEmailIsNotNull();
// 사용 예제
List<User> usersNull = userRepository.findByEmailIsNull();
List<User> usersNotNull = userRepository.findByEmailIsNotNull();
True/False : Boolean 타입의 레코드를 검색할 때 사용합니다.
// True/False : Boolean 값 조회
List<User> findByActiveTrue();
List<User> findByActiveFalse();
// 사용 예제
List<User> activeUsers = userRepository.findByActiveTrue();
List<User> inactiveUsers = userRepository.findByActiveFalse();
Before/After : 시간을 기준으로 값을 검색합니다.
// Before/After : 시간 비교
List<User> findByCreatedDateBefore(LocalDate date);
List<User> findByCreatedDateAfter(LocalDate date);
// 사용 예제
List<User> usersBefore = userRepository.findByCreatedDateBefore(LocalDate.of(2023, 1, 1));
List<User> usersAfter = userRepository.findByCreatedDateAfter(LocalDate.of(2023, 1, 1));
LessThan/GreaterThan : 특정 값(숫자)을 기준으로 대소 비교를 할 때 사용합니다.
// LessThan/GreaterThan : 숫자 비교
List<Product> findByPriceLessThan(Double price);
List<Product> findByPriceGreaterThan(Double price);
// 사용 예제
List<Product> cheapProducts = productRepository.findByPriceLessThan(100.0);
List<Product> expensiveProducts = productRepository.findByPriceGreaterThan(500.0);
Between : 두 값(숫자) 사이의 데이터를 조회합니다.
// Between : 값 사이에 해당하는 데이터 조회
List<Product> findByPriceBetween(Double startPrice, Double endPrice);
// 사용 예제
List<Product> midRangeProducts = productRepository.findByPriceBetween(100.0, 500.0);
OrderBy : SQL 문에서 order by와 동일한 기능을 수행합니다. 예를 들어, 가격순으로 이름 조회를 수행한다면 List<Product> findByNameOrderByPriceAsc(String name); 와 같이 작성합니다.
// OrderBy : 정렬 조건
List<Product> findByNameOrderByPriceAsc(String name);
List<Product> findByNameOrderByPriceDesc(String name);
// 사용 예제
List<Product> productsAsc = productRepository.findByNameOrderByPriceAsc("Laptop");
List<Product> productsDesc = productRepository.findByNameOrderByPriceDesc("Laptop");
countBy : SQL 문의 count와 동일한 기능을 수행하며, 결괏값의 개수(count)를 추출합니다.
// countBy : 데이터 개수 조회
long countByActiveTrue();
long countByActiveFalse();
// 사용 예제
long activeUserCount = userRepository.countByActiveTrue();
long inactiveUserCount = userRepository.countByActiveFalse();
'웹 개발 > 🍃 SpringBoot' 카테고리의 다른 글
JPA | DAO 설계 (0) | 2025.01.13 |
---|---|
@GeneratedValue(strategy = GenerationType.IDENTITY) 전략 제대로 이해하기 (0) | 2025.01.10 |
JPA | 엔티티 설계 (0) | 2025.01.10 |
Hibernate hibernate.ddl-auto 속성 완벽 가이드 (0) | 2025.01.09 |
JPA | 영속성 컨텍스트 (0) | 2025.01.07 |