스프링

 

 

 

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)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

about author

PHRASE

Level 60  라이트

성공이 보이면 지치기 쉽다. -팔만 대장경

댓글 ( 4)

댓글 남기기

작성