안녕하세요 NOT-ERROR팀의 백엔드 개발자 홍민정입니다. 🫧
☑️ Spring Data JPA
▪️ Spring Data JPA
- 지루하게 반복되는 CRUD문제를 세련된 방법으로 해결하여 개발자는 인터페이스만 작성하면 됨
- Spring data JPA가 구현 객체를 동적으로 생성해서 주입함
- JpaReapository 인터페이스를 제공하여 이를 상속받아 우리가 상상할 수 있는 모든 API를 처리할 수 있음
public interface CartDetailRepository extends JpaRepository<CartDetail, Long> {
}
▪️ Spring Data JPA의 쿼리 메서드 기능
- 기본적으로 CRUD 메서드 및
- 메서드 이름으로 쿼리를 생성함
- @Query 애너테이션으로 쿼리를 직접 정의할 수 있음
- 메서드 이름만으로 JPQL 쿼리를 생성할 수 있음
- 선언된 메서드에 대해서는 애플리케이션 로딩 시점에 쿼리를 다 만들어버림
public interface CartDetailRepository extends JpaRepository<CartDetail, Long> {
CartDetail findByProductId(Long productId);
}
- 기본적으로 JPA가 CRUD 메서드 및 쿼리 메서드 기능을 사용하더라도 원하는 조건의 데이터를 수집하기 위해서는 필연적으로 쿼리를 작성하게 됨
▪️ @Query 어노테이션을 사용해서 직접 쿼리를 작성
① JPQL로 작성 (nativeQuery = false)
public interface CartDetailRepository extends JpaRepository<CartDetail, Long> {
@Query(value = "select cd from CartDetail cd where cd.product.productId > : productId")
CartDetail findByProductId(@Param(value="productId") Long productId);
}
- JPA의 일부분으로 정의된 플랫폼 독립적인 객체지향 쿼리 언어
- 일반 SQL이 데이터베이스를 바라보고 작성한다면 JPQL은 엔티티클래스를 바라보고 작성
- ② SQL로 작성 (nativeQuery = true)
public interface CartDetailRepository extends JpaRepository<CartDetail, Long> {
@Query( value = "select cart_detail_id, cart_id, product_id, count"
+ "from cart_detail"
+ "where product_id = :productId", nativeQuery = true)
CartDetail findByProductId(@Param(value="productId") Long productId);
- 어떠한 이유로 JPQL을 사용할 수 없을 때 JPA는 native SQL을 통해 직접 SQL을 사용할 수 있는 기능을 제공함
- SQL을 개발자가 직접 정의
⛔️ SQL, JPQL의 문제점
- SQL과 JPQL은 문자열로 type-check가 불가능함
- 잘 해봐야 애플리케이션 로딩 시점에 알 수 있으며 컴파일 시점에 알 수 있는 방법이 없음
- 해당 로직을 실행하기 전까지 작동여부를 확인할 수 없음
- 해당 쿼리 실행 시점에 오류를 발견
☑️ Query DSL
- SQL, JPQL을 코드로 작성할 수 있도록 도와주는 빌더 API
- 정적 타입을 이용해서 SQL 등의 쿼리를 생성해주는 프레임워크
▪️ QueryDSL의 장점
- 문자가 아닌 코드로 작성함으로써 컴파일 시점에 문법 오류를 쉽게 발견
- 코드 자동완성 등 IDE의 도움을 받을 수 있음
- 동적인 쿼리 작성이 단순하고 쉬움
- 코드모양이 JPQL과 거의 비슷
- 쿼리 작성 시 제약조건 등을 메서드 추출을 통해 재사용할 수 있음
🔻 약간의 단점은, Gradle 설정 및 사용법 등을 익혀야 한다는 점 .. !!
▪️QueryDSL 설정
build.gradle
dependencies{
// QueryDSL 추가
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
annotationProcessor(
"javax.persistence:javax.persistence-api",
"javax.annotation:javax.annotation-api",
"com.querydsl:querydsl-apt:${queryDslVersion}:jpa")
}
// QueryDSL 추가
sourceSets {
main {
java {
srcDirs = ["$projectDir/src/main/java", "$projectDir/build/generated"]
}
}
}
▪️ Query DSL 사용
** 가장 먼저 Q 클래스 생성
- build.gradle을 통해 생성됨
- CartDetail.java @Entity를 가지고 QCartDetil이라는 QueryDSL 전용 객체를 만들 수 있음
- 엔티티매니저를 JPAQueryFactory에 넣고 QCartDetail 객체를 가지고 쿼리를 코드로 짤 수 있음
- 가장 큰 장점은 IDE의 도움을 받을 수 있다는 것과 컴파일 타임에 오류를 잡아서쿼리 때문에 실수를 할 일이 없음
① QueryDslConfig.java
- 먼저 JPAQueryFactory를 Bean으로 등록하여 프로젝트 전역에서 QueryDSL을 작성할 수 있도록 함
@Configuration
public class QueryDslConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
② CartDetailRepository.java
- 기존의 CartDetailRepository에서 사용한 쿼리 메서드를 삭제하고 동일한 메서드 시그니처를 새로운 커스텀 인터페이스에 정의
public interface CartDetailRepository {
CartDetail findByProductId(Long productId);
}
③ CartDetailRepositoryImpl.java
@Repository
public class CartDetailRepositoryImpl implements CartDetailRepository {
private final JPAQueryFactory jpaQueryFactory;
public CartDetailRepositoryImpl(JPAQueryFactory jpaQueryFactory) {
this.jpaQueryFactory = jpaQueryFactory;
}
@Override
public CartDetail findByProductId(Long productId) {
return jpaQueryFactory.selectFrom(QCartDetail.cartDetail)
.Join(QCartDetail.cartDetail.product,QProduct.product)
.where(QCartDetail.cartDetail.Product.productId.eq(productId)
.fetch();
}
}
'[ BE ] 기술' 카테고리의 다른 글
[BE-기술] 채식 유형 계층형 검색 쿼리 만들기 (0) | 2022.10.10 |
---|---|
[BE-기술] 자기참조를 활용한 계층형 카테고리 구현 (0) | 2022.10.10 |
[BE-기술] AWS EC2서버 개설과 RDS 연동 (1) | 2022.10.08 |
[BE-기술] 401(Unauthorized)과 403(Forbidden) (2) | 2022.10.08 |
[BE-기술] 채식쇼핑몰 '채식이들' 프로젝트 백엔드 개발 설계 후기 (0) | 2022.10.06 |