ํ๋ก์ ์
ํ๋ก์ ์ ๊ณผ ๊ธฐ๋ณธ ๊ฒฐ๊ณผ ๋ฐํ
ํ๋ก์ ์ ๋์์ด ํ๋์ธ ๊ฒฝ์ฐ
@Test
public void simpleProjection() {
List<String> result = queryFactory
.select(member.username)
.from(member)
.fetch();
}
- ํ์ ์ ๋ช ํํ๊ฒ ์ง์ ํ ์ ์์.
- ํ๋ก์ ์ ๋์์ด ๋ ์ด์์ด๋ฉด Tuple์ด๋ DTO๋ก ์กฐํํด์ผํจ
ํ๋ก์ ์ ๋์์ด ๋ ์ด์์ผ ๊ฒฝ์ฐ
Tuple๋ก ์กฐํ
@Test
public void tupleProjection() {
List<Tuple> result = queryFactory
.select(member.username, member.age)
.from(member)
.fetch();
for (Tuple tuple : result) {
String username = tuple.get(member.username);
Integer age = tuple.get(member.age);
System.out.println("username = " + username);
System.out.println("age = " + age);
}
}
- com.querydsl.core.Tuple๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ฐํ ๋ฐ์ ์ ์์
- get()์ ์ฟผ๋ฆฌ ํ์
(Q)์ผ๋ก alias๋ฅผ ์ฃผ์
ํด ๊ฐ์ ๋ฐํ ๋ฐ์ ์ ์๋ค
์: tuple.get(member.username)
์ฐธ๊ณ ! ํฌํ ๊ฐ์ ๊ฒฝ์ฐ์๋ ๋ ํฌ์งํ ๋ฆฌ ๊ณ์ธต์์ ์ฌ์ฉํ๋ ๊ฒ์ ๊ด์ฐฎ์ผ๋, ๊ทธ ํ๋ถ(์๋น์ค, ์ปจํธ๋กค๋ฌ) ๊ณ์ธต์ผ๋ก ๋ด๋ ค๊ฐ๊ฒ ๋๋ฉด ์ข์ ์ค๊ณ๊ฐ ์๋๋ค. ์๋จ์ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ด QueryDSL์ ์์กด์ฑ์ผ๋ก ์ฎ์ฌ๋ฒ๋ฆฌ๋ฉด ์ข์ง ์๋ค. QueryDSL์ ์์กด์ฑ์ด ์๋จ๊น์ง ๋์ด๊ฐ๊ธฐ ๋๋ฌธ์ ์ข์ง ์์.
DTO ์กฐํ
ํ์ ์ํฐํฐ DTO ์์
import lombok.Data;
@Data
public class MemberDto {
private String username;
private int age;
public MemberDto() {
}
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
QueryDSL์์ ๊ฒฐ๊ณผ๋ฅผ DTO๋ก ์กฐํํ ๋ ๋น ์์ฑ ๋ฐฉ๋ฒ 3๊ฐ์ง๋ฅผ ์ง์ํ๋ค.
- ํ๋กํผํฐ ์ ๊ทผ: setter๋ฅผ ์ด์ฉํ์ฌ ๊ฐ์ ์ฃผ์
- ํ๋ ์ง์ ์ ๊ทผ
- ์์ฑ์ ์ฌ์ฉ
// setter๋ฅผ ์ด์ฉํ์ฌ ์ฃผ์
@Test
public void findDtoBySetter() {
List<MemberDto> result = queryFactory
.select(Projections.bean(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
}
// ํ๋์ ๋ฐ๋ก ๊ฐ์ ๊ผฝ์๋ฒ๋ฆผ
@Test
public void findDtoByField() {
List<MemberDto> result = queryFactory
.select(Projections.fields(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
}
// ์์ฑ์๋ฅผ ์ด์ฉํ์ฌ ์ฃผ์
@Test
public void findDtoByConstructor() {
List<MemberDto> result = queryFactory
.select(Projections.constructor(MemberDto.class,
member.username,
member.age))
.from(member)
.fetch();
}
- ํ๋กํผํฐ ์ ๊ทผ์ด๋ ํ๋ ์ง์ ์ ๊ทผ ์ DTO์ ํ๋๋ช ๊ณผ ์ํฐํฐ์ ์์ฑ๋ช ์ ๋ง์ถฐ์ค์ผํจ.
- ์์ฑ์ ์ฌ์ฉ์ ๊ฒฝ์ฐ์๋ ์์์ ํ์ ๋ง ๋ง์ถฐ์ฃผ๋ฉด ๋๋ค.
ํ๋กํผํฐ ์ ๊ทผ์ด๋ ํ๋ ์ง์ ์ ๊ทผ ์ ์ด๋ฆ์ด ๋ค๋ฅผ ๋ ํด๊ฒฐ ๋ฐฉ์
@Test
public void findUserDto() {
QMember memberSub = new QMember("memberSub");
List<UserDto> result = queryFactory
.select(Projections.bean(UserDto.class,
member.username.as("name"),
// ์๋ธ ์ฟผ๋ฆฌ๋ฅผ ์ด์ฉํ๋ ๋ฐฉ๋ฒ ExpressionUtils๋ก ๊ฐ์ธ์ค์ผ ํ๋ค.
ExpressionUtils.as(JPAExpressions
.select(memberSub.age.max())
.from(memberSub), "age")))
.from(member)
.fetch();
}
- ExperssionUtils.as(source, alias): ํ๋๋ ์๋ธ ์ฟผ๋ฆฌ์ ๋ณ์นญ ์ ์ฉ
- username.as("memberName"): ํ๋์ ๋ณ์นญ ์ ์ฉ
@QueryProjection ์ฌ์ฉํ์ฌ DTO ์์ฑ์ ์ฌ์ฉํ์ฌ ์กฐํ
์์ฑ์ + @QueryProjection
@Data
public class MemberDto {
private String username;
private int age;
public MemberDto() {
}
@QueryProjection
public MemberDto(String username, int age) {
this.username = username;
this.age = age;
}
}
- ์ฌ์ฉํ ์์ฑ์์ @QueryProjection ์ด๋ ธํ ์ด์ ์ ๋ถ์ฌ์ฃผ๋ฉด ๋จ.
- ์ด๋ ธํ ์ด์ ์ ๋ถ์๋ค๋ฉด Qํ์ ์ผ๋ก DTO๋ฅผ ์์ฑํ์ฌ ์ฌ์ฉํ ์ ์๋ค.
@QueryProjection ํ์ฉ ์์
@Test
public void findDtoByQueryProjection() {
List<MemberDto> result = queryFactory
.select(new QMemberDto(member.username, member.age))
.from(member)
.fetch();
}
- ์์ฑ์ ํ๋ผ๋ฏธํฐ ํ์ ๋ง ๋ง์ถฐ์ฃผ๋ฉด ์ ์ฃผ์ ๋๋ค.
@QueryProjection๋ QueryDSL์์ ์ ๊ณตํ๋ ํ๋ก์ ์ ๊ถ๊ทน์ ๋ฐฉ๋ฒ์ด์ง๋ง ์ฅ๋จ์ ์ด ์กด์ฌํ๋ค.
์ฅ์
- Projections.constructor()๋ฅผ ์ฌ์ฉํ๋ฉด ์ปดํ์ผ ์ค๋ฅ๋ฅผ ์ก์ ์ ์์ง๋ง, ํด๋น ๋ฐฉ๋ฒ์ ์ปดํ์ผ ์ค๋ฅ๋ก ์ก์ ์ ์๋ค.
๋จ์
- Qํ์ผ์ ์์ฑํด์ผํจ.
- ์์กด๊ด๊ณ๊ฐ ๋ฌธ์ , DTO ์์ฒด๊ฐ QueryDSL์ ์์กด์ฑ์ด ์๊น. ๊ทธ๋ผ ์ปจํธ๋กค๋ฌ, ์๋น์ค ๊ณ์ธต์์ QueryDSL์ ์์กด์ฑ์ด ์๊ธฐ๊ธฐ ๋๋ฌธ์ DTO๊ฐ ์์ํจ์ ์์ด๋ฒ๋ฆฐ๋ค. ์ํคํ ์ฒ ์ค๊ณ๊ด์ ์์ ์ข์ง ์๋ค.
@QueryProjection ๊ฒฐ๋ก ! ์ค์ฉ์ ์ธ ๊ด์ ์์๋ ์ฌ์ฉํ๋ฉด ์ข์ง๋ง, ์๋น์ค์ ์ด์ ์์ ์์ ๋ดค์ ๋๋ ๊ณ ๋ คํด๋ณผ ์ฌํญ์ด๋ค.
Distinct
List<String> result = queryFactory
.select(member.username).distinct()
.from(member)
.fetch();
- JPQL์ distinct์ ๊ฐ์ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
๋์ ์ฟผ๋ฆฌ
๋์ ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ์กฐ๊ฑด์ ๋ฐ๋ฅธ ์ฟผ๋ฆฌ๋ฅผ ํธ๋ฆฌํ๊ฒ ์์ฑํ ์ ์๋ค.
๋์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ ๋ฐฉ์์๋ ๋ ๊ฐ์ง๊ฐ ์๋ค.
- BooleanBuilder ํ์ฉ
- where()์ ๋ค์ค ํ๋ผ๋ฏธํฐ ์ฌ์ฉ
BoolenBuilder ํ์ฉ ์์
@Test
public void dynamicQuery_BooleanBuilder() {
String usernameParam = "member1";
int ageParam = 10;
List<Member> result = searchMember1(usernameParam, ageParam);
}
public List<Member> searchMember1(String usernameCond, Integer ageCond) {
BooleanBuilder builder = new BooleanBuilder();
if(usernameCond != null) {
builder.and(member.username.eq(usernameCond));
}
if(ageCond != null) {
builder.and(member.age.eq(ageCond));
}
return queryFactory
.selectFrom(member)
.where(builder)
.fetch();
}
- BooleanBuilder ์ธ์คํด์ค์ .and()๋ฅผ ์ฌ์ฉํด ๊ฒ์ ์กฐ๊ฑด์ ๋ถ์ผ ์ ์๋ค.
where ๋ค์ค ํ๋ผ๋ฏธํฐ ํ์ฉ ์์ (๊ฐ์ฌ๋์ด ์ค๋ฌด์์ ์์ฃผ ์ฐ์๋ ๋ฐฉ์)
@Test
public void dynamicQuery_WhereParam() {
String usernameParam = "member1";
int ageParam = 10;
List<Member> result = searchMember(usernameParam, ageParam);
}
public List<Member> searchMember(String usernameCond, Integer ageCond) {
return queryFactory
.selectFrom(member)
.where(usernameEq(usernameCond), ageEq(ageCond))
.fetch();
}
// ์๋์ ๊ฐ์ด ๋์ ์กฐ๊ฑด ์ฟผ๋ฆฌ๋ฅผ ๋ฉ์๋๋ก ๋นผ๋ฉด ์ฌ์ฌ์ฉ์ฑ์ด ์ฆ๊ฐํ๋ค. (๋ค๋ฅธ ์ฟผ๋ฆฌ์์ ์ฌ์ฌ์ฉ์ด ๊ฐ๋ฅํจ)
private Predicate usernameEq(String usernameCond) {
return usernameCond != null ? member.username.eq(usernameCond) : null;
}
private Predicate ageEq(Integer ageCond) {
if (ageCond == null) {
return null;
}
return member.age.eq(ageCond);
}
- where ์กฐ๊ฑด์ null ๊ฐ์ด ์์ผ๋ฉด ๋ฌด์๋๋๋ฐ ์ด๋ฅผ ํ์ฉํจ.
- ๋ฉ์๋๋ฅผ ๋ฐ๋ก ๋นผ์ ์์ฑํ๋ฉด ๋ค๋ฅธ ์ฟผ๋ฆฌ์์๋ ์ฌ์ฌ์ฉํ ์ ์์.
- ์ฟผ๋ฆฌ ์์ฒด์ ๊ฐ๋ ์ฑ์ด ๋์์ง.
์์ ๋์ ์กฐ๊ฑด ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ์๋์ ๊ฐ์ด ์กฐํฉํ์ฌ ํ๋์ ๋ฉ์๋๋ก ๋ง๋ค ์๋ ์๋ค.
private BooleanExpression allEq(String usernameCond, Integer ageCond) {
return usernameEq(usernameCond).and(ageEq(ageCond));
}
- BooleanExpression: Predicate์ ์์ ๋ฐ์ ๊ตฌํ๋ ์ถ์ ํด๋์ค
- BooleanExpression๋ฅผ ์ด์ฉํ๋ฉด and()๋ฅผ ์ฒด์ธ์ผ๋ก ํ ๋ฒ์ ๋์ ์ฟผ๋ฆฌ๋ฅผ ์ฎ์ ์ ์๋ค.
- ๋จ ์ด ๊ฒฝ์ฐ์๋ null ๊ฐ ์ฒ๋ฆฌ๋ฅผ ์ ํด์ฃผ์ด์ผ ํ๋ค.
์์ , ์ญ์ ์ ๋ฒํฌ ์ฐ์ฐ
์์
@Test
public void bulkUpdate() {
long count = queryFactory
.update(member)
.set(member.username, "๋นํ์")
.where(member.age.lt(14))
.execute();
}
- ๋ฐํ๋๋ long ํ์ ๊ฐ์ ๋ฐ์๋ ๋ฐ์ดํฐ์ ์
์๋์ ๊ฐ์ด ๊ธฐ์กด ๊ฐ์ ๋ํ๊ธฐ, ๊ณฑ ์ฐ์ฐ์ด ๊ฐ๋ฅํ๋ค.
// ๋ํ๊ธฐ (๋ง์ด๋์ค ์ ์์ ์ฃผ์
)
@Test
public void bulkAdd() {
long count = queryFactory
.update(member)
.set(member.age, member.age.add(2))
.execute();
}
// ๊ณฑ
@Test
public void bulkMultiply() {
long count = queryFactory
.update(member)
.set(member.age, member.age.multiply(2))
.execute();
}
์ญ์
@Test
public void bulkDelete() {
long count = queryFactory
.delete(member)
.where(member.age.gt(13))
.execute();
}
์ฃผ์! JPQL ๋ฐฐ์น ์ฟผ๋ฆฌ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์์์ฑ ์ปจํ ์คํธ์ ์๋ ์ํฐํฐ๋ฅผ ๋ฌด์ํ๊ณ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฟผ๋ฆฌ๋ฅผ ๋ ๋ฆฌ๊ธฐ ๋๋ฌธ์ ๋ฐฐ์น ์ฟผ๋ฆฌ๋ฅผ ์คํํ ํ์๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ด๊ธฐํ ํ๋ ๊ฒ์ด ์์ ํ๋ค.