본격적으로 queryDsl을 학습하기 전에 학습에 필요한 간단한 사원관리 프로젝트를 생성하고자 한다.
그동안은 스프링 부트의 버전을 2.7.x 버전을 사용했지만 이번에는 3.0대 버전으로 업그레이드 하여 생성을 한 후에 그동안 했던 설정들이 변경된 부분을 동시에 학습해보고자 한다.
1. 프로젝트 생성
- (spring boot 3.0.12, java version 17)
- build.gradle(dependency), aplication.yml(spring, jpa, log 셋팅)
2. 엔티티 생성
- 엔티티를 생성 후에 프로젝트를 실행하여 로그 상에서 jpa가 테이블을 만들어주는 지 확인
- ddl-auto를 create로 설정하여 엔티티 설정에 맞게 테이블을 생성하고, data.sql로 데이터 초기화 하는 방식으로 진행
2-1 Hibernate 초기화 VS Scrpt 초기화 (spring boot 2.5.x 버전 이상의 경우)
- 동시 사용을 추천하지 않지만 어쩔수 없이 사용해야 하는 경우를 위해 간단히 짚고 넘어가자
- Hibernate 초기화 : spring.jpa.hibernate.ddl-auto 옵션에 대한 초기화 설정
-- create : 기존 테이블 삭제 후 다시 생성 (create + drop)
-- none : 초기화를 사용하지 않음
-- create-drop : create와 같은 로직이지만 종료 시점에 drop이 발생한다.
-- update : 변경된 부분만 반영한다. (운영DB에서 사용 불가)
-- validate : 엔티티와 테이블이 정상적으로 매핑되었는지 여부만 확인
- Script 초기화 (schema.sql) : JPA가 접근할 테이블 구조를 정의하는 언어 DDL
- Script 초기화 (data.sql) : 만들어진 테이블에 초기 데이터 삽입이 필요한 경우 DML
기본 동작 순서 : Script 초기화 -> Hibernate 초기화
spring.sql.init.mode=always
spring.jpa.defer-datasource-initialization=true
스프링 부트에서는 데이터베이스의 초기 설정을 위해서 기본적으로 script(schema.sql(ddl), data.sql(insert 등의 dml)) 파일을 사용할 수 있다. 내장 데이터 베이스(H2, Derby, HSQL)이 아닌 외부데이터베이스를 사용한다면 아래의 두 가지 옵션을 반드시 추가해야 한다. (2.5.x 버전(기본적으로 script파일 실행 이후에 hibernate(ddl-auto옵션)가 초기화 된다.) 이상부터 사용하는 옵션으로 설명하겠다.)
- spring.sql.init.mode(스프링 외부 DB sql script 실행을 위한 옵션) : 내장 데이터 베이스(H2, Derby, HSQL)는 기본적으로 Script기반 초기화를 실행하지만, 내장 데이터베이스가 아닌 외부데이터베이스를 사용한다면 Script 초기화를 사용하기 위해서 해당 옵션을 사용해야 한다.
-- always : 모든 데이터베이스에 Script 초기화 적용
-- embedded : 내장 데이터베이스만 Script 초기화 적용
-- never : 모든 데이터베이스에 Script 초기화 적용 x
- spring.jpa.defer-datasource-initialization (Hibernate 초기화를 우선 실행하기 위한 옵션)
두 옵션에 조합으로 인해 생기는 동작 순서의 경우의 수 확인해보기
1. ddl-auto = create, schema.sql, data.sql 둘 다 존재
create -> schema.sql -> data.sql
이미 테이블을 만든 상태에서 schema.sql로 또 테이블을 만들기 때문에 이미 존재하는 테이블이라며 에러가 발생한다.
2. ddl-auto = create, data.sql만 존재
create -> data.sql
테이블 생성후에 data.sql이 insert 된다.
3. ddl-auto = none, schema.sql, data.sql 둘 다 존재
schma.sql -> data.sql
스키마 생성 후에 데이터 삽입 완료
4. ddl-auto = none, data.sql만 존재
data.sql의 insert만 실행된다.
예시로는 create만 들어서 사용했지만 각 옵션의 기능과 초기화의 동작순서만 명확히 인지하면 될 것이다.
3. querydsl 설정
build.gradle
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
id 'java'
id 'war'
id 'org.springframework.boot' version '3.0.12'
id 'io.spring.dependency-management' version '1.1.3'
}
group = 'com.example'
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-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
// Querydsl 추가
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
}
tasks.named('bootBuildImage') {
builder = 'paketobuildpacks/builder-jammy-base:latest'
}
tasks.named('test') {
useJUnitPlatform()
}
// Querydsl 설정부
def generated = 'src/main/generated'
// querydsl QClass 파일 생성 위치를 지정
tasks.withType(JavaCompile) {
options.getGeneratedSourceOutputDirectory().set(file(generated))
}
// java source set 에 querydsl QClass 위치 추가
sourceSets {
main.java.srcDirs += [ generated ]
}
// gradle clean 시에 QClass 디렉토리 삭제
clean {
delete file(generated)
}
QueryDslConfig
package com.example.querydsl.config;
import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class QueryDslConfig {
@PersistenceContext
private EntityManager em;
@Bean
public JPAQueryFactory jpaQueryFactory(){return new JPAQueryFactory(em);}
}
4. 테스트 코드
@Test
@Transactional
public void updateBulk() throws Exception{
//before bulk update
List<Sawon> result = queryFactory
.selectFrom(sawon)
.fetch();
long executeCnt = queryFactory
.update(sawon)
.set(sawon.sawonName, "비회원")
.where(sawon.sawonAge.lt(25))
.execute();
em.flush();
em.clear();
//after bulk update
result = queryFactory
.selectFrom(sawon)
.fetch();
result.forEach(sawon -> System.out.println("sawon = " + sawon.getSawonName()));
}
Result
Before Bulk Update
After Bulk Update
'BackEnd > Spring' 카테고리의 다른 글
Spring Security 총 정리 (0) | 2023.12.22 |
---|---|
SpringBoot Day 2 (0) | 2022.08.18 |
SpringBoot Day1 (0) | 2022.08.10 |
SpringMVC_게시판_기능정리_국비 (0) | 2022.07.15 |
SpringLegacyProject_SpringSecurity적용하기 (0) | 2022.07.12 |