[SpringBoot] 스프링부트 프로젝트 생성 후 JPA 사용 및 테스트하기4 Search~라이브러리 추가~(만들기 스프링부트3.25 )_IntelliJ IDEA 2024.1 JDK17 spring-boot test--++

  * 초기 세팅 (개발환경) 및 설정 

https://rockbottomdevbus.blogspot.com/2024/04/springboot-1-325-intellij-idea-20241.html

* 이전글 
[SpringBoot] 스프링부트 프로젝트 생성 후 JPA 사용 및 테스트하기1 엔티티 설정 및 Insert 테스트~JPA 사용방법~


[SpringBoot] 스프링부트 프로젝트 생성 후 JPA 사용 및 테스트하기2 RUD 테스트~ORM/JPA~

https://rockbottomdevbus.blogspot.com/2024/04/springboot-jpa-2-rud-jpa-325-intellij.html

[SpringBoot] 스프링부트 프로젝트 생성 후 JPA 사용 및 테스트하기3 Pageing~쿼리 메소드 및 @Query 테스트~

https://rockbottomdevbus.blogspot.com/2024/04/springboot-jpa-3-pageing-query-325.html


* 라이브러리 추가 

1. 라이브러리 추가 ( 흰색표시 : 수정 및 추가 )

--build.gradle

buildscript {
ext {
queryDslVersion = "5.0.0"
}
}

plugins {
id 'java'
id 'org.springframework.boot' version '3.2.5'
id 'io.spring.dependency-management' version '1.1.4'
}

group = 'org.zerock'
version = '0.0.1-SNAPSHOT'

java {
sourceCompatibility = '17'
}

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'
developmentOnly 'org.springframework.boot:spring-boot-devtools'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
// lombok setting - test에서 사용할 수 있게 설정
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'

runtimeOnly 'com.mysql:mysql-connector-j'
testImplementation 'org.springframework.boot:spring-boot-starter-test'

//Thymeleaf의 레이아웃 기능을 위한 추가 라이브러리
implementation 'nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:3.3.0'

implementation "com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta"

annotationProcessor(
"jakarta.persistence:jakarta.persistence-api",
"jakarta.annotation:jakarta.annotation-api",
"com.querydsl:querydsl-apt:${queryDslVersion}:jakarta"
)

}

test {
useJUnitPlatform()
}


sourceSets{
main{
java{
srcDirs = ["$projectDir/src/main/java","$projectDir/build/generated"]
}
}
}

compileJava.dependsOn('clean')


2. 그래들 토글창에서 컴파일 

- {project name} >> Tasks >> other >> compileJava 더블클릭 or 우클릭 RUN


3. 프로젝트 경로에서 컴파일확인

- 경로 :  {project name} >> build>> classes>> java >> org >> {project package} >> {project name} > domain 

- 첨부 사진처럼 나오면 성공 




Search 

1. search 패키지 생성 >> 경로 : /main/java/{pakage name}/repository/

- 위 경로에 인터페이스 파일 생성 

-- BoardSearch.java 

package org.zerock.b01.repository.search;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.zerock.b01.domain.Board;


/*
1. Querydsl과 기존의 JPARepository의 연동 작업 설정을 위한 인터페이스 생성
2. 이 인터페이스를 구현하는 구현체 생성. 주의사항 구현채의 이름은 인터페이스 + Impl 로 작성
이름이 다른경우 제대로 동작하지 않을 수 있음
3. 마지막으로 BoardRepository의 선언부에서 BoardSearch 인터페이스를 추가 지정

*/

public interface BoardSearch {

Page<Board> serach(Pageable pageable);

}


2. 1번  경로에 BoardSearchImpl 파일 생성

-- BoardSearchImpl.java 

package org.zerock.b01.repository.search;

import com.querydsl.jpa.JPQLQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.zerock.b01.domain.Board;
import org.zerock.b01.domain.QBoard;

import java.util.List;

/*
BoardSearch를 상속받는 구현체
*/
public class BoardSearchImpl extends QuerydslRepositorySupport implements BoardSearch{
public BoardSearchImpl() {
super(Board.class);
}

@Override
public Page<Board> search1(Pageable pageable) {

//Q도메인을 이용한 쿼리 작성 및 테스트
//Querydsl의 목적은 "타입" 기반으로 "코드"를 이용해서 JPQL 쿼리를 생성 후 실행
//Q도메인은 이 때 코드를 만드는 대신에 클래스가 Q도메인 클래스

//1. Q도메인 생성
QBoard board = QBoard.board;
JPQLQuery<Board> query = from(board); // select ~ from board
query.where(board.title.contains("1")); //where title like

List<Board> title = query.fetch(); //JPQL 쿼리 실행
long count = query.fetchCount(); //쿼리실행


return null;
}
}


