무슨 이유에서인지 QueryDSL 의 쿼리 결과를 list() 로도, unique() 로도 반환할 수 없다. 원인을 파악해야 한다.
QueryDSL
QueryDSL 환경 설정을 위해 build.gradle.kts 파일에 설정을 추가한다. 추가하는 설정은 이 글 맨 아래에서 확인할 수 있다.
설정 파일
persistence.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- xml 네임스페이스와 사용할 버전을 지정한다. -->
<!-- jpa 2.1 을 사용하려면 여기에 xmlns 와 2.1 을 쓰면 된다. -->
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence" version="2.2">
<!-- 영속성 유닛. 일반적으로 연결할 데이터베이스당 하나의 영속성 유닛을 등록한다. -->
<!-- 그리고 영속성 이름에는 고유한 이름을 부여해야 한다. 여기서는 jpabook. -->
<persistence-unit name="jpabook">
<!-- @Entity 애너테이션이 들어가는 클래스 목록 -->
<class>com.wisdoom.Member</class>
<class>com.wisdoom.Team</class>
<properties>
<!-- 필수 속성 -->
<!-- name 이 javax.persistence 로 시작하는 속성은 JPA 표준 속성으로 특정 구현체에 종속되지 않는다. -->
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/test"/>
<!-- 특정 데이터베이스를 활용하기 위해 사용. -->
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<!-- 옵션 -->
<!-- DDL 을 콘솔에 출력한다. -->
<property name="hibernate.show_sql" value="true"/>
<!-- 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.
create: 기존 테이블 삭제 후 생성; create-drop: create 속성과 동일하나, create 한 내용을 애플리케이션 종료 시에 DROP
update: 데이터베이스 테이블과 엔티티 매핑 정보를 비교해 변경 사항만 수정
validate: 데이터베이스 테이블과 엔티티 매핑 저보를 비교했을 때 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다.
-->
<!-- <property name="hibernate.hbm2ddl.auto" value="create"/>-->
<!--???? 기본 키 생성 전략을 사용하기 위한 설정 ????-->
<property name="hibernate.id.new_generator_mappings" value="true"/>
<!-- 실제로 실행되는 sql query 를 다이얼로그에 출력한다 true. -->
<property name="hibernate.use_sql_comments" value="true"/>
<!-- 실제로 실행되는 sql query 를 사람이 보기 좋게 출력한다 true. 보기 좋게 출력하지 않는다 false. -->
<property name="hibernate.format_sql" value="false"/>
<!--<property name="hibernate.hbm2ddl.auto" value="create" />-->
</properties>
</persistence-unit>
</persistence>
build.gradle.kts
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
val kotlinVersion = "1.4.32"
kotlin("jvm") version kotlinVersion
kotlin("plugin.allopen") version kotlinVersion
kotlin("plugin.jpa") version kotlinVersion
kotlin("kapt") version kotlinVersion
}
allOpen {
annotation("javax.persistence.Entity")
}
group = "me.wisdoom"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
}
dependencies {
// JPA, 하이버네이트
implementation("org.hibernate:hibernate-entitymanager:5.4.32.Final")
// h2 데이터베이스
implementation("com.h2database:h2:1.4.200")
// QueryDSL 환경 설정
// JPAQuery class 사용 가능
implementation("com.querydsl:querydsl-jpa:4.3.0")
// Q Class 생성
kapt("com.querydsl:querydsl-apt:4.3.0:jpa")
testImplementation(kotlin("test-junit5"))
testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.0")
}
tasks.test {
useJUnitPlatform()
}
tasks.withType<KotlinCompile>() {
kotlinOptions.jvmTarget = "1.8"
}
QueryDSL 사용 예제 - 쿼리 타입 직접 지정 (“m”)
fun main() {
// QueryDSL
val emf = Persistence.createEntityManagerFactory("jpabook")
val em = emf.createEntityManager()
val query = JPAQueryFactory(em)
val qMember = QMember("m") // JPQL 별칭(identification variable 또는 alias)이 m
val members = query.from(qMember)
.where(qMember.name.eq("수호"))
.orderBy(qMember.name.desc())
println(members)
}
QueryDSL 사용 예제 - 기본 인스턴스 사용
fun main() {
// QueryDSL
val emf = Persistence.createEntityManagerFactory("jpabook")
val em = emf.createEntityManager()
val query = JPAQueryFactory(em)
val members = query.from(member)
.where(member.name.eq("수호"))
.orderBy(member.name.desc())
println(members)
}
위의 두 사용 예제의 결과는 같으며, 아래와 같이 JPQL 쿼리가 실행된다.
select m
from Member m
where m.name = ?1
order by m.name desc
QueryDSL 사용 예제 - 동적 쿼리 생성: BooleanBuilder
fun main() {
// QueryDSL 동적 쿼리 생성 - BooleanBuilder
val builder = BooleanBuilder()
builder.and(QMember.member.name.eq("수호"))
.and(QMember.member.id.goe(1))
println(builder)
}
member1.name = 수호 && member1.id >= 1
QueryDSL 사용 예제 - 메소드 위임: QueryDelegate
아래와 같이 클래스를 하나 정의하고, static function 을 정의한다. 코틀린에서는 companion object 를 사용한다.
class MemberExpression {
companion object {
@QueryDelegate(Member::class)
fun isHigh(member: QMember, par: Int): BooleanExpression = member.id.gt(1)
}
}
위처럼 클래스와 함수를 정의한 후 build 를 하면 Q Class 에 아래와 같이 메서드가 생성된 것을 확인할 수 있다.
@Generated("com.querydsl.codegen.EntitySerializer")
public class QMember extends EntityPathBase<Member> {
// 생략
public BooleanExpression isHigh(Integer par) {
return MemberExpression.Companion.isHigh(this, par);
}
}