ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [스프링부트/웹 애플리케이션 개발]스프링 데이터 JPA, QueryDSL
    스프링&스프링부트 2023. 1. 17. 16:23

    스프링 데이터 JPA

    ( https://spring.io/projects/spring-data-jpa )

    - 스프링 데이터 JPA는 JPA를 사용할 때 지루하게 반복하는 코드를 자동화

     

     

    MemberRepository

    package jpabook.jpashop.repository;
    import jpabook.jpashop.domain.Member;
    import lombok.RequiredArgsConstructor;
    import org.springframework.stereotype.Repository;
    import javax.persistence.EntityManager;
    import java.util.List;
    
    @Repository
    @RequiredArgsConstructor
    public class MemberRepository {
    
    	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).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();
    	}
    }

     

     

     

    스프링 데이터 JPA 적용

    package jpabook.jpashop.repository;
    import jpabook.jpashop.domain.Member;
    import org.springframework.data.jpa.repository.JpaRepository;
    import java.util.List;
    
    public interface MemberRepository extends JpaRepository<Member, Long> {
    	List<Member> findByName(String name);
    }

    - findOne() findById()로 변경

     

     

     

    - 스프링 데이터 JPA는 JpaRepository 라는 인터페이스를 제공하는데, 여기에 기본적인 CRUD 기능이 모두 제공

    - findByName 처럼 일반화 하기 어려운 기능도 메서드 이름으로 정확한 JPQL 쿼리를 실행한다.

        > select m from Member m where m.name = :name

    - 개발자는 인터페이스만 만들면 된다. 구현체는 스프링 데이터 JPA가 애플리케이션 실행시점에 주입해준다

        >> 스프링 데이터 JPA는 JPA를 사용해서 이런 기능을 제공할 뿐이다. 결국 JPA 자체를 잘 이해하는 것이 가장 중요

     

     

     

     

     

    QueryDSL

    - Querydsl은 SQL(JPQL)과 모양이 유사하면서 자바 코드로 동적 쿼리를 편리하게 생성할 수 있다.

    ( http://www.querydsl.com )

     

     

    orderRepository

    public List<Order> findAll(OrderSearch orderSearch) {
    
        //JPAQueryFactory query = new JPAQueryFactory(em);
        QOrder order = QOrder.order;
        QMember member = QMember.member; // static으로 코드 줄일 수 있음
    
        return query
                .select(order)
                .from(order)
                .join(order.member, member)
                .where(statusEq(orderSearch.getOrderStatus()), nameLike(orderSearch.getMemberName())) //동적쿼리
                //.where(order.status.eq(orderSearch.getOrderStatus())) > 정적쿼리
                .limit(1000)
                .fetch();
    }
    
    private Predicate nameLike(String memberName) {
        if (!StringUtils.hasText(memberName)) {
            return null;
        }
        return QMember.member.name.like(memberName);
    }
    
    private BooleanExpression statusEq(OrderStatus statusCond) {
        if (statusCond == null) {
            return null;
        }
        return QOrder.order.status.eq(statusCond);
    }

     

     

     

    build.gradle 코드 추가

    //querydsl 추가
    buildscript {
       ext {
          queryDslVersion = "5.0.0"
       }
    }
    
    plugins {
       id 'java'
       id 'org.springframework.boot' version '2.7.7'
       id 'io.spring.dependency-management' version '1.0.15.RELEASE'
       id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
    }
    
    group = 'jpabook'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '11'
    
    configurations {
       compileOnly {
          extendsFrom annotationProcessor
       }
    }
    
    repositories {
       mavenCentral()
    }
    
    dependencies {
       implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
       implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
       implementation 'org.springframework.boot:spring-boot-starter-validation'
       implementation 'org.springframework.boot:spring-boot-starter-web'
       implementation 'org.springframework.boot:spring-boot-devtools'
       implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5'
    
       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'
    
       //querydsl 추가
       implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
       implementation "com.querydsl:querydsl-apt:${queryDslVersion}"
    
    }
    
    tasks.named('test') {
       useJUnitPlatform()
    }
    
    test {
       useJUnitPlatform()
    }
    
    
    //querydsl 추가
    def querydslDir = "$buildDir/generated/querydsl"
    
    querydsl {
       jpa = true
       querydslSourcesDir = querydslDir
    }
    
    sourceSets {
       main.java.srcDir querydslDir
    }
    
    compileQuerydsl{
       options.annotationProcessorPath = configurations.querydsl
    }
    
    configurations {
       compileOnly {
          extendsFrom annotationProcessor
       }
       querydsl.extendsFrom compileClasspath
    }

    - 동작은 되는데 generate에 파일들이 보이지 않음...?

    - 오류 발생해서 ( https://jaimemin.tistory.com/2120 ) 참고

     

     

     

    실무에서는 복잡한 동적 쿼리를 많이 사용하게 되는데, 이때 Querydsl을 사용하면 높은 개발 생산성을 얻으면서 동시에 쿼리 오류를 컴파일 시점에 빠르게 잡을 수 있다. 꼭 동적 쿼리가 아니라 정적 쿼리인 경우에도 다음과 같은 이유로 Querydsl을 사용하는 것이 좋다.

    - 직관적인 문법

    - 컴파일 시점에 빠른 문법 오류 발견

    - 코드 자동완성

    - 코드 재사용(이것은 자바다)

    - JPQL new 명령어와는 비교가 안될 정도로 깔끔한 DTO 조회를 지원한다.

     

     

    728x90
Designed by Tistory.