백엔드/JPA
[JPA 활용2] [6] 스프링 데이터 JPA·Querydsl 소개
RE-Heat
2023. 9. 7. 20:57
실전! 스프링 부트와 JPA 활용2 - API 개발과 성능 최적화 - 인프런 | 강의
스프링 부트와 JPA를 활용해서 API를 개발합니다. 그리고 JPA 극한의 성능 최적화 방법을 학습할 수 있습니다., 스프링 부트, 실무에서 잘 쓰고 싶다면? 복잡한 문제까지 해결하는 힘을 길러보세요
www.inflearn.com
인프런 김영한 님의 강의를 듣고 작성한 글입니다.
[1] 스프링 데이터 JPA 소개
스프링 데이터 JPA는 JPA를 사용할 때 지루하게 반복되는 코드를 자동화해 준다.
MemberRepositoryOld - 스프링 데이터 적용 전
@Repository
@RequiredArgsConstructor
public class MemberRepositoryOld {
private final EntityManager em;
public void save(Member member) {
em.persist(member);
}
public Member findOne(Long id) {
return em.find(Member.class, id);
}
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class) //JPQL from의 대상이 엔티티라는 게 좀 다름
.getResultList();
}
public List<Member> findByName(String name) {
return em.createQuery("select m From Member m where m.name= :name", Member.class)
.setParameter("name", name)
.getResultList();
}
}
MemberRepository - 스프링 데이터 적용 후
public interface MemberRepository extends JpaRepository<Member, Long> {
//select m from Member m where m.name = ?
List<Member> findByName(String name);
}
- 인터페이스로 만들고 JpaRepository를 상속한다.
- JpaRepository<>라는 인터페이스는 기본적인 CRUD 기능을 제공한다.
- save, findById 등 생각할 수 있는 다양한 기능을 제공한다.
- 공통이 아닌 findByName()도 따로 넣어주면 where 조건에 name이 자동으로 들어간다.
- 개발자는 인터페이스만 만들면 구현체는 스프링 데이터 JPA가 애플리케이션 실행시점에 주입해 준다.
[2] Querydsl 소개
Querydsl은 SQL(JPQL)과 모양이 유사하며 자바 코드로 동적 쿼리를 편리하게 생성할 수 있다.
■ Querydsl 적용 전 기존 동적쿼리
① 문자로 만든 동적 쿼리
- 띄어쓰기 실수라도 하면 오류가 뜬다.
② JPA Criteria
- 어떤 JPQL이 날아갈지 생각해 내기 쉽지 않다.
■ Querydsl 적용
① 세팅 방법
build.gradle 수정
//querydsl 추가
buildscript {
dependencies {
classpath("gradle.plugin.com.ewerk.gradle.plugins:querydsl-plugin:1.0.10")
}
}
plugins {
id 'org.springframework.boot' version '2.4.1'
id 'io.spring.dependency-management' version '1.0.10.RELEASE'
id 'java'
}
group = 'jpabook'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
//apply plugin: 'io.spring.dependency-management'
apply plugin: "com.ewerk.gradle.plugins.querydsl"
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-devtools'
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
// implementation 'org.hibernate:hibernate-core:5.4.13.Final'
implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
//추가
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 = 'src/main/generated'
def querydslDir = "$buildDir/generated/querydsl"
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
}
- generated된 Q파일이 어디에 위치할지 명시하는 등 세팅 작업이 조금 복잡한 편이다.
② Querydsl 적용 동적 쿼리
public List<Order> findAll(OrderSearch orderSearch) {
QOrder order = QOrder.order;
QMember member = QMember.member;
JPAQueryFactory query = new JPAQueryFactory(em)
return query
.select(order)
.from(order)
.join(order.member, member)
.where(statusEq(orderSearch.getOrderStatus()),
nameLike(orderSearch.getMemberName()))
.limit(1000)
.fetch();
}
private BooleanExpression statusEq(OrderStatus statusCond) {
if (statusCond == null) {
return null;
}
return order.status.eq(statusCond);
}
private BooleanExpression nameLike(String nameCond) {
if (!StringUtils.hasText(nameCond)) {
return null;
}
return member.name.like(nameCond);
}
- QOrder, QMember는 static화, JPAQueryFactory는 생성자 주입 방식으로 처리 가능
■ Querydsl 장점
- 직관적인 문법
- 컴파일 시점에 빠른 문법 오류 발견 : 자바 코드여서 가능함
- 코드 자동완성 : 자바코드여서 IntelliJ에서 자동완성 기능 제공
- 코드 재사용 : statusEq 등 재사용 가능한 데 역시 자바 코드여서 가능한 일이다.
- JPQL new 명령어와는 비교는 안될 정도로 깔끔한 DTO 조회를 지원한다.