먼저 순수 JPA에서 DTO로 조회하는 방법을 알아보자.
// MemberDto
package study.querydsl.dto;
import lombok.Data;
@Data
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
Test에서 JPQL에서는 아래와 같이 짜야 한다.
// QuerydslBasicTest
@Test
public void findDtoByJPQL() {
// 생성자 처럼 MemberDto를 생성하는 것처럼 하고 생성자의 값들이 넘어온다. => new operation을 활용하는 방법
// new 해서 패키지명 다 적고 Dto 명 적고 생성자를 호출하는 것처럼 생긴 이 문법이 JPQL에서 제공하는 new Operation 문법
List<MemberDto> result = em.createQuery("select new study.querydsl.dto.MemberDto(m.username, m.age) from Member m", MemberDto.class)
.getResultList();
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
/* select
new study.querydsl.dto.MemberDto(m.username,
m.age)
from
Member m
// sql을 보면 username age 2개만 조회해주는 것을 확인할 수 있다.
select
member0_.username as col_0_0_,
member0_.age as col_1_0_
from
member member0_
// member1234 4개 보여주고
memberDto = MemberDto(username=member1, age=10)
memberDto = MemberDto(username=member2, age=20)
memberDto = MemberDto(username=member3, age=30)
memberDto = MemberDto(username=member4, age=40)
- 순수 JPA에서 DTO를 조회할 때는 new 명령어를 사용해야 한다.
- DTO의 package 이름을 다 적어줘야해서 지저분하고
- 생성자 방식만 지원해준다. setter를 넣거나 필드에 바로 값을 넣는 것은 안되고 생성자가 꼭 있어야 한다.
여기서 Querydsl은 결과를 DTO 반환할 때 3가지 방법을 지원해주느데
- 프로퍼티 접근을 지원해주고
- 필드 직접 접근
- 생성자 사용을 지원해준다.
// QueryBasicTest
@Test
public void findDtoBySetter() {
List<MemberDto> result = queryFactory
// select에 프로젝션스가 들어간다.
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
test를 돌리면 아래와 같은 에러가 나오는데
com.querydsl.core.types.ExpressionException: study.querydsl.dto.MemberDto
at com.querydsl.core.types.QBean.newInstance(QBean.java:246)
at com.querydsl.jpa.FactoryExpressionTransformer.transformTuple(FactoryExpressionTransformer.java:51)
at org.hibernate.hql.internal.HolderInstantiator.instantiate(HolderInstantiator.java:85)
at org.hibernate.loader.hql.QueryLoader.getResultList(QueryLoader.java:508)
at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2682)
at org.hibernate.loader.Loader.list(Loader.java:2677)
at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:540)
at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:400)
at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:219)
at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1468)
at org.hibernate.query.internal.AbstractProducedQuery.doList(AbstractProducedQuery.java:1649)
at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1617)
at org.hibernate.query.Query.getResultList(Query.java:165)
at com.querydsl.jpa.impl.AbstractJPAQuery.getResultList(AbstractJPAQuery.java:191)
at com.querydsl.jpa.impl.AbstractJPAQuery.fetch(AbstractJPAQuery.java:243)
at study.querydsl.entity.QuerydslBasicTest.findDtoBySetter(QuerydslBasicTest.java:624)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.InstantiationException: study.querydsl.dto.MemberDto
at java.base/java.lang.Class.newInstance(Class.java:571)
at com.querydsl.core.types.QBean.create(QBean.java:251)
at com.querydsl.core.types.QBean.newInstance(QBean.java:222)
... 84 more
Caused by: java.lang.NoSuchMethodException: study.querydsl.dto.MemberDto.<init>()
at java.base/java.lang.Class.getConstructor0(Class.java:3349)
at java.base/java.lang.Class.newInstance(Class.java:556)
... 86 more
MemberDto에서 기본 생성자를 만들어준다. 두가지 방법이 있다.
querydsl이 memberdto를 만든다음에 값을 set set 해줘야 하고
만드는 과정에서 기본 생성자를 호출해야하는데 기본 생성자가 없으니까 오류가 난것이다.
@Data
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
// 디폴트 생성자 하나를 만들어주고
public MemberDto() {
}
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
@Data
@NoArgsConstructor
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
수정해주고 test를 돌리면 실행 결과가 잘 나온다. (프로퍼티 접근방법 )
/* select
member1.username,
member1.age
from
Member member1 */ select
member0_.username as col_0_0_,
member0_.age as col_1_0_
from
member member0_
memberDto = MemberDto(username=member1, age=10)
memberDto = MemberDto(username=member2, age=20)
memberDto = MemberDto(username=member3, age=30)
memberDto = MemberDto(username=member4, age=40)
이제 필드를 활용한 방법을 알아보자.
// 필드를 활용한 방법
// getter setter 없어도 된다. => 바로 필드에다가 값을 꽂아버린다.
@Test
public void findDtoByField() {
List<MemberDto> result = queryFactory
// select에 프로젝션스가 들어간다.
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
필드에다가 바로 값을 꽂아버린다.
// MemberDto 필드
@Data
@NoArgsConstructor
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
memberDto = MemberDto(username=member1, age=10)
memberDto = MemberDto(username=member2, age=20)
memberDto = MemberDto(username=member3, age=30)
memberDto = MemberDto(username=member4, age=40)
첫번째 beans 방식은
@Test
public void findDtoBySetter() {
List<MemberDto> result = queryFactory
// select에 프로젝션스가 들어간다.
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
getter setter를 통해서 값이 생성이 된 것이고
// MemberDto
@Data
@NoArgsConstructor
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
두번째 findDtoByField방식은 값이 필드에 다 꽂힌다.
// 필드를 활용한 방법
// getter setter 없어도 된다. => 바로 필드에다가 값을 꽂아버린다.
@Test
public void findDtoByField() {
List<MemberDto> result = queryFactory
// select에 프로젝션스가 들어간다.
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
// MemberDto 필드
@Data
@NoArgsConstructor
public class MemberDto {
// username이랑 age 두개만 최적화해서 가져오고 싶다해서 MemberDto를 만들어줌
private String username;
private int age;
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
그 다음 방식은 생성자 접근 방법인데 아래와 같다.
@Test
public void findDtoByConstructor() {
// member의 username과 age랑 타입을 맞춰야 한다.
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
원하는 결과값이 나오게 된다.
/* select
member1.username,
member1.age
from
Member member1 */ select
member0_.username as col_0_0_,
member0_.age as col_1_0_
from
member member0_
// MemberDto에서 @Data를 넣어줘서 아래와 같이 나오고
memberDto = MemberDto(username=member1, age=10)
memberDto = MemberDto(username=member2, age=20)
memberDto = MemberDto(username=member3, age=30)
memberDto = MemberDto(username=member4, age=40)
이제는 임의로 UserDto를 하나 만들어보자.
package study.querydsl.dto;
import lombok.Data;
@Data
public class UserDto {
private String name;
private int age;
}
test에서 userdto를 만들어서 실행결과를 확인하면
@Test
public void findUserDto() {
List<UserDto> result = queryFactory
// member에서 userdto로 member를 조회를 하고 싶은데 이름이 안맞다.
.select(Projections.fields(UserDto.class,
member.username,
member.age))
.from(member)
.fetch();
for (UserDto userDto : result) {
System.out.println("userDto = " + userDto);
}
}
아래의 결과값이 null이 나오게 된다.
userDto = UserDto(name=null, age=10)
userDto = UserDto(name=null, age=20)
userDto = UserDto(name=null, age=30)
userDto = UserDto(name=null, age=40)
그러면 .as해서 "name"을 넣어주면
@Test
public void findUserDto() {
List<UserDto> result = queryFactory
// member에서 userdto로 member를 조회를 하고 싶은데 이름이 안맞다.
.select(Projections.fields(UserDto.class,
member.username.as("name"),
member.age))
.from(member)
.fetch();
for (UserDto userDto : result) {
System.out.println("userDto = " + userDto);
}
}
결과가 잘 나오게 된다.
userDto = UserDto(name=member1, age=10)
userDto = UserDto(name=member2, age=20)
userDto = UserDto(name=member3, age=30)
userDto = UserDto(name=member4, age=40)
즉 as해서 dto에 있는 값을 넣어주면 된다.
// UserDto
package study.querydsl.dto;
import lombok.Data;
@Data
public class UserDto {
private String name;
private int age;
}
서브쿼리를 이용해서도 작성해보자. Querydsl의 서브쿼리에 자세한 포스팅은 여기를 참고하고 학습하자.
@Test
public void findUserDto() {
QMember memberSub = new QMember("memberSub");
List<UserDto> result = queryFactory
// Projections.fields에서 UserDto를 조회하는데
.select(Projections.fields(UserDto.class,
// 첫번째는 name으로 되었고
member.username.as("name"),
// 두번째는 서브쿼리를 썼다.
ExpressionUtils.as(JPAExpressions
// 나이가 최대인거를 select의 서브쿼리로 씀
// 회원 나이의 max값으로만 서브쿼리를 해서 오른쪽에는 10 20 30 40 이 아니고 최대나이 40으로 찍는 것이다.
.select(memberSub.age.max())
// 서브쿼리의 결과가 매핑이 된다.
.from(memberSub), "age")
))
.from(member)
.fetch();
for (UserDto userDto : result) {
System.out.println("userDto = " + userDto);
}
}
서브쿼리의 결과는 UserDto에 매칭이 된다.
package study.querydsl.dto;
import lombok.Data;
@Data
public class UserDto {
private String name;
private int age;
}
결과가 잘 나오게 된다.
userDto = UserDto(name=member1, age=40)
userDto = UserDto(name=member2, age=40)
userDto = UserDto(name=member3, age=40)
userDto = UserDto(name=member4, age=40)
이 부분도
member.username.as("name"),
expressionutils를 써도된다. 지저분하다. 첫번째 방법을 쓰자. 서브쿼리 같은 경우는 expressionutils로 감싸야 한다.
ExpressionUtils
@Test
public void findUserDto() {
QMember memberSub = new QMember("memberSub");
List<UserDto> result = queryFactory
// Projections.fields에서 UserDto를 조회하는데
.select(Projections.fields(UserDto.class,
// 첫번째는 name으로 되었고
ExpressionUtils.as(member.username, "name"),
// 두번째는 서브쿼리를 썼다.
ExpressionUtils.as(JPAExpressions
// 나이가 최대인거를 select의 서브쿼리로 씀
// 회원 나이의 max값으로만 서브쿼리를 해서 오른쪽에는 10 20 30 40 이 아니고 최대나이 40으로 찍는 것이다.
.select(memberSub.age.max())
// 서브쿼리의 결과가 매핑이 된다.
.from(memberSub), "age")
))
.from(member)
.fetch();
userDto = UserDto(name=member1, age=40)
userDto = UserDto(name=member2, age=40)
userDto = UserDto(name=member3, age=40)
userDto = UserDto(name=member4, age=40)
생성자 constructor에서 userdto로 바꿔서 돌려보자.
@Test
public void findDtoByConstructor() {
// member의 username과 age랑 타입을 맞춰야 한다.
List<UserDto> result = queryFactory
.select(Projections.constructor(UserDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (UserDto UserDto : result) {
System.out.println("UserDto = " + UserDto);
}
}
에러가 난다. UserDto에서 constructor를 만들어 주자. 그리고 다시 돌려주면 정상작동 한다.
// UserDto
package study.querydsl.dto;
import lombok.Data;
@Data
public class UserDto {
private String name;
private int age;
// default 생성자도 함께 만들어준다.
public UserDto() {
}
public UserDto(String name, int age) {
this.name = name;
this.age = age;
}
}
결과가 잘 나온다.
/* select
member1.username,
member1.age
from
Member member1 */ select
member0_.username as col_0_0_,
member0_.age as col_1_0_
from
member member0_
UserDto = UserDto(name=member1, age=10)
UserDto = UserDto(name=member2, age=20)
UserDto = UserDto(name=member3, age=30)
UserDto = UserDto(name=member4, age=40)
정리해보자.
- setter를 통한 방식
@Test
public void findDtoBySetter() {
List<MemberDto> result = queryFactory
// select에 프로젝션스가 들어간다.
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
- 필드에 바로 injection하는 방식
// 필드를 활용한 방법
// getter setter 없어도 된다. => 바로 필드에다가 값을 꽂아버린다.
@Test
public void findDtoByField() {
List<MemberDto> result = queryFactory
// select에 프로젝션스가 들어간다.
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (MemberDto memberDto : result) {
System.out.println("memberDto = " + memberDto);
}
}
- 생성자 constructor로 해주는 방식
@Test
public void findDtoByConstructor() {
// member의 username과 age랑 타입을 맞춰야 한다.
List<UserDto> result = queryFactory
.select(Projections.constructor(UserDto.class,
member.username,
member.age))
.from(member)
.fetch();
// DTO 조회하는게 간편하다.
for (UserDto UserDto : result) {
System.out.println("UserDto = " + UserDto);
}
}
setter나 필드같은경우 이름 매칭하는것이 굉장히 중요하기 때문에 as("name")을 썼고
복잡할 때는 ExpressionUtils로 alias를 줄 수가 있고 매칭을 해서 값을 넣어줄 수가 있다.
@Test
public void findUserDto() {
QMember memberSub = new QMember("memberSub");
List<UserDto> result = queryFactory
// Projections.fields에서 UserDto를 조회하는데
.select(Projections.fields(UserDto.class,
// 첫번째는 name으로 되었고
member.username.as("name"),
// 두번째는 서브쿼리를 썼다.
ExpressionUtils.as(JPAExpressions
// 나이가 최대인거를 select의 서브쿼리로 씀
// 회원 나이의 max값으로만 서브쿼리를 해서 오른쪽에는 10 20 30 40 이 아니고 최대나이 40으로 찍는 것이다.
.select(memberSub.age.max())
// 서브쿼리의 결과가 매핑이 된다.
.from(memberSub), "age")
))
.from(member)
.fetch();
for (UserDto userDto : result) {
System.out.println("userDto = " + userDto);
}
}
<출처 김영한: 실전! Querydsl >
https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84/dashboard
실전! Querydsl - 인프런 | 강의
Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, - 강의 소개 | 인프런...
www.inflearn.com
'Spring > QueryDSL' 카테고리의 다른 글
동적 쿼리 - BooleanBuilder (0) | 2022.04.21 |
---|---|
프로젝션과 결과 반환 - @QueryProjection (0) | 2022.04.21 |
프로젝션과 결과 반환 - 기본 (0) | 2022.04.20 |
Case 문 & 상수, 문자 더하기 (0) | 2022.04.20 |
서브 쿼리 (0) | 2022.04.20 |