본문 바로가기
Backend/이커머스 api

order api - domain(model, product, redis, repository) repository

by 큌 2025. 2. 10.
반응형

repository(ProductItemRepository, ProductRepository, ProductRepositoryCustom, ProductRepositoryImpl)

ProductItemRepository

package com.zerobase.cms.order.domain.repository;

import com.zerobase.cms.order.domain.model.ProductItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductItemRepository extends JpaRepository<ProductItem, Long> {
}

상품 아이템 리포지토리 (ProductItemRepository)

이 코드는 ProductItem 엔티티에 대한 데이터베이스 접근을 제공하는 리포지토리 인터페이스(ProductItemRepository)입니다. Spring Data JPA를 사용하여 데이터베이스 연동을 간편하게 처리하며, 기본적인 CRUD (Create, Read, Update, Delete) 기능을 제공합니다.

1. 인터페이스 구조 및 어노테이션

  • import org.springframework.data.jpa.repository.JpaRepository;: JpaRepository 인터페이스를 가져옵니다. JpaRepository는 Spring Data JPA에서 제공하는 인터페이스로, 기본적인 CRUD 기능을 제공합니다.
  • import org.springframework.stereotype.Repository;: Repository 어노테이션을 가져옵니다. 이 어노테이션은 해당 인터페이스가 리포지토리임을 나타냅니다.
  • @Repository: 이 어노테이션은 해당 인터페이스가 리포지토리임을 나타냅니다. Spring Framework는 @Repository 어노테이션이 붙은 인터페이스를 자동으로 감지하여 Bean으로 등록하고, 데이터베이스 접근 기능을 제공합니다.
  • public interface ProductItemRepository extends JpaRepository<ProductItem, Long> { ... }: ProductItemRepository 인터페이스를 정의합니다.
    • public interface ProductItemRepository: ProductItemRepository 인터페이스를 정의합니다. public은 이 인터페이스가 모든 패키지에서 접근 가능하다는 것을 의미합니다.
    • extends JpaRepository<ProductItem, Long>: JpaRepository 인터페이스를 상속받습니다. JpaRepository는 Spring Data JPA에서 제공하는 인터페이스로, 기본적인 CRUD 기능을 제공합니다.
      • ProductItem: 리포지토리가 관리할 엔티티 타입을 지정합니다. 여기서는 ProductItem 엔티티를 관리합니다.
      • Long: 엔티티의 ID (기본 키) 타입을 지정합니다. 여기서는 ProductItem 엔티티의 ID가 Long 타입임을 나타냅니다.

2. 핵심 개념 및 추가 설명

  • 리포지토리 (Repository): 리포지토리는 데이터베이스에 접근하여 데이터를 조회, 생성, 수정, 삭제하는 기능을 제공하는 인터페이스 또는 클래스입니다. 리포지토리는 데이터베이스 접근 로직을 캡슐화하고, 애플리케이션 코드에서 데이터베이스 접근을 분리하는 역할을 합니다.
  • Spring Data JPA: Spring Data JPA는 JPA (Java Persistence API)를 사용하여 데이터베이스 연동을 간편하게 처리할 수 있도록 해주는 Spring Framework 모듈입니다. Spring Data JPA는 리포지토리 인터페이스를 정의하고, 인터페이스를 구현하는 코드를 자동으로 생성하여 개발자가 직접 데이터베이스 접근 코드를 작성하는 번거로움을 줄여줍니다.
  • JpaRepository: JpaRepository는 Spring Data JPA에서 제공하는 인터페이스로, 기본적인 CRUD (Create, Read, Update, Delete) 기능을 제공합니다. JpaRepository 인터페이스를 상속받으면 다음과 같은 메서드를 사용할 수 있습니다.
    • save(entity): 엔티티를 저장하거나 업데이트합니다.
    • findById(id): ID로 엔티티를 조회합니다.
    • findAll(): 모든 엔티티를 조회합니다.
    • delete(entity): 엔티티를 삭제합니다.
    • deleteAll(): 모든 엔티티를 삭제합니다.
  • CRUD (Create, Read, Update, Delete): CRUD는 데이터베이스 애플리케이션에서 가장 기본적인 데이터 처리 작업입니다.
    • Create: 새로운 데이터를 생성합니다.
    • Read: 기존 데이터를 조회합니다.
    • Update: 기존 데이터를 수정합니다.
    • Delete: 기존 데이터를 삭제합니다.

ProductRepository

package com.zerobase.cms.order.domain.repository;

import com.zerobase.cms.order.domain.model.Product;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductRepository extends JpaRepository<Product, Long>, ProductRepositoryCustom {
   @EntityGraph(attributePaths = {"productItems"}, type = EntityGraphType.LOAD)
    Optional<Product> findWithProductItemsById(Long id);

    @EntityGraph(attributePaths = {"productItems"}, type = EntityGraphType.LOAD)
   Optional<Product> findBySellerIdAndId(Long sellerId, Long id);

    @EntityGraph(attributePaths = {"productItems"}, type = EntityGraphType.LOAD)
    List<Product> findAllByIdIn(List<Long> ids);
}

상품 리포지토리 (ProductRepository)

이 코드는 Product 엔티티에 대한 데이터베이스 접근을 제공하는 리포지토리 인터페이스(ProductRepository)입니다. Spring Data JPA를 사용하여 데이터베이스 연동을 간편하게 처리하며, 기본적인 CRUD (Create, Read, Update, Delete) 기능 외에도 특정 조건을 만족하는 상품을 조회하는 메서드를 제공합니다. 또한, EntityGraph를 사용하여 연관된 엔티티 (ProductItem)를 함께 로드하는 기능을 제공합니다.

1. 인터페이스 구조 및 어노테이션

  • import java.util.List;: List 인터페이스를 가져옵니다. List는 순서가 있는 데이터 컬렉션을 나타냅니다.
  • import java.util.Optional;: Optional 클래스를 가져옵니다. Optional은 값이 null일 수 있는 변수를 처리하기 위한 클래스입니다.
  • import org.springframework.data.jpa.repository.EntityGraph;: EntityGraph 어노테이션을 가져옵니다. EntityGraph는 엔티티를 조회할 때 함께 로드할 연관된 엔티티를 지정합니다.
  • import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType;: EntityGraphType 열거형을 가져옵니다. EntityGraphType은 EntityGraph의 로딩 방식을 지정합니다.
  • import org.springframework.data.jpa.repository.JpaRepository;: JpaRepository 인터페이스를 가져옵니다. JpaRepository는 Spring Data JPA에서 제공하는 인터페이스로, 기본적인 CRUD 기능을 제공합니다.
  • import org.springframework.stereotype.Repository;: Repository 어노테이션을 가져옵니다. 이 어노테이션은 해당 인터페이스가 리포지토리임을 나타냅니다.
  • @Repository: 이 어노테이션은 해당 인터페이스가 리포지토리임을 나타냅니다. Spring Framework는 @Repository 어노테이션이 붙은 인터페이스를 자동으로 감지하여 Bean으로 등록하고, 데이터베이스 접근 기능을 제공합니다.
  • public interface ProductRepository extends JpaRepository<Product, Long>, ProductRepositoryCustom { ... }: ProductRepository 인터페이스를 정의합니다.
    • public interface ProductRepository: ProductRepository 인터페이스를 정의합니다. public은 이 인터페이스가 모든 패키지에서 접근 가능하다는 것을 의미합니다.
    • extends JpaRepository<Product, Long>: JpaRepository 인터페이스를 상속받습니다. JpaRepository는 Spring Data JPA에서 제공하는 인터페이스로, 기본적인 CRUD 기능을 제공합니다.
      • Product: 리포지토리가 관리할 엔티티 타입을 지정합니다. 여기서는 Product 엔티티를 관리합니다.
      • Long: 엔티티의 ID (기본 키) 타입을 지정합니다. 여기서는 Product 엔티티의 ID가 Long 타입임을 나타냅니다.
    • , ProductRepositoryCustom: 사용자 정의 리포지토리 인터페이스인 ProductRepositoryCustom을 상속받습니다. 이를 통해 ProductRepository에서 직접 구현하지 않은 사용자 정의 메서드를 사용할 수 있습니다.

2. 인터페이스 메서드

2.1. findWithProductItemsById(Long id)

  • 기능: ID로 상품을 조회하고, 상품 아이템 목록을 함께 로드합니다.Optional<Product> findWithProductItemsById(Long id);`
    • @EntityGraph(attributePaths = {"productItems"}, type = EntityGraphType.LOAD): 이 어노테이션은 엔티티를 조회할 때 함께 로드할 연관된 엔티티를 지정합니다.
      • attributePaths = {"productItems"}: 함께 로드할 연관된 엔티티의 속성 경로를 지정합니다. 여기서는 Product 엔티티의 productItems 속성을 지정하여 Product를 조회할 때 productItems도 함께 로드하도록 합니다.
      • type = EntityGraphType.LOAD: 엔티티 그래프의 로딩 방식을 지정합니다. EntityGraphType.LOAD는 지정된 속성 경로에 있는 엔티티를 즉시 로딩하는 방식을 나타냅니다.
    • Optional<Product> findWithProductItemsById(Long id);: ID로 상품을 조회하는 메서드를 정의합니다.
      • Optional<Product>: 반환 타입으로, 조회된 Product 엔티티를 Optional 객체로 감싸서 반환합니다. Optional은 값이 null일 수 있는 변수를 처리하기 위한 클래스입니다.
      • Long id: 조회할 상품의 ID를 나타내는 파라미터입니다.

2.2. findBySellerIdAndId(Long sellerId, Long id)

  • 기능: 판매자 ID와 상품 ID로 상품을 조회하고, 상품 아이템 목록을 함께 로드합니다.Optional<Product> findBySellerIdAndId(Long sellerId, Long id);`
    • @EntityGraph(attributePaths = {"productItems"}, type = EntityGraphType.LOAD): 이 어노테이션은 위에서 설명한 것과 동일합니다.
    • Optional<Product> findBySellerIdAndId(Long sellerId, Long id);: 판매자 ID와 상품 ID로 상품을 조회하는 메서드를 정의합니다.
      • Long sellerId: 조회할 상품의 판매자 ID를 나타내는 파라미터입니다.
      • Long id: 조회할 상품의 ID를 나타내는 파라미터입니다.

2.3. findAllByIdIn(List<Long> ids)

  • 기능: 주어진 ID 목록에 해당하는 상품 목록을 조회하고, 각 상품의 상품 아이템 목록을 함께 로드합니다.List<Product> findAllByIdIn(List<Long> ids);`
    • @EntityGraph(attributePaths = {"productItems"}, type = EntityGraphType.LOAD): 이 어노테이션은 위에서 설명한 것과 동일합니다.
    • List<Product> findAllByIdIn(List<Long> ids);: 주어진 ID 목록에 해당하는 상품 목록을 조회하는 메서드를 정의합니다.
      • List<Long> ids: 조회할 상품 ID 목록을 나타내는 파라미터입니다.

3. 사용자 정의 리포지토리 (Custom Repository)

  • ProductRepositoryCustom: 이 인터페이스는 ProductRepository에서 직접 구현하지 않은 사용자 정의 메서드를 정의하는 데 사용됩니다. 사용자 정의 리포지토리를 사용하면 복잡한 쿼리나 데이터베이스 관련 로직을 별도의 클래스에서 구현할 수 있습니다.

4. 핵심 개념 및 추가 설명

  • 리포지토리 (Repository): 리포지토리는 데이터베이스에 접근하여 데이터를 조회, 생성, 수정, 삭제하는 기능을 제공하는 인터페이스 또는 클래스입니다. 리포지토리는 데이터베이스 접근 로직을 캡슐화하고, 애플리케이션 코드에서 데이터베이스 접근을 분리하는 역할을 합니다.
  • Spring Data JPA: Spring Data JPA는 JPA (Java Persistence API)를 사용하여 데이터베이스 연동을 간편하게 처리할 수 있도록 해주는 Spring Framework 모듈입니다. Spring Data JPA는 리포지토리 인터페이스를 정의하고, 인터페이스를 구현하는 코드를 자동으로 생성

ProductRepositoryCustom

package com.zerobase.cms.order.domain.repository;

import com.zerobase.cms.order.domain.model.Product;
import java.util.List;

public interface ProductRepositoryCustom {
    List<Product> searchByName(String name);
}

ProductRepositoryImpl

package com.zerobase.cms.order.domain.repository;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.zerobase.cms.order.domain.model.Product;
import com.zerobase.cms.order.domain.model.QProduct;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class ProductRepositoryImpl implements ProductRepositoryCustom {

    private final JPAQueryFactory queryFactory;

    @Override
    public List<Product> searchByName(String name) {
        String search = "%" + name + "%"; // name이 포함된 모든 문자열 찾을 수 있다.

        QProduct product = QProduct.product;
        return queryFactory.selectFrom(product) // Product 테이블에서 데이터를 선택해라.
                .where(product.name.like(search)) // name과 문자열이 일치하는 모든 Product를 찾아라.
                .fetch();
    }
}

상품 검색 기능 구현 (ProductRepositoryImpl)

이 코드는 상품(Product) 데이터를 검색하는 기능을 구현한 클래스입니다. 특히, 상품 이름(name)으로 검색하는 기능을 제공하며, 일반적인 데이터베이스 접근 방식보다 더 유연하고 강력한 검색 기능을 제공하기 위해 Querydsl이라는 라이브러리를 사용합니다.

1. 패키지 선언

  • package: 자바에서 클래스들을 묶어 관리하는 폴더와 같은 개념입니다. 여기서는 com.zerobase.cms.order.domain.repository라는 패키지에 현재 클래스가 속해 있음을 나타냅니다. 패키지를 사용하면 클래스 이름의 충돌을 방지하고, 코드를 체계적으로 관리할 수 있습니다.

2. 임포트 (Import)

  • import: 다른 패키지에 있는 클래스를 현재 클래스에서 사용하기 위해 불러오는 역할을 합니다. 마치 다른 폴더에 있는 파일을 현재 폴더로 가져오는 것과 같습니다.
    • com.querydsl.jpa.impl.JPAQueryFactory: Querydsl 라이브러리의 핵심 클래스로, 데이터베이스에 쿼리를 실행하는 역할을 합니다.
    • com.zerobase.cms.order.domain.model.Product: 상품 정보를 담는 Product 클래스를 가져옵니다. (상품 이름, 가격, 설명 등)
    • com.zerobase.cms.order.domain.model.QProduct: Querydsl에서 Product 클래스를 쿼리할 때 사용하는 클래스입니다. (자동 생성됨)
    • java.util.List: 여러 개의 Product 객체를 담을 수 있는 목록(List) 인터페이스를 가져옵니다.
    • lombok.RequiredArgsConstructor: Lombok 라이브러리의 어노테이션으로, 필수 필드(final이나 @NonNull이 붙은 필드)를 사용하는 생성자를 자동으로 생성해줍니다.
    • org.springframework.stereotype.Repository: Spring 프레임워크의 어노테이션으로, 현재 클래스가 데이터베이스에 접근하는 Repository임을 나타냅니다.

3. 클래스 선언

  • @Repository: Spring 프레임워크에게 이 클래스가 데이터 저장소(Repository) 역할을 한다는 것을 알려줍니다. Spring은 @Repository 어노테이션이 붙은 클래스를 자동으로 관리하고, 데이터베이스 연동 관련 예외를 처리하는 등 다양한 기능을 제공합니다.
  • @RequiredArgsConstructor: Lombok 라이브러리의 어노테이션입니다. 이 클래스의 final 필드나 @NonNull 어노테이션이 붙은 필드를 매개변수로 받는 생성자를 자동으로 만들어줍니다. 여기서는 JPAQueryFactory를 주입받기 위해 사용됩니다.
  • public class ProductRepositoryImpl: ProductRepositoryImpl이라는 이름의 클래스를 정의합니다. public은 이 클래스가 어디서든 접근 가능하다는 의미입니다.
  • implements ProductRepositoryCustom: ProductRepositoryImpl 클래스가 ProductRepositoryCustom 인터페이스를 구현한다는 의미입니다. 인터페이스는 클래스가 구현해야 할 메서드들을 정의해 놓은 것입니다. 즉, ProductRepositoryImpl 클래스는 ProductRepositoryCustom 인터페이스에 정의된 모든 메서드를 반드시 구현해야 합니다.

4. 멤버 변수 선언

  • private final JPAQueryFactory queryFactory: JPAQueryFactory 타입의 queryFactory라는 멤버 변수를 선언합니다.
    • private: 이 변수는 현재 클래스 내에서만 접근 가능합니다.
    • final: 이 변수는 한 번 값이 할당되면 변경할 수 없습니다.
    • JPAQueryFactory: Querydsl을 사용하여 데이터베이스에 쿼리를 실행하는 객체입니다.

5. 생성자 (자동 생성)

  • @RequiredArgsConstructor 어노테이션 덕분에 JPAQueryFactory를 매개변수로 받는 생성자가 자동으로 생성됩니다. 이 생성자는 queryFactory 멤버 변수에 값을 할당합니다. 즉, Spring이 JPAQueryFactory 객체를 생성하여 ProductRepositoryImpl 클래스의 인스턴스를 만들 때 자동으로 주입해줍니다.

6. searchByName 메서드 구현

  • @Override: 이 메서드가 ProductRepositoryCustom 인터페이스에 정의된 메서드를 재정의(Override)한다는 것을 나타냅니다.
  • public List<Product> searchByName(String name): searchByName이라는 이름의 메서드를 정의합니다.
    • public: 이 메서드는 어디서든 호출 가능합니다.
    • List<Product>: 이 메서드는 Product 객체의 목록(List)을 반환합니다.
    • String name: 이 메서드는 String 타입의 name이라는 매개변수를 받습니다. 이 매개변수는 검색할 상품 이름입니다.
  • String search = "%" + name + "%": 검색 문자열을 만듭니다.
    • %는 SQL에서 사용하는 와일드카드 문자로, 임의의 문자열을 나타냅니다.
    • "%" + name + "%"는 name 변수의 앞뒤에 %를 붙여서 name을 포함하는 모든 문자열을 검색하도록 합니다. 예를 들어, name이 "apple"이면 search는 "%apple%"이 됩니다.
  • QProduct product = QProduct.product: QProduct는 Querydsl에서 자동으로 생성해주는 클래스입니다. QProduct.product는 Product 엔티티의 쿼리 타입을 나타내는 싱글톤 객체입니다. 이를 통해 Product 테이블의 컬럼에 접근할 수 있습니다.
  • queryFactory.selectFrom(product): Querydsl을 사용하여 데이터베이스에서 데이터를 선택하는 쿼리를 시작합니다.
    • queryFactory: JPAQueryFactory 객체로, 데이터베이스 쿼리를 실행하는 역할을 합니다.
    • selectFrom(product): product (즉, Product 테이블)에서 데이터를 선택하겠다는 의미입니다.
  • .where(product.name.like(search)): 쿼리에 조건을 추가합니다.
    • where(): 쿼리의 조건을 지정하는 메서드입니다.
    • product.name: Product 테이블의 name 컬럼을 나타냅니다.
    • like(search): name 컬럼의 값이 search 문자열과 유사한(like) 데이터를 찾습니다. SQL의 LIKE 연산자와 같습니다.
  • .fetch(): 쿼리를 실행하고 결과를 가져옵니다.
    • fetch(): 쿼리 결과를 List<Product> 형태로 반환합니다.

요약

이 코드는 Querydsl을 사용하여 상품 이름으로 상품을 검색하는 기능을 구현합니다. searchByName 메서드는 입력받은 이름을 포함하는 상품 이름을 가진 모든 상품을 데이터베이스에서 찾아 목록으로 반환합니다. Querydsl을 사용하면 SQL 쿼리를 직접 작성하는 것보다 더 안전하고 유연하게 쿼리를 작성할 수 있습니다. 또한, 컴파일 시점에 오류를 검출할 수 있어 런타임 오류를 줄이는 데 도움이 됩니다.

반응형