1. Maven 설정
pom.xml
1)libray
<properties> <java.version>17</java.version> <querydsl.version>5.0.0</querydsl.version> </properties>
<!-- querydsl 설정 -->
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-core</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-sql-spring</artifactId>
<version>${querydsl.version}</version>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<version>${querydsl.version}</version>
<classifier>jakarta</classifier>
</dependency>querydsl-jpa 와 querydsl-apt 추가하면 되지만 부수적으로 querydsl-core, querydsl-sql-spring 추가했다.
그리고 3.0 이상에서은
<classifier>jakarta</classifier> 추가해 야 한다
2)plugin
<plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <outputDirectory>target/generated-sources/java</outputDirectory> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin>
만약에
clean -> compile 시에 다음과 같은 오류가 나오면,
Compilation failure
Attempt to recreate a file for type com.shop.entity.QItem

clean -> compile 다시 시도 하거나
다음 플러그인 코드에서 다음 내용을 삭제해야 한다.
<outputDirectory>target/generated-sources/java</outputDirectory>-->
=>
<plugin> <groupId>com.mysema.maven</groupId> <artifactId>apt-maven-plugin</artifactId> <version>1.1.3</version> <executions> <execution> <goals> <goal>process</goal> </goals> <configuration> <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor> </configuration> </execution> </executions> </plugin>

2. Gradle 설정
build.gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.shop'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-security'
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.thymeleaf.extras:thymeleaf-extras-springsecurity6'
implementation group: 'com.github.gavlyukovskiy', name: 'p6spy-spring-boot-starter', version: '1.9.1'
implementation group: 'nz.net.ultraq.thymeleaf', name: 'thymeleaf-layout-dialect', version: '3.3.0'
implementation group: 'org.modelmapper', name: 'modelmapper', version: '3.2.0'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'org.mariadb.jdbc:mariadb-java-client'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'org.springframework.security:spring-security-test'
// ⭐ Spring boot 3.x이상에서 QueryDsl 패키지를 정의하는 방법
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}
tasks.named('test') {
useJUnitPlatform()
}
def querydslSrcDir = 'src/main/generated'
clean {
delete file(querydslSrcDir)
}
tasks.withType(JavaCompile) {
options.generatedSourceOutputDirectory = file(querydslSrcDir)
options.compilerArgs.add("-parameters")
}
Optional<Integer> page
1) Optional 사용시 @PathVaiable 를 사용해야 한다.
2)jpql 에서 파라미터는 항 @Param을 생략해서는 안된다.
소스 :
https://github.com/braverokmc79/jpa-shop-maven-and-gradle

오류시
Querydsl Q클래스 생성 오류 (Attempt to recreate a file for type)
Attempt to recreate a file for type
어플리케이션 시작 시 Q클래스를 생성할 수 없다고 나온다.
이미 존재하는 Q클래스 삭제해도 똑같은 오류,
인텔리제이 build 방식을 gradle->인텔리제이로 변경
3.설정 테스트
소스 :
https://github.com/braverokmc79/demojpa3

1) AppConfig
package com.example.demojap3;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
@RequiredArgsConstructor
public class AppConfig {
private final EntityManager em;
@Bean
public JPAQueryFactory queryFactory(){
return new JPAQueryFactory(em);
}
}
2)Post
package com.example.demojap3.post;
import jakarta.persistence.*;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
@Entity
@Data
@ToString(of={"title", "content"})
public class Post {
@Id
@GeneratedValue
private Long id;
private String title;
@Lob
private String content;
@Temporal(TemporalType.TIMESTAMP)
private Date created;
}
3) PostDto
package com.example.demojap3.post;
import com.querydsl.core.annotations.QueryProjection;
import jakarta.persistence.Lob;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;
import lombok.Data;
import lombok.ToString;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import java.util.Date;
@Data
@ToString
public class PostDto {
private Long id;
private String title;
private String content;
private Date created;
@QueryProjection
public PostDto(Long id, String title, String content, Date created) {
this.id = id;
this.title = title;
this.content = content;
this.created = created;
}
}
4) PostSearchCondition
package com.example.demojap3.post;
import lombok.Data;
@Data
public class PostSearchCondition {
private String title;
private String content;
}
5) PostCustomRepository
package com.example.demojap3.post;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
public interface PostCustomRepository {
public Page<PostDto> searchPostList(PostSearchCondition condition, Pageable pageable) ;
}
6) PostCustomRepositoryImpl
package com.example.demojap3.post;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import java.util.List;
import static com.example.demojap3.post.QPost.post;
import static org.springframework.util.StringUtils.hasText;
@RequiredArgsConstructor
public class PostCustomRepositoryImpl implements PostCustomRepository{
private final JPAQueryFactory queryFactory;
@Override
public Page<PostDto> searchPostList(PostSearchCondition condition, Pageable pageable) {
List<PostDto> postDtoList = queryFactory.select(
new QPostDto(post.id, post.title, post.content, post.created)
).where(titleEq(condition.getTitle()),
contentEq(condition.getContent())
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.from(post).fetch();
JPAQuery<Long> countQuery = queryFactory
.select(post.count())
.from(post)
.where(titleEq(condition.getTitle()),
contentEq(condition.getContent())
);
return PageableExecutionUtils.getPage(postDtoList, pageable, countQuery::fetchOne);
}
private BooleanExpression titleEq(String title) {
return hasText(title) ? post.title.eq(title) :null;
}
private BooleanExpression contentEq(String content) {
return hasText(content) ? post.content.eq(content) :null;
}
}
7) PostCustomRepository
package com.example.demojap3.post;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface PostRespository extends JpaRepository<Post, Long> , PostCustomRepository {
}
8) PostRespositoryTest
package com.example.demojap3.post;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
@SpringBootTest
@Transactional
@Rollback(value = false)
class PostRespositoryTest {
@Autowired
PostRespository postRespository;
@Test
public void crud(){
Post post=new Post();
post.setTitle("hello1");
post.setContent("content1");
post.setCreated(new Date());
postRespository.save(post);
Post post2=new Post();
post2.setTitle("hello1");
post2.setContent("content2");
post2.setCreated(new Date());
postRespository.save(post2);
Post post3=new Post();
post3.setTitle("hello3");
post3.setContent("content3");
post3.setCreated(new Date());
postRespository.save(post3);
PostSearchCondition condition =new PostSearchCondition();
condition.setTitle("hello1");
Page<PostDto> postList =postRespository.searchPostList(condition, PageRequest.of(0, 10));
System.out.println("postList.getTotalElements() = " + postList.getTotalElements());
postList.forEach(postDto -> System.out.println("postDto.toString() = " + postDto.toString()));
Assertions.assertThat(postList.getNumberOfElements()).isEqualTo(2);
}
}
select
p1_0.id,
p1_0.title,
p1_0.content,
p1_0.created
from
post p1_0
where
p1_0.title=? offset ? rows fetch first ? rows only
postList.getTotalElements() = 2
postDto.toString() = PostDto(id=1, title=hello1, content=content1, created=2023-04-09 14:24:50.018)
postDto.toString() = PostDto(id=2, title=hello1, content=content2, created=2023-04-09 14:24:50.052)

















댓글 ( 4)
댓글 남기기