Spring + Boot

JPA와 Entity , 동적 쿼리 처리를 위한 Querydsl 설정

devRachel 2021. 4. 18. 18:16

 

Entity 클래스에 @MappedSuperClass라는 어노테이션을 적용하면 테이블로 생성되지 않습니다.

실제 테이블은 Entity클래스를 상속한 엔티티의 클래스로 데이터베이스 테이블이 생성됩니다.

JPA는 JPA만의 고유한 메모리공간(context)을 이용해서 엔티티 객체들을 관리합니다.

기존의 Mybatis 기반의 프로그램과 달리 단계가 하나 더 있는 방식입니다.

 

Mybatis를 이용하는 경우 SQL을 위해 전달되는 객체는 모두 SQL 처리가 끝난 뒤 어떻게 되든 상관없는 객체들인 반면에 JPA에서 사용하는 엔티티 객체들은 영속 콘텍스트라는 곳에서 관리되는 객체입니다. 이 객체들이 변경되면 결과적으로 데이터베이스에 이를 반영하는 방식입니다.

 

JPA에서는 해당 엔티티 객체는 유지되고 필요할 때 꺼내서 재사용하는 방식입니다. 이런 엔티티 객체에 변화가 일어나는 것을 감지하는 리스너가 존재합니다.

 

JPA내부에서 엔티티 객체가 생성, 변경되는 것을 감지하는 역할을 AuditingEntityListener로 이루어집니다.  이것을 통해 regDate,modDate에 적절한 값이 지정됩니다.

 

@CreateDate는 JPA에서 엔티티의 생성 시간을 처리하고, @LastModifiedDate는 최종 수정 시간을 자동으로 처리하는 용도로 사용합니다. 속성으로 insertable, updateable 등이 있는데 updateable=false로 지정하면 해당 엔티티 객체를 데이터베이스에 반영할때 지정해놓은 컬럼값은 변경되지 않습니다.

 

AuditionEntityListener를 활성화시키기 위해서 프로젝트에 @EnableJpaAuditing 설정을 추가해야합니다.

 

 

import lombok.*;

import javax.persistence.*;
import java.sql.Blob;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Frboard extends BaseEntity{

    @Id
    @GeneratedValue (strategy = GenerationType.IDENTITY)
    private Long gno;

    @Column(length = 100, nullable = false)
    private String title;

    @Lob
    @Column(nullable = false)
    private Blob contents;

    @Column(length = 50, nullable = false)
    private String writer;
    
}

 

 

JPA의 쿼리 메서드의 기능과 @Query를 통해 많은 기능을 구현할 수 있지만 아쉽게도 선언할 때 고정된 형태의 값을 가진다는 단점이 있어서 단순한 몇 가지의 검색 조건을 만들어야 하는 사오항에서는 기본 기능만으로 충분하지만 복잡한 조합을 이용하는 경우의 수가 많은 상황에서는 동적 쿼리를 생성해서 처리할 수 있는 Querydsl을 사용합니다. 

 

http://www.querydsl.com/ 으로 이동해서 JPA에 적용해봅시다.

 

Querydsl을 이용하면 코드 내부에서 상황에 맞는 쿼리를 생성할 수 있지만 이를 위해 작성된 엔티티 클래스를 그대로 이용하는 것이 아닌 Q도메인 이라는 것을 이용해야됩니다.

이를 작성하기 위해 Querydsl 라이브러리를 이용해서 엔티티 클래스를 Q도메인 클래스로 변환하는 방식을 이용하기 때문에 추가적인 설정이 필요합니다.

 

 

plugins {
    id 'org.springframework.boot' version '2.4.4'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
    id 'war'
    id 'com.ewerk.gradle.plugins.querydsl' version '1.0.10'
}

group = 'com.ggggggg'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

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-web'
    compileOnly 'org.projectlombok:lombok'
    developmentOnly 'org.springframework.boot:spring-boot-devtools'
    annotationProcessor 'org.projectlombok:lombok'
    providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.springframework.security:spring-security-test'

    compile group: 'org.mariadb.jdbc', name: 'mariadb-java-client'

    compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity5'
    compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-java8time'

    compile group: 'org.thymeleaf.extras', name: 'thymeleaf-extras-springsecurity5'
    implementation 'com.querydsl:querydsl-jpa'
    implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'
    compile group: 'io.jsonwebtoken', name: 'jjwt', version: '0.9.1'
}

test {
    useJUnitPlatform()
}

//추가
def  querydslDir = "$buildDir/generated/querydsl"

querydsl {
    jpa = true

    querydslSourcesDir = querydslDir
}

sourceSets {
    main.java.srcDir querydslDir
}

configurations {
    querydsl.extendsFrom compileClasspath
}

compileQuerydsl {
    options.annotationProcessorPath = configurations.querydsl
}

 

 

build.gradle 파일을 갱신하고 compileQuerydsl을 실행하면 프로젝트 안에 있는 build 폴더에 엔티티 클래스 이름과 동일하지만 앞에 Q가 붙은 클래스들이 생성됩니다.

 

@Generated("com.querydsl.codegen.EntitySerializer")
public class QFrboard extends EntityPathBase<Frboard> {

    private static final long serialVersionUID = 936126458L;

    public static final QFrboard frboard = new QFrboard("frboard");

    public final QBaseEntity _super = new QBaseEntity(this);

    public final SimplePath<java.sql.Blob> contents = createSimple("contents", java.sql.Blob.class);

    public final NumberPath<Long> gno = createNumber("gno", Long.class);

    //inherited
    public final DateTimePath<java.time.LocalDateTime> modDate = _super.modDate;

    //inherited
    public final DateTimePath<java.time.LocalDateTime> regDate = _super.regDate;

    public final StringPath title = createString("title");

    public final StringPath writer = createString("writer");

    public QFrboard(String variable) {
        super(Frboard.class, forVariable(variable));
    }

    public QFrboard(Path<? extends Frboard> path) {
        super(path.getType(), path.getMetadata());
    }

    public QFrboard(PathMetadata metadata) {
        super(Frboard.class, metadata);
    }

}

repository 도 Querydsl 인터페이스를 상속받기 위해 변경합니다.

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;

public interface FrboardRepository extends JpaRepository<Frboard, Long>, QuerydslPredicateExecutor<Frboard> {
}

'Spring + Boot' 카테고리의 다른 글

서비스계층과 DTO  (0) 2021.04.21
Spring Security - 3  (0) 2021.04.17
Spring Security 연동하기 -2 (CSRF를 곁들인)  (0) 2021.04.16
Spring Security 연동하기 -1  (0) 2021.04.15
JPA 쿼리 메서드 기능 @Query  (0) 2021.04.08