3. BoardRepository.java 파일에 BoardSearch 추가 상속

-- BoardRepository.java 

package org.zerock.b01.repository;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.zerock.b01.domain.Board;
import org.zerock.b01.repository.search.BoardSearch;

// <Board, Long> - 엔티티 타입 , id

public interface BoardRepository extends JpaRepository<Board, Long>, BoardSearch {
Page<Board> findByTitleContainingOrderByBnoDesc(String keyword, Pageable pageable);

//@Query 어노테이션에서 사용하는 구문은 JPQL을 사용
//JPQL SQL과 유사하게 JPA에서 사용하는 쿼리 언어
//@Query를 이용하는 경우
//1. 조인과 같이 복잡한 쿼리를 실행하려고 할 떄
//2. 원하는 속성만 추출해서 Object[] 로 처리하거나 DTO 로 처리가능
//3. 속성 값 중 nativeQuery 속성 값을 true로 지정하면 SQL구문으로 사용이 가능함.
@Query("select b FROM Board b where b.title like concat('%',:keyword,'%')")
Page<Board> findKeyword(String keyword, Pageable pageable);

@Query(value = "select now()", nativeQuery = true)
String getTime();

}


4. BoardRepositoryTests.java 파일에 Search 테스트 메서드 추가 

--BoardRepositoryTests.java 

@Test
public void testSearch1(){
// 2 page order by bno desc
Pageable pageable = PageRequest.of(1,10, Sort.by("bno").descending());

log.info(boardRepository.serach1(pageable));

}


5. BoardSearchImpl.java 파일에 search1 기능 구현

- search1 메서드는 아래 쿼리와 동일한형태로 만들어 주게끔 구현

select * from board
where
(title like concat('%','1','%')
or
content like concat('%','1','%'))
order by bno desc;

- search1 메서드는 아래 쿼리와 동일한형태로 만들어 주게끔 구현

--BoardSearchImpl.java

package org.zerock.b01.repository.search;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.JPQLQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.zerock.b01.domain.Board;
import org.zerock.b01.domain.QBoard;

import java.util.List;

/*
BoardSearch를 상속받는 구현체
*/
public class BoardSearchImpl extends QuerydslRepositorySupport implements BoardSearch{
public BoardSearchImpl() {
super(Board.class);
}

@Override
public Page<Board> search1(Pageable pageable) {

//Q도메인을 이용한 쿼리 작성 및 테스트
//Querydsl의 목적은 "타입" 기반으로 "코드"를 이용해서 JPQL 쿼리를 생성 후 실행
//Q도메인은 이 때 코드를 만드는 대신에 클래스가 Q도메인 클래스

//1. Q도메인 생성
QBoard board = QBoard.board;
JPQLQuery<Board> query = from(board); // select ~ from board
//booleanBuilder() 사용
BooleanBuilder booleanBuilder = new BooleanBuilder();// (
booleanBuilder.or(board.title.contains("11")); //title like
booleanBuilder.or(board.content.contains("11")); //content like

// query.where(board.title.contains("1")); //where title like
query.where(booleanBuilder); // )
query.where(board.bno.gt(0L)); // bno > 0
this.getQuerydsl().applyPagination(pageable, query);

List<Board> title = query.fetch(); //JPQL 쿼리 실행
long count = query.fetchCount(); //쿼리실행
return null;
}
}


6. BoardSearch.java 파일에 searchAll 메서드 테스트 추가

--BoardSearch.java

// title content 의 내용 검색
Page<Board> serachAll(String[] types, String keyword, Pageable pageable);


7. BoardSearchImpl searchAll 메서드 추가(오버라이드)

--BoardSearchImpl.java


@Override
public Page<Board> serachAll(String[] types, String keyword, Pageable pageable) {

// 1. Qdomain 객체 생성
QBoard board = QBoard.board;

// 2. QL 작성..
JPQLQuery<Board> query = from(board); // select ... from board

// 3.
if ((types != null && types.length > 0) && (keyword != null)) {
//검색 조건과 키워드가 있는 경우
BooleanBuilder booleanBuilder = new BooleanBuilder();
for (String type : types) {
switch (type){
case "t":
booleanBuilder.or(board.title.contains(keyword)); // title like cocat('%',keyword,'%')
break;
case "c":
booleanBuilder.or(board.content.contains(keyword)); // content like cocat('%',keyword,'%')
break;
case "w":
booleanBuilder.or(board.writer.contains(keyword)); // writer like cocat('%',keyword,'%')
break;
}
} // for end

query.where(booleanBuilder); // )
} // if end

//bno > 0
query.where(board.bno.gt(0L));

// paging
this.getQuerydsl().applyPagination(pageable, query);

List<Board> list = query.fetch();

long count = query.fetchCount();

// Page<T> 형식으로 반환 : Page<Board>
// PageImpl을 통해서 반환 : (list - 실제 목록 데이터, pageable, total - 전체개수)
return new PageImpl<Board>(list, pageable, count);
}


8. BoardRepositoryTests searchAll 테스트 메서드 추가 

--BoardRepositoryTests.java 

@Test
public void testSearchAll(){

String[] types = {"t","c","w"};

String keyword = "1";

Pageable pageable = PageRequest.of(1,10, Sort.by("bno").descending());

Page<Board> result = boardRepository.serachAll(types,keyword,pageable);

result.getContent().forEach(board -> log.info("board : " + board));
log.info("사이즈 : " + result.getSize());
log.info("페이지 번호 : " + result.getNumber());
log.info("이전페이지 : " + result.hasPrevious());
log.info("다음페이지: " + result.hasNext());
}


9. BoardSearchImpl 완성

--BoardSearchImpl.java

package org.zerock.b01.repository.search;

/*
BoardSearch를 상속받는 구현채
*/

import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.JPQLQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import org.zerock.b01.domain.Board;
import org.zerock.b01.domain.QBoard;

import java.util.List;

public class BoardSearchImpl extends QuerydslRepositorySupport implements BoardSearch {

public BoardSearchImpl() {
super(Board.class);
}

@Override
public Page<Board> serach1(Pageable pageable) {

//Q 도메인을 이용한 쿼리 작성 및 테스트
//Querydsl의 목적은 "타입" 기반으로 "코드" 를 이용해서 JPQL 쿼리를 생성하고 실행한다...
//Q 도메인은 이 때에 코드를 만드는 대신 클래스가 Q도메인 클래스...

QBoard board = QBoard.board;

JPQLQuery<Board> query = from(board); // select .. from board

// BooleanBuilder() 사용
BooleanBuilder booleanBuilder = new BooleanBuilder();

booleanBuilder.or(board.title.contains("11")); // title like..
booleanBuilder.or(board.content.contains("11")); // content like..

// booleanBuilder.or(board.title.contains("11")); // where title like...

query.where(booleanBuilder); // )
query.where(board.bno.gt(0l)); // bno > 0

query.where(board.title.contains("1")); // where title like...

//paging
this.getQuerydsl().applyPagination(pageable, query);

List<Board> title = query.fetch(); // JPQLQuery에 대한 실행

long count = query.fetchCount(); // 쿼리 실행

return null;
}

@Override
public Page<Board> serachAll(String[] types, String keyword, Pageable pageable) {

// 1. Qdomain 객체 생성
QBoard board = QBoard.board;

// 2. QL 작성..
JPQLQuery<Board> query = from(board); // select ... from board

// 3.
if ((types != null && types.length > 0) && (keyword != null)) {
//검색 조건과 키워드가 있는 경우
BooleanBuilder booleanBuilder = new BooleanBuilder();
for (String type : types) {
switch (type){
case "t":
booleanBuilder.or(board.title.contains(keyword)); // title like cocat('%',keyword,'%')
break;
case "c":
booleanBuilder.or(board.content.contains(keyword)); // content like cocat('%',keyword,'%')
break;
case "w":
booleanBuilder.or(board.writer.contains(keyword)); // writer like cocat('%',keyword,'%')
break;
}
} // for end

query.where(booleanBuilder); // )
} // if end

//bno > 0
query.where(board.bno.gt(0L));

// paging
this.getQuerydsl().applyPagination(pageable, query);

List<Board> list = query.fetch();

long count = query.fetchCount();

// Page<T> 형식으로 반환 : Page<Board>
// PageImpl을 통해서 반환 : (list - 실제 목록 데이터, pageable, total - 전체개수)
return new PageImpl<Board>(list, pageable, count);
}
}

댓글

T O P