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)
댓글 남기기