본문 바로가기

IntelliJ/Spring boot

[Springboot]게시판 만들기 2장 Querydsl 설정 및 테스트 - hoyhi

Querydsl 이용 시 코드 내부에서 상황에 맞는 쿼리 생성이 가능하지만 이를 위해서는
작성된 엔티티 클래스를 그대로 사용하는 것이 아니라 Q도메인 이라는 것을 이용해야 함
그리고 이를 작성하기 위해서 Quertdsl 라이브러리를 이용해서 엔티티 클래스를 Q도메인 클래스로 변환하는 방식 이용
-> 추가적인 설정 필요

 

1. build.gradle 에 코드 추가

plugins {
    id 'org.springframework.boot' version '2.4.5'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    id 'war'
    // Querydsl 사용을 위해 추가
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

group = 'com.example'
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-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    compile group: 'org.mariadb.jdbc', name: 'mariadb-java-client'
    compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-java8time'

    //Querydsl 사용을 위해 추가
    implementation 'com.querydsl:querydsl-jpa'
}

test {
    useJUnitPlatform()
}

// Querydsl 사용을 위해 추가
def querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true
    querydslSourcesDir = querydslDir
}

sourceSets {
    main.java.srcDir querydslDir
}

configurations {
    querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

 

2. 적용 후 compileQuerydsl 실행

 

3. GuestbookRepository -> QuerydslPredicateExecutor 인터페이스 상속

package com.example.chapter4.repository;

import com.example.chapter4.Entity.Guestbook;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;

//JpaRepository<Guestbook,Long> -> <Table명, 기본키 데이터 타입>
//QuerydslPredicateExecutor<Guestbook> -> findAll : Optional을 리턴 / findOne : List, Page 등으로 리턴
public interface GuestbookRepository extends JpaRepository<Guestbook, Long>, QuerydslPredicateExecutor<Guestbook> {
}

 

 

4. GuestbookRepositoryTests 생성 후 실행

package com.example.chapter4.repository;

import com.example.chapter4.Entity.Guestbook;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.stream.IntStream;

@SpringBootTest
public class GuestbookRepositoryTests {
    @Autowired
    private GuestbookRepository guestbookRepository;

    @Test
    public void insertDummies(){
        IntStream.rangeClosed(1,300).forEach(i->{
            Guestbook guestbook = Guestbook.builder()
                    .title("Title...."+i)
                    .content("Contnet...."+i)
                    .writer("user...."+i%10)
                    .build();
            System.out.println(guestbookRepository.save(guestbook));
        });
    }
}

 

 

5. 데이터 생성 확인

 

6. Guestbook 클래스에 수정 메서드 추가

package com.example.chapter4.Entity;

import lombok.*;

import javax.persistence.*;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Guestbook extends BaseEntity{

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

    @Column(length = 100, nullable = false)
    private String title;

    @Column(length = 1500, nullable = false)
    private String content;

    @Column(length = 50, nullable = false)
    private String writer;

    // 엔티티 클래스는 가능하면 setter 관련 기능을 만들지 않는 것이 권장사항이지만
    // 필요에 따라서 수정 기능을 만들기도 함
    // 아래는 제목과 내용을 수정할 수 있도록 메서드 추가
    public void changeTitle(String title){
        this.title = title;
    }

    public void changeContent(String content){
        this.content = content;
    }
}

 

7. 수정 시간 테스트

GuestbookRepositoryTests.java

@Test
    public void updateTest(){
        //존재하는 번호로 테스트
        Optional<Guestbook> result = guestbookRepository.findById(300L);
        if(result.isPresent()){
            Guestbook guestbook = result.get();

            guestbook.changeTitle("changeTitle...");
            guestbook.changeContent("changeContent...");

            guestbookRepository.save(guestbook);
        }
    }

 

 

Querydsl 사용법 

- booleanBuilder 생성

- 조건에 맞는 구문은 Querydsl에서 사용하는 Predicate 타입의 함수 생성

- BooleanBuilder에 작성된 Predicate 추가하고 실행

 

8. 단일 항목 검색 테스트

 

GuestbookRepositoryTests.java

 @Test
    public void testQuery1(){
        Pageable pageable = PageRequest.of(0,10, Sort.by("gno").descending());
        // 동적 처리를 위한 Q도메인 클래스 얻어오기
        // Q도메인 클래스 이용 시 엔티티 클래스에 선언된 title, content 같은 필드들을 변수로 활용 가능
        QGuestbook qGuestbook = QGuestbook.guestbook; // 1
        String keyword = "1";

        // BooleanBuilder는 where문에 들어가는 조건들을 넣어주는 컨테이너
        BooleanBuilder builder = new BooleanBuilder(); // 2

        // 원하는 조건은 필드 값과 같이 결합해서 생성
        // BooleanBuilder 안에 들어가는 값은 com.querydsl.core.types.Predicate 타입
        BooleanExpression expression = qGuestbook.title.contains(keyword); // 3

        // 만들어진 조건은 where 문에 and나 or같은 키워드와 결합
        builder.and(expression); // 4

        // BooleanBuilder는 GuestbookRepository에 추가된
        // QuerydslPredicateExcutor 인터페이스의 findAll() 사용 가능
        Page<Guestbook> result = guestbookRepository.findAll(builder,pageable); // 5

        result.stream().forEach(guestbook -> {
            System.out.println(guestbook);
        });
    }

 

9. 다중 항목 검색 테스트

 

GuestbookRepositoryTests.java

    @Test
    public void testQuery2() {

        Pageable pageable = PageRequest.of(0, 10, Sort.by("gno").descending());

        QGuestbook qGuestbook = QGuestbook.guestbook;

        String keyword = "1";

        BooleanBuilder builder = new BooleanBuilder();

        //exTitle like %1%
        BooleanExpression exTitle =  qGuestbook.title.contains(keyword);

        //exContent like %1%
        BooleanExpression exContent =  qGuestbook.content.contains(keyword);

        // exTitle like %1% or exContent like %1%
        BooleanExpression exAll = exTitle.or(exContent); // 1----------------

        builder.and(exAll); //2-----

        //qGuestbook.gno > 0L
        builder.and(qGuestbook.gno.gt(0L)); // 3-----------

        Page<Guestbook> result = guestbookRepository.findAll(builder, pageable);

        result.stream().forEach(guestbook -> {
            System.out.println(guestbook);
        });

    }