BackEnd/Spring

테스트 프로젝트 생성_사원관리

Leo.K 2023. 11. 17. 15:59

본격적으로 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