Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- DAO 연동 컨트롤러 서비스 설계
- MariaDB
- Swagger
- 데이터베이스 연동
- Java
- 프로젝트 생성
- 개발자
- spring
- 백엔드
- 인텔리제이
- #devops #terraform #state
- 제로베이스
- 유효성검사
- JPA
- 백엔드스쿨
- 스프링부트실전가이드
- DAO 설계
- 엔티티 설계
- auditing
- 제로베이스 #백엔드 #Java #Spring #개발자 #백엔드공부 #백엔드 스쿨
- 스프링 부트 핵심 가이드
- 백엔드공부
- validated
- ORM
- 리포지토리 인터페이스
Archives
- Today
- Total
JeongJin's Blog
08. Spring Data JPA 활용 (3) 본문
8.5 @Query 어노테이션 사용하기
- JPQL을 사용하면 JPA 구현체에서 자동으로 쿼리 문장을 해석하고 실행하게 된다.
- 만약 데이터베이스를 다른 데이터베이스로 변경할 일이 없다면 직접 해당 데이터베이스에 특화된 SQL을 작성할 수 있으며, 주로 튜닝된 쿼리를 사용하고자 할 때 직접 SQL을 작성한다.
@Query("SELECT p FROM Product AS p WHERE p.name = ?1"
- 조건문에서 '?1' 은 파라미터를 전달받기 위한 인자에 해당한다.
- 주의할 점은 파라미터의 순서가 바뀌면 오류가 발생할 가능성이 있어 @Param 어노테이션을 사용하는 것이 좋다.
@Query("SELECT p FROM Product AS p WHERE p.number = :name")
List<Product> findByNameParam(@Param("name") String name);
- 파라미터를 바인딩하는 방식으로 메서드를 구현하면 코드의 가독성이 높아지고 유지보수가 수월해진다.
@Query("SELECT p.name, p.price, p.stock FROM Product AS p WHERE p.name = :name")
List<Object[]) findByNumberParam2(@Param("name") String name);
- SELECT에 가져오고자 하는 컬럼을 지정한다.
- 리턴 타입으로는 Object 배열의 리스트 형태로 정의 한다.
8.6 QueryDSL 적용하기
- @Query 어노테이션의 단점으로 직접 문자열을 입력하기 때문에 컴파일 시점에 에러를 잡지 못하고 Runtime 에러가 발생할 수 있다.
- 운영 환경에 배포하고 나서 오류가 발견되는 리스크를 유발한다.
8.6.1 QueryDSL 이란?
- 정적 타입을 이용해 SQL과 같은 쿼리를 생성할 수 있도록 지원하는 프레임워크
- 문자열이나 XML 파일을 통해 쿼리를 작성하는 대신 QueryDSL이 제공하는 플루언트(Fluent) API를 활용해 쿼리를 생성할 수 있다.
- 참고 URL
8.6.2 QueryDSL의 장점
- IDE가 제공하는 코드 자동 완성 기능을 사용할 수 있다.
- 문법적으로 잘못된 쿼리를 허용하지 않는다. 따라서 정상적으로 활용된 QueryDSL은 문법 오류를 발생시키지 않는다.
- 고정된 SQL 쿼리를 작성하지 않기 때문에 동적으로 쿼리를 생성할 수 있다.
- 코드로 작성하므로 가독성 및 생산성이 향샹된다.
- 도메인 타입과 프로퍼티를 안전하게 참조할 수 있다.
8.6.3 QueryDSL을 사용하기 위한 프로젝트 설정
- gradle 방식으로 설정
//querydsl 추가
buildscript {
dependencies {
classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")
}
}
// querySQL
apply plugin: "com.ewerk.gradle.plugins.querydsl"
dependencies {
...
//querydsl 추가
testImplementation("org.junit.vintage:junit-vintage-engine") {
exclude group: "org.hamcrest", module: "hamcrest-core"
}
//querydsl 추가
implementation 'com.querydsl:querydsl-jpa'
//querydsl 추가
implementation 'com.querydsl:querydsl-apt'
}
//querydsl 추가
def querydslDir = "$projectDir/build/generated"
querydsl {
library = "com.querydsl:querydsl-apt"
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main {
java {
srcDirs = ['src/main/java', querydslDir]
}
}
}
compileQuerydsl{
options.annotationProcessorPath = configurations.querydsl
}
configurations {
querydsl.extendsFrom compileClasspath
}
8.6.4 기본적인 QueryDSL 사용하기
Test 클래스 생성 후 상단에 @SpringBootTest 를 넣어주어야 EntityManager nullPointException 에러가 발생하지 않는다.!!
@PersistenceContext
EntityManager entityManager;
@Test
void queryDslTest() {
JPAQuery<Product> query = enw JPAQuery(entityManager);
QProduct qProduct = QProduct.product;
List<Product> productList = query
.form(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
}
- JPAQuery 객체를 생성한다
- List 타입으로 받기 위해서 fetch() 를 사용
- List fetch(): 조회 결과를 리스트로 반환
- T fetchOne(): 단 건의 조회 결과를 반환
- T fetchFirst(): 여러 건의 조회 결과 중 1건을 반환, 내부 로직에서 'limit(1).fetchOne()' 으로 구현되어 있음
- Long fetchCount(): 조회 결과의 개수를 반환
- QueryResult fetchResults(): 조회 결과 리스트와 개수를 포함한 QueryResults를 반환
@PersistenceContext
EntityManager entityManager;
@Test
void QueryDslTest2() {
JPAQueryFactory jpaQueryFactory = enw JPAQueryFactory(entityManager);
QProduct qProduct = QProduct.product();
List<Product> productList = jpaQueryFactory.selectFrom(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
}
- JPAQueryFactory를 활용한 쿼리 작성
- JPAQuery 와 다르게 select 절부터 작성 가능
- 전체 컬럼을 조회하지 않고 일부만 조회하고 싶다면
- selectFrom(gProduct) -> select(qProduct.name, qProduct.stock).from(qProduct) 로 수정한다.
- 비지니스 로직에서 활용하려면 config 클래스에 bean을 등록한다.
@Configuration
public class QueryDSLConfiguration {
@PersistenceContext
EntityManager entitiManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
- JPAQueryFactory를 초기화 하지 않고 @Autowired로 빈을 주입받아서 바로 사용 가능하다
@Autowired
JPAQueryFactory jpaQueryFactory;
@Test
void queryDslTest4() {
QProduct qProduct = QProduct.product;
List<String> productList = jpaQueryFactory
.select(qProduct.name)
.from(qProduct)
.where(qProduct.name.eq("펜"))
.orderBy(qProduct.price.asc())
.fetch();
}
8.6.5 QuerydslPredicateExecutor, QuerydslRepositorySupport 활용
- Spring Data JPA 에서는 QueryDSL을 더욱 편하게 사용할 수 있게 QuerydslPredicateExecutor 인터페이스와 QuerydslRepositorySupport 클래스를 제공한다.
QuerydslPredicateExecutor 인터페이스
- JpaRepository와 함께 레포지토리에서 QueryDSL을 사용할 수 있게 인터페이스를 제공한다.
public interface QProductRepository extends JpaRepository<Product, Long>, QuerydslPredicateExecutor {
}
- QuerydslPredicateExcutor 인터페이스의 메서드 대부분은 Predicate 타입을 받는다.
@SpringBootTest
public class QProductRepositoryTest {
@Autowired
QProductRepository qProductRepository;
@Test
public void queryDSLTest1() {
Predicate predicate = QProduct.product.name.containsIgnoreCase("펜")
.and(QProduct.product.price.between(1000, 2500));
Optional<Product> foundProduct = qProductRepository.findOne(predicate);
}
}
- findOne() 는 QuerydslPredicateExecutor 내부에 메서드로 구현되어 있다.
**QuerydslRepositorySupport 추상 클래스 사용하기**
- 가장 보편적으로 사용하는 방식은 CustomRepository를 활용해 레포지토리를 구현하는 방법
public interface ProductRepositoryCustom {
List findByName(String name);
}
@Component
public class ProductRepositoryCustomImpl extends QuerydslRepositorySuppory implements
ProductRepositoryCustom {
public ProductRepositoryCustomImpl() {
super(Product.class);
}
@Override
public List<Product> findByName(String name) {
QProduct qProduct = QProduct.product;
List<Product> productList = from(product)
.wehre(product.name.eq(name))
.select(product)
.fetch();
return productList;
}
}
```
'Book Study > 스프링 부트 핵심 가이드' 카테고리의 다른 글
09. 연관관계 매핑 (0) | 2023.11.15 |
---|---|
08. Spring Data JPA 활용 (4) (2) | 2023.11.08 |
08. Spring Data JPA 활용 (2) (0) | 2023.11.07 |
08. Spring Data JPA 활용 (1) (0) | 2023.11.06 |
06. 데이터베이스 연동(7) (2) | 2023.11.03 |