본문 바로가기

백엔드/Spring

[Spring] JpaRepository 쿼리 자동 생성 예제

JpaRepository는 메서드 이름으로 쿼리를 유추하는 기능이 있습니다. (참고 문서)

findBy를 활용한 예제를 통해 알아보겠습니다.

 


@Entity
@Table(name = "products")
public class Product {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long productId;

    @Column(nullable = false, unique = true)
    private String name;

    @Column(nullable = false)
    private int price;

    @Column(nullable = false)
    private String image;

    protected Product() {
    }
}

 

가령 위와 같은 Entity가 있다고 해보겠습니다.

 

 

 

public interface ProductRepository extends JpaRepository<Product, Long> {}

 

그리고 Repository는 위와 같습니다. JpaRepository를 상속받고 아무 것도 하지 않았습니다.

 

 

 

ProductRepository를 이용해서 위 특정 id를 갖는 Product를 찾고 싶다면 어떻게 해야 할까요?

 

@DataJpaTest
public class ProductRepositoryTest {

    private ProductRepository productRepository;

    @Autowired
    public ProductRepositoryTest(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
    
    // 이 메서드만 보시면 됩니다.
    @Test
    public void test() {
        productRepository.findById(1L);
    }
}

 

JPA를 다뤄본 분들이라면 어렵지 않게 답할 수 있을 겁니다.

JpaRepository를 상속받는 것만으로도 findById를 사용할 수 있기 때문입니다.

 

 

 

그런데 만약 특정 name을 갖는 Product를 찾고 싶다면 어떻게 해야 할까요?

안타깝게도 name은 우리가 직접 만는 변수라서 findByName()같은 함수가 있을리 없습니다.

슬퍼하면서 쿼리를 직접 작성하는 대신, JpaRepository가 제공하는 자동 쿼리 생성을 사용해봅시다.

 

 

 

public interface ProductRepository extends JpaRepository<Product, Long> {
    // name은 unique하므로 리턴 타입이 List<Product>가 아닌 Product입니다.
    Product findByName(String name);
}

 

Repository interface에 메서드 한 줄 적으면 됩니다.

이러면 JPA가 자동으로 Product 클래스에서 name이라는 변수를 찾아서 자동으로 쿼리를 생성해줍니다.

jdbctemplate 같은 프레임워크를 사용했다면 아직도 sql 쿼리를 작성하고 있었을 겁니다.

 

 

 

여기서 Entity를 바꿔서 보겠습니다.

 

@Entity
@Table(name = "wish_products")
public class WishList {

    @EmbeddedId
    private WishListId wishListId;

    @Column(nullable = false)
    private int quantity;

    public WishList() {
    }
}

 

조금 더 복잡해졌습니다.

이번에는 @EmbeddedId를 사용해서 복합키를 사용했습니다.

그렇다면 복합키 클래스인 WishListId를 볼까요?

 

 

 

@Embeddable
public class WishListId implements Serializable {
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;

    @ManyToOne
    @JoinColumn(name = "product_id")
    private Product product;

    protected WishListId() {}
}

 

오 맙소사. 연관 관계 매핑으로 이 클래스에는 User와 Product가 들어갑니다.

Product는 위의 두 예제에서 봤던 그 Product입니다.

이 글을 읽는 데에 User에 대한 정보는 필요 없으므로 생략하겠습니다.

 

요약하자면,

  • 클래스 구조로는 WishList 객체 내부에 WishListId 객체가 있고, WishListId 객체 내부에 User와 Product 객체가 있고
  • Table 구조로는 WishList table은 user_id와 product_id라는 외래 키를 가지면서, 기본 키가 (user_id, product_id) 입니다.

 

만약 WishList table에서 특정 productId를 갖는 모든 WishList를 불러와야 한다면 어떻게 할까요?

WishList의 WishListId의 Product의 productId를 검색해야 합니다.

이런 경우에는 눈물을 머금고 직접 sql문을 짜야만 할 것 같습니다.

 

 

 

public interface WishListRepository extends JpaRepository<WishList, WishListId> {
    List<WishList> findByWishListIdProductProductId(Long productId);
}

 

하지만 JpaRepository는 위대합니다.

그런 말도 안 되는 상황도 코드 한 줄로 해결할 수 있습니다. 근데 이제 조금 긴.

차근차근 따라가보면 말이 된다는 것을 알 수 있습니다.

 

findBy <WishListId> <Product> <ProductId>처럼 분해해서 보면 말 그대로 WishListId의 Product의 ProductId를 찾아 간 것을 알 수 있습니다.

 

 

 

public interface WishListRepository extends JpaRepository<WishList, WishListId> {
    List<WishList> findByWishListIdProduct(Product product);
}

 

생각을 조금 응용해보면 특정 productId가 아닌, product를 갖고 있는 경우에도 위와 같이 findBy를 적용할 수 있습니다.

'백엔드 > Spring' 카테고리의 다른 글

[Spring] Embedded, Join Column의 변수에 접근하기  (0) 2024.07.18