Search

Querydsl

Querydsl

Querydsl์€ ํƒ€์ž… ์•ˆ์ „ํ•œ SQL ๋ฐ JPQL ์ฟผ๋ฆฌ๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค.
๊ธฐ์กด JPQL๊ณผ Criteria API์˜ ๋ณต์žกํ•œ ์ฝ”๋“œ ์ž‘์„ฑ์„ ๋‹จ์ˆœํ™”ํ•˜๋ฉด์„œ๋„ ๊ฐ€๋…์„ฑ๊ณผ ์œ ์ง€๋ณด์ˆ˜์„ฑ์„ ๋†’์—ฌ์ฃผ๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Querydsl ์ •์˜

Querydsl์€ SQL, JPQL, JPA๋ฅผ ์‰ฝ๊ฒŒ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ๋„๋ก ์ง€์›ํ•˜๋Š” ํƒ€์ž… ์•ˆ์ „ํ•œ ์ฟผ๋ฆฌ ๋นŒ๋” API์ž…๋‹ˆ๋‹ค.
๋™์  ์ฟผ๋ฆฌ๋ฅผ ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ์ปดํŒŒ์ผ ์‹œ์ ์—์„œ ์˜ค๋ฅ˜๋ฅผ ๊ฒ€์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
ํŠน์ง•
โ€ข
SQL, JPQL, JPA, MongoDB ๋“ฑ ๋‹ค์–‘ํ•œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์™€ ํ˜ธํ™˜
โ€ข
์ฝ”๋“œ ์ž๋™ ์ƒ์„ฑ(Q-Class)์œผ๋กœ ์ง๊ด€์ ์ธ ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹ ์ง€์›
โ€ข
๋ฉ”์„œ๋“œ ์ฒด์ด๋‹ ๋ฐฉ์‹์œผ๋กœ ๊ฐ€๋…์„ฑ์ด ๋›ฐ์–ด๋‚จ
โ€ข
JPQL๊ณผ ๋™์ผํ•œ ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•˜๋ฉด์„œ๋„ ๋” ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ ์ž‘์„ฑ ๊ฐ€๋Šฅ
โ€ข
์ปดํŒŒ์ผ ํƒ€์ž„ ํƒ€์ž… ์ฒดํฌ ์ง€์› โ†’ ์˜ค๋ฅ˜๋ฅผ ์‚ฌ์ „์— ๋ฐฉ์ง€

Querydsl ์ฃผ์š” ์–ด๋…ธํ…Œ์ด์…˜ ๋ฐ ํด๋ž˜์Šค

ํด๋ž˜์Šค
์„ค๋ช…
JPAQueryFactory
Querydsl์˜ ์ฟผ๋ฆฌ ๋นŒ๋” ๊ฐ์ฒด (์ฟผ๋ฆฌ ์‹คํ–‰์„ ์œ„ํ•œ ์ค‘์‹ฌ ํด๋ž˜์Šค)
QEntity
Querydsl์ด ์ž๋™ ์ƒ์„ฑํ•œ ์—”ํ„ฐํ‹ฐ ํด๋ž˜์Šค (์˜ˆ: QUsers, QBoards)
BooleanExpression
where() ์กฐ๊ฑด์„ ๋™์ ์œผ๋กœ ์ถ”๊ฐ€ํ•  ๋•Œ ์‚ฌ์šฉ
Projections
DTO ๋งคํ•‘ ์‹œ ์‚ฌ์šฉ
Expressions
๋ณต์žกํ•œ ์—ฐ์‚ฐ์„ ์ˆ˜ํ–‰ํ•  ๋•Œ ์‚ฌ์šฉ

Querydsl ์„ค์ • ๋ฐ ๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•

Querydsl ์„ค์ • (Spring Boot ๊ธฐ์ค€)

1. Gradle ์˜์กด์„ฑ ์ถ”๊ฐ€

dependencies { implementation 'com.querydsl:querydsl-jpa:5.0.0' annotationProcessor 'com.querydsl:querydsl-apt:5.0.0:jpa' }
Plain Text
๋ณต์‚ฌ

2. QClass ์ž๋™ ์ƒ์„ฑ ํ”Œ๋Ÿฌ๊ทธ์ธ ์ถ”๊ฐ€

tasks.withType(JavaCompile).configureEach { options.annotationProcessorGeneratedSourcesDirectory = file("$buildDir/generated") }
Plain Text
๋ณต์‚ฌ

3. QClass ๋นŒ๋“œ (์ž๋™ ์ƒ์„ฑ)

./gradlew clean compileJava
Plain Text
๋ณต์‚ฌ
์‹คํ–‰ ํ›„ build/generated ํด๋”์— QEntity ํด๋ž˜์Šค ์ž๋™ ์ƒ์„ฑ๋จ

Querydsl ํ•ต์‹ฌ ์‚ฌ์šฉ๋ฒ•

JPAQueryFactory ์„ค์ •

@RequiredArgsConstructor @Repository public class UserRepository { private final JPAQueryFactory queryFactory; }
Java
๋ณต์‚ฌ
JPAQueryFactory๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด EntityManager ์—†์ด๋„ ์ฟผ๋ฆฌ ์‹คํ–‰ ๊ฐ€๋Šฅ

์ „์ฒด ์กฐํšŒ (SELECT)

public List<Users> findAllUsers() { QUsers users = QUsers.users; // Querydsl์ด ์ž๋™ ์ƒ์„ฑํ•œ QClass return queryFactory .selectFrom(users) .fetch(); }
Java
๋ณต์‚ฌ
selectFrom(QEntity)์„ ์‚ฌ์šฉํ•˜์—ฌ ์ „์ฒด ๋ฐ์ดํ„ฐ๋ฅผ ์กฐํšŒ

์กฐ๊ฑด ๊ฒ€์ƒ‰ (WHERE)

public Users findByUsername(String username) { QUsers users = QUsers.users; return queryFactory .selectFrom(users) .where(users.username.eq(username)) .fetchOne(); }
Java
๋ณต์‚ฌ
users.username.eq(username) โ†’ WHERE username = 'John'

์—ฌ๋Ÿฌ ์กฐ๊ฑด ๋™์  ๊ฒ€์ƒ‰ (BooleanExpression ํ™œ์šฉ)

public List<Users> findUsers(String username, Integer age) { QUsers users = QUsers.users; return queryFactory .selectFrom(users) .where(eqUsername(username), eqAge(age)) .fetch(); } private BooleanExpression eqUsername(String username) { return username != null ? QUsers.users.username.eq(username) : null; } private BooleanExpression eqAge(Integer age) { return age != null ? QUsers.users.age.eq(age) : null; }
Java
๋ณต์‚ฌ
BooleanExpression์„ ํ™œ์šฉํ•˜๋ฉด ๋™์  ์ฟผ๋ฆฌ๋ฅผ ๊ฐ„๊ฒฐํ•˜๊ฒŒ ์ž‘์„ฑ ๊ฐ€๋Šฅ

์ •๋ ฌ (ORDER BY)

public List<Users> findUsersSortedByAge() { QUsers users = QUsers.users; return queryFactory .selectFrom(users) .orderBy(users.age.desc()) // ๋‚˜์ด ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ .fetch(); }
Java
๋ณต์‚ฌ

ํŽ˜์ด์ง• (LIMIT & OFFSET)

public List<Users> findUsersWithPaging(int offset, int limit) { QUsers users = QUsers.users; return queryFactory .selectFrom(users) .orderBy(users.id.asc()) .offset(offset) .limit(limit) .fetch(); }
Java
๋ณต์‚ฌ
offset(0).limit(10) โ†’ ์ฒซ ๋ฒˆ์งธ ํŽ˜์ด์ง€, 10๊ฐœ ๋ฐ์ดํ„ฐ ์กฐํšŒ

JOIN ํ™œ์šฉ

public List<Boards> findBoardsWithUser(Long userId) { QBoards boards = QBoards.boards; QUsers users = QUsers.users; return queryFactory .selectFrom(boards) .join(boards.user, users) // Boards โ†’ Users JOIN .where(users.id.eq(userId)) .fetch(); }
Java
๋ณต์‚ฌ
join(boards.user, users) โ†’ Boards ์—”ํ„ฐํ‹ฐ์—์„œ Users์™€ ์กฐ์ธ

DTO ๋งคํ•‘ (Projections ํ™œ์šฉ)

public List<UserDTO> findUsersAsDTO() { QUsers users = QUsers.users; return queryFactory .select(Projections.constructor(UserDTO.class, users.id, users.username, users.age)) .from(users) .fetch(); }
Java
๋ณต์‚ฌ
Projections.constructor()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ DTO๋กœ ๋ณ€ํ™˜

Querydsl vs JPQL vs Criteria API ๋น„๊ต

๋น„๊ต ํ•ญ๋ชฉ
Querydsl
JPQL
Criteria API
ํƒ€์ž… ์•ˆ์ „์„ฑ
๋†’์Œ (์ปดํŒŒ์ผ ํƒ€์ž„ ์ฒดํฌ)
์—†์Œ
์žˆ์Œ
๊ฐ€๋…์„ฑ
์šฐ์ˆ˜
๋ณต์žกํ•จ
๊ธธ๊ณ  ๊ฐ€๋…์„ฑ ๋‚ฎ์Œ
๋™์  ์ฟผ๋ฆฌ ์ง€์›
์šฐ์ˆ˜
๋ถ€์กฑ
๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ฝ”๋“œ ๋ณต์žก
ํŽ˜์ด์ง• ์ง€์›
๊ฐ„๊ฒฐ
์ง์ ‘ ์ž‘์„ฑํ•ด์•ผ ํ•จ
๊ฐ€๋Šฅ
JOIN ์ฒ˜๋ฆฌ
์ง๊ด€์ 
๋ณต์žก
๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ฝ”๋“œ ๊ธธ์–ด์ง

Querydsl ์š”์•ฝ

1.
ํƒ€์ž… ์•ˆ์ „ํ•œ ๋™์  ์ฟผ๋ฆฌ ์ž‘์„ฑ ๊ฐ€๋Šฅ
2.
JPQL๋ณด๋‹ค ๊ฐ„๊ฒฐํ•˜๊ณ  ์œ ์ง€๋ณด์ˆ˜์„ฑ์ด ๋›ฐ์–ด๋‚จ
3.
๋ฉ”์„œ๋“œ ์ฒด์ด๋‹ ๋ฐฉ์‹์œผ๋กœ ๊ฐ€๋…์„ฑ์ด ๋†’์Œ
4.
QClass ์ž๋™ ์ƒ์„ฑ์œผ๋กœ ๊ฐœ๋ฐœ ํŽธ์˜์„ฑ ์ฆ๊ฐ€
5.
ํŽ˜์ด์ง•, JOIN, DTO ๋ณ€ํ™˜์„ ์‰ฝ๊ฒŒ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ
Querydsl์„ ํ™œ์šฉํ•˜๋ฉด ๋ณต์žกํ•œ JPQL์„ ์‰ฝ๊ฒŒ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค!