[Querydsl] ๋ด๊ฐ ์ฐพ์ ์ฐ๋ ค๊ณ ์ ๋ฆฌํ ๊ธ
๐โ๏ธ ๊ณ์ํด์ ์ ๋ฐ์ดํธ ํ๊ธฐ ๐โ๏ธ
๋ง์ง๋ง ์ ๋ฐ์ดํธ 2022/03/29
1. Querydsl ์ ์ ์ฌ์ฉํ ๊น?
2. ์๋๋ฐฉ์?
3. ์์กด์ฑ
4. Repository ๊ตฌ์กฐ
5. Projection ์ ๋ํ ๊ณ ์ฐฐ ...
6. ๋์ ์ฟผ๋ฆฌ (BooleanBuilder)
7. ExpressionUtils
8. ์ ๋ ฌ ํ์ ์ ๋ฐ๋ฅธ ์ ๋ ฌ ์ฒ๋ฆฌ
Querydsl ์ ์ ์ฌ์ฉํ ๊น?
JPA ๋ฅผ ์ฌ์ฉํ๋ฉด์ (@Query ํฌํจ) ์กฐํ ๊ธฐ๋ฅ์ ๋ํ ํ๊ณ๊ฐ ์๋ค.
๋์ ์ธ ์ฟผ๋ฆฌ์ธ ๊ฒฝ์ฐ์ธ๋ฐ ์๋ฅผ ๋ค์ด ์ฃผ๋ฌธ ํ์ด์ง๋ฅผ ๊ฒ์ ํ๋ค๊ณ ํ์ ๋ ์นดํ ๊ณ ๋ฆฌ or ์ํ๋ช or ๊ธฐ์ ๋ช ๋ฑ๋ฑ... ์ผ๋ก ๊ฒ์ ์กฐ๊ฑด์ด ๋ฌ๋ผ์ง๋ ๋ถ๋ถ์ด๋ค.
๊ทธ๋์ ์ฌ์ฉํ๊ฒ ๋ ๊ฒ์ด ๋ฐ๋ก Querydsl ํ๋ ์์ํฌ ์ด๋ค.
- ํ์ ์ฒดํฌ๊ฐ ๋ฐ๋ก ๊ฐ๋ฅํ๋ค
- ์๋ฐ ์ฝ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋ค
์๋๋ฐฉ์?
Querydsl -> JPQL -> SQL
์์กด์ฑ
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
</dependency>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
์ํฐํฐ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก Q ๊ฐ ๋ถ๋ ํด๋์ค๋ค์ ์๋ ์์ฑํด์ค๋ค.
๋น๋๋ฅผ ํด๋ณด๋ฉด target/generated-sources/java ์ฌ๊ธฐ์ Qํด๋์ค๋ค์ด ์๊ฒจ๋๊ฑธ ๋ณผ ์ ์๋ค.
- querydsl-jpa : QueryDSL JPA ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- querydsl-apt : ์ฟผ๋ฆฌ ํ์ (Q)์ ์์ฑํ ๋ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
Repository ๊ตฌ์กฐ
public interface UserRepository extends JpaRepository<Users, Long>, UserRepositoryCustom {
}
public interface UserRepositoryCustom {
List<Users> findByOrderByUser(Long userId, UserSearch userSearch);
}
public class UserRepositoryImpl extends QuerydslRepositorySupport implements
UserRepositoryCustom {
private JPAQueryFactory queryFactory;
// 1.
public UserRepositoryImpl() {
super(Users.class);
}
// 2.
@Override
public void setEntityManager(EntityManager entityManager) {
super.setEntityManager(entityManager);
queryFactory = new JPAQueryFactory(entityManager);
}
1. QuerydslRepositorySupport๋ฅผ ์์ํ๊ฒ๋๋ฉด ๋ ์ด์ QueryDsl์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋จ, ์์ฑ์์์ super(Entity.class)๋ฅผ ํธ์ถํ๋ฉด ๊ฐ๋ฅ ํ๋ค.
- QuerydslRepositorySupport ํด๋์ค์๋ ๊ธฐ๋ณธ์์ฑ์๊ฐ ์์.
2.
QuerydslRepositorySupport ๋ฅผ ์ฌ์ฉํ๋ฉด ๊ตฌํ์ฒด์ JPQLQuery ๋ฅผ ์ฌ์ฉํด์ ์ฟผ๋ฆฌ๋ฅผ ์งํํด์ผ ํ๊ธฐ ๋๋ฌธ์ from ์ผ๋ก ์์ํด์ผ ํ๋ค. ๊ทธ๋์ queryFactory (์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ select ๋ก ์์ํ๊ธฐ ์ํด )๋ฅผ ์ด์ฉํ๊ธฐ ์ํด
EntityManager ๋ฅผ ์์ ํด๋์ค์ ์ ๋ฌํ์ฌ JPAQuery์์ ์ ๊ณตํด์ฃผ๋ (select,selectFrom ๋ฑ๋ฑ ..) ์ ๊ตฌํํ ์ ์๋ค.
-> ์ฑ๋ฅ ์ฐจ์ด๋ ์์ง๋ง ์ผ๋ฐ์ ์ผ๋ก ์ฌ์ฉํ๋ SQL๋ก ๊ตฌํํด์ ์ฝ๋ ๊ฐ๋ ์ฑ์ ๋์ด๋ ๊ฒ ๋ํ ์ด๋์ด๋ผ๊ณ ์๊ฐํ๋ค!
Projection ์ ๋ํ ๊ณ ์ฐฐ ...
์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ํ๋์ ํ๋๋ฅผ ์กฐํ ํ๋ ๊ฒ์ ์ฝ๋ค.
List<String> result = queryFactory
.select(users.name)
.from(users)
.fetch();
ํ์ง๋ง ๋ณดํต ์ฌ๋ฌ ๋ฆฌํด๊ฐ์ด ํ์ํ๋ฐ ์๋์ ๊ฐ์ด ์์ฑํ๋ค๋ฉด Tuple ๋ก ๋ฐํ๋๋ค.
์ฐ๋ฆฌ๋ ๊ฒฐ๊ณผ ๊ฐ์ ๊ฐ์ฒด๋ก ๋ง๋ค์ด ์ฌ์ฉํด์ผ ํ๊ธฐ ๋๋ฌธ์ DTO ๋ก ๋ง๋ค์ด ์ฐ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ณด๊ณ ์ ํ๋ค!
List<Tuple> result = queryFactory
.select(users.name,user.age)
.from(users)
.fetch();
Projections ์ ์ฌ์ฉํ๋ฉด ๋๋ค.
1. constructor
์์ฑ์ ๊ธฐ๋ฐ ์์ฑ
-> ๋ถ๋ณ ๊ฐ์ฒด ์ด์ง๋ง dto ์์ฑ์์ ํ๋ผ๋ฏธํฐ ์์์ ๋์ผํด์ผ ํ๋ค.(ํ๋๊ฐ ๋ง์์๋ก ์ค์ ์ํ์ด ์๋ค.)
-> ์ด๋ฆ์ ๋ง์ถฐ์ฃผ์ง ์์๋ ํด๋น์๋ฆฌ์ ๋ค์ด๊ฐ๋ ๊ฐ์ ๋ฃ์ด์ค๋ค
List<UserInfoDto> result = queryFactory
.select(
Projections.constructor(UserInfoDto.class,
users.name,
user.age
))
.from(users)
.fetch();
2. bean
DTO์ ์๋ setter ๋ฉ์๋(@Setter) ๊ธฐ๋ฐ ์์ฑ
-> DTO๊ฐ ๋ถ๋ณ ๊ฐ์ฒด๊ฐ ์๋๊ฒ ๋๊ธฐ ๋๋ฌธ์ ์ง์ํ๋ ๋ฐฉ๋ฒ
List<UserInfoDto> result = queryFactory
.select(
Projections.bean(UserInfoDto.class,
users.name.as("userName"),
user.age.as("userAge")
))
.from(users)
.fetch();
3. Fields
ํ๋์ ์ง์ ์ ๊ทผํด์ ๊ฐ์ ์ฑ์์ค๋ค.
-> ํ๋์ ์ง์ ์ ๊ทผํ๊ธฐ ๋๋ฌธ์ ์ด๋ฆ์ด ํ๋ฆฌ๋ฉด ์๋๋ค
-> DB ์ปฌ๋ผ ์ด๋ฆ๊ณผ DTO ํ๋ ์ด๋ฆ์ด ๋ค๋ฅผ ๊ฒฝ์ฐ .as ๋ก ๋ณ๊ฒฝํ์ฌ ๋ฃ์ด์ฃผ๋ฉด ๋๋ค
List<UserInfoDto> result = queryFactory
.select(
Projections.fields(UserInfoDto.class,
users.name.as("userName"),
user.age.as("userAge")
))
.from(users)
.fetch();
4. @QueryProjection
DTO ์ ๋ํ Q class ๊ธฐ๋ฐ ์์ฑ
-> DTO ์์ฑ์์ @QueryProjection ์ด๋ ธํ ์ด์ ์ ์ ์ธํ๊ณ querydsl ๋ฅผ ์ปดํ์ผ ํ๋ฉด QUserInfoDto.class ์ฒ๋ผ Q ํด๋์ค๊ฐ ์์ฑ๋๋ค.
-> Q ํด๋์ค ์์ฑ์ ๋ฏธ๋ฆฌ ํด์ผ ํ๋ค.
-> DTO ๊ฐ querydsl ์ ์์กดํ๋ค๋ ๋จ์ ์ด ์๋ค.
List<UserInfoDto> result = queryFactory
.select(
new QUserInfoDto(
users.name.as("userName"),
user.age.as("userAge")
))
.from(users)
.fetch();
๋๋ ํ๋๋ฅผ ์ฃผ๋ก ์ฌ์ฉํ ๊ฒ ๊ฐ๋ค.
1. ์์ฑ์ - ์ค๋ฌด์์๋ ์ ๋ ์ฌ์ฉ ๋ชปํ ๊ฒ ๊ฐ๋ค dto ํ๋๊ฐ 10๊ฐ ์ด์ ์ผ ๋๊ฐ ๋ง๋ค
2. setter - ๋ถ๋ณ์ฑ ๊ฐ์ฒด๊ฐ ์๋๋ ๋ถ์ํ๋ค
3. QueryProjection - Qํด๋์ค๊ฐ ์ถ๊ฐ๋๋ค๋๊ฒ ๋ง์ ์๋ค๊ณ querydsl ์ ์์กด์ด ๊ฐํด์ง๊ธฐ ๋๋ฌธ์ ์ ์ง๋ณด์์์ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์์ ๊ฒ ๊ฐ๋ค.
๋์ ์ฟผ๋ฆฌ (BooleanBuilder)
๊ฒ์ ๋ฑ์์ ์ฌ์ฉ๋ ์ ์๋๋ก ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑ๋๋ ์กฐ๊ฑด๋ค์ nullable ํ๊ฒ ๋ฃ์ด์ค ์ ์๊ฒ ํด์ค๋ค.
1. ์ ์ ๋ฅผ ๊ฒ์ํ ๋ ์ด๋ฆ์ด๋ ๋์ด๋ก ๊ฒ์ํ๊ณ ์ถ๋ค
2. ์ด๋ฆํ๋๋ก ๊ฐ์ ๋ฐ์ ๊ฒฝ์ฐ builder.and(users.name.eq(name)) ์ฟผ๋ฆฌ๊ฐ ์๋ํ๋ค.
BooleanBuilder builder = new BooleanBuilder();
if (name != null) {
builder.and(users.name.eq(name));
}
if (age != null) {
builder.and(users.age.eq(age));
}
return jpaQueryFactory
.selectFrom(users)
.where(builder)
.fetch();
์ค๋ฌด์์๋ ์ข๋ ๊ฐ๋ ์ฑ์ ์ข๊ฒ ํ๊ธฐ ์ํด ์๋์ ๊ฐ์ด ์๋๋ค
return jpaQueryFactory
.selectFrom(users)
.where(eqName(name), eqAge(age))
.fetch();
}
private BooleanExpression eqName(String name) {
return name != null ? users.name.eq(name) : null;
}
private BooleanExpression eqAge(int age) {
return age == null ? users.age.eq(age) : null;
}
ExpressionUtils
http://querydsl.com/static/querydsl/4.4.0/apidocs/com/querydsl/core/types/ExpressionUtils.html
ExpressionUtils (Querydsl 4.4.0 API)
Converts the given object to an Expression Casts expressions and wraps everything else into co
querydsl.com
๊ณต์ ๋ฌธ์์ ๋ณด๋ฉด ์ด๋ค ๋ฉ์๋๊ฐ ์๋์ง ํ์ธํ ์ ์๋ค!
ํ์ฌ์์ ๋ ๊ฑฐ์ ํ๋ก์ ํธ ์์ ํ๋ ๊ฒ์ ๋ดค๋๋ฐ ExpressionUtils ๋ก Subquery ๋ฅผ ๊ตฌํํ๋๊ฑธ ๋ณด๊ณ ์ ๋ฆฌํฉ๋๋ค!
ํ์ฌ ํ๋ก์ ํธ์์๋ ์ํฐํฐ๋ก ๊ตฌ์ฑ๋์ด์๊ธฐ ๋๋ฌธ์ ์๋ธ์ฟผ๋ฆฌ๊ฐ ํ์ํ ์ผ์ ๊ฑฐ์ ์์๋ค!
return jpaQueryFactory
.select(Projections.fields(
ExpressionUtils.as(getUserCount(UserStatus.ํํดํ์),"usersCount"))
.from(users).fetchCount());
private JPQLQuery<Long> getUserCount(UserStatus status){
return select(users.id.count())
.from(users)
.where(status == null ? null : eqUserStatus(status))
}
private Predicate eqUserStatus(UserStatus status){
return users.status.eq(status);
}
์ฌ๊ธฐ์ as ๋ก ์๋ฆฌ์์ค๋ฅผ ํด์ฃผ๋๋ฐ ํ๋ผ๋ฏธํฐ๋ก ๋ฐ๋ Expression ์ ์๋์ ๊ฐ์ ํ์ ์ ๋ฐ์ ์ ์๋ค. ๊ทธ๋์ JPQLQuery ๋ฅผ ๋ง๋ค์ด์ ํ๋ผ๋ฏธํฐ๋ก ๋์ ํด์ฃผ์๋ค!
Predicate ๋ ์๋ฐ์ฝ๋๋ก ์กฐ๊ฑด๋ฌธ์ ํํ(where ์ )ํ ์ ์๊ฒ ํด์ฃผ์ด์ ์กฐ๊ฑด๋ฌธ๋ค์ ๋ฐ๋ก ๊ด๋ฆฌํ ์ ์๋๋ก ํด์ค๋๋ค~!
http://querydsl.com/static/querydsl/4.4.0/apidocs/com/querydsl/core/types/Expression.html
Expression (Querydsl 4.4.0 API)
Expression defines a general typed expression in a Query instance. The generic type parameter is a reference to the type the expression is bound to. The central Expression subinterfaces are
querydsl.com
์ ๋ ฌ ํ์ ์ ๋ฐ๋ฅธ ์ ๋ ฌ ์ฒ๋ฆฌ
private OrderSpecifier getOrderBy(Order sortType) {
switch (sortType) {
case ์ต๊ทผ๊ฐ์
:
return new OrderSpecifier(Order.DESC, user.createAt);
case ๋์์ฐ๋ น์:
return new OrderSpecifier(Order.ASC, user.age);
case ํ์์ํ:
return new OrderSpecifier(Order.ASC, user.status);
default:
return new OrderSpecifier(Order.ASC, user.createAt);
}
}
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.querydsl.core.types;
public enum Order {
ASC,
DESC;
private Order() {
}
}