[Java/JPA] QueryDSL์ด๋?
QueryDSL
QueryDSL์ Java ๊ธฐ๋ฐ์ ํ์ ์์ (type-safe)ํ SQL ์ฟผ๋ฆฌ ์์ฑ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ์ฃผ๋ก JPA, Hibernate ๋ฑ๊ณผ ํจ๊ป ์ฌ์ฉ๋์ด ๋ณต์กํ ์ฟผ๋ฆฌ๋ฅผ ์ฝ๋๋ก ์์ ํ๊ฒ ์์ฑํ ์ ์๋๋ก ๋์์ค๋๋ค. ๋ค์์ QueryDSL์ ์ฃผ์ ํน์ง๊ณผ ์ฅ์ , ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ ์์์ ๋๋ค.
QueryDSL์ ์ ์ ํ์ ์ ์ด์ฉํด์ SQL๊ณผ ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๋๋ก ํด ์ฃผ๋ ์คํ์์ค ํ๋ ์์ํฌ์ด๋ค. Fluent API๋ฅผ ์ด์ฉํด ์ฝ๋ ์์ฑ ํ์์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๊ฒ ํ๋ค.
QueryDSL์ด ์ ํฉํ ์ํฉ์ ์๋์ ๊ฐ๋ค.
- ๋ณต์กํ ๊ฒ์ ์กฐ๊ฑด์ด ํ์ํ ๊ฒฝ์ฐ (ex. ๊ฒ์ํ ํํฐ, ๊ฒ์ ๊ธฐ๋ฅ)
- ๋์ ์ฟผ๋ฆฌ๊ฐ ์์ฃผ ํ์ํ ๋๋ฉ์ธ
- SQL ์ฟผ๋ฆฌ๋ฅผ ๋ฌธ์์ด์ด ์๋ ์์ ํ ์ฝ๋๋ก ๋ค๋ฃจ๊ณ ์ถ์ ๋
Querydsl - Unified Queries for Java
Unified Queries for Java. Querydsl is compact, safe and easy to learn. <!-- Querydsl Unified Queries for Java Querydsl provides a unified querying layer for multiple backends in Java. Compared to the alternatives Querydsl is more compact, safer and easier
querydsl.com
QueryDSL์ ์ฌ์ฉํ๋ ์ด์
- ์ฟผ๋ฆฌ๋ฅผ Java ์ฝ๋๋ก ์์ฑํ๊ธฐ ๋๋ฌธ์, ์ปดํ์ผ ์์ ์ ๋ฌธ๋ฒ ์ค๋ฅ๋ฅผ ์ก์ ์ ์๋ค.
- SQL ๋ฌธ์์ด ๊ธฐ๋ฐ ์ฟผ๋ฆฌ(JPQL, Native Query)๋ณด๋ค ๋ฐํ์ ์ค๋ฅ๊ฐ ์ค์ด๋๋ ์ฅ์ ์ด ์์.
→ JPQL์ ์ฟผ๋ฆฌ๋ฅผ ๋ฌธ์์ด๋ก ์์ฑํ๊ธฐ ๋๋ฌธ์, ์๋ชป ์์ฑํ๊ฑฐ๋ ์คํ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ ์ปดํ์ผ ์์ ์ ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋ค๊ฐ ๋ฐํ์ ์ค์ ์๋ฌ๊ฐ ๋ฐ์ํ๊ธฐ ๋๋ฌธ์ ์คํ ์ ์๋ ์๋ชป๋ ๋ถ๋ถ์ ํ์ ํ ์ ์๋ค.
- SQL ๋ฌธ์์ด ๊ธฐ๋ฐ ์ฟผ๋ฆฌ(JPQL, Native Query)๋ณด๋ค ๋ฐํ์ ์ค๋ฅ๊ฐ ์ค์ด๋๋ ์ฅ์ ์ด ์์.
- ๋์ ์ฟผ๋ฆฌ ์์ฑ์ด ์ฉ์ดํ๋ค. (Qํด๋์ค, ๋ฉ์๋ ์ฌ์ฉ)
- IF ์กฐ๊ฑด ๋ฑ์ ์ฌ์ฉํ์ฌ ์ ์ฐํ๊ฒ ์กฐ๊ฑด์ ์ถ๊ฐํ ์ ์๋ค.
- ๋ณต์กํ ๊ฒ์ ์กฐ๊ฑด(ex. ์ฌ๋ฌ ๊ฒ์ ํํฐ ์ ์ฉ)์ ์ ํฉํ๋ค.
→ JPQL์ ์ด์ฉํด ๋์ ์ฟผ๋ฆฌ๋ฅผ ๋ค๋ฃจ๊ธฐ ์ํด์๋ ๋ฌธ์์ด์ ์กฐ๊ฑด์ ๋ง๊ฒ ์กฐํฉํ์ฌ ์ฌ์ฉํด์ผ ํ๋ค. ์ด ๊ฒฝ์ฐ ์ฝ๋๊ฐ ๋ณต์กํด์ง๊ณ ๋ฐํ์ ์๋ฌ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค๋ ์น๋ช ์ ์ธ ๋จ์ ์ด ์กด์ฌํ๋ค.
- IDE ์๋์์ฑ์ ์ง์ํ๋ค.
- Qํด๋์ค๋ฅผ ํตํด ํ๋๋ช ์๋์์ฑ์ด ๊ฐ๋ฅํ์ฌ ์์ฐ์ฑ์ด ํฅ์๋๋ค.
- SQL-Likeํ ๋ฌธ๋ฒ์ Java ์ฝ๋๋ก ํํํ ์ ์๊ธฐ์ ๋ฌธ๋ฒ์ด ์ง๊ด์ ์ด๋ค.
QueryDSL ์ฌ์ฉ ์์
1. Entity ํด๋์ค
@Entity
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
private int age;
}
2. Qํด๋์ค ์์ฑ
Gradle ๋๋ Maven์์ annotationProcessor ์ค์ ์ผ๋ก Qํด๋์ค(QMember) ์๋ ์์ฑ
Maven์ ๊ฒฝ์ฐ, IntelliJ ์ฐ์ธก ๋ฉ์ด๋ธ ๋๊ตฌ - Lifecycle - compile์ ์คํํ์ฌ Qํด๋์ค๋ฅผ ์์ฑํ ์ ์๋ค.
3-1. ์ ์ ์ฟผ๋ฆฌ ์์
JPAQueryFactory queryFactory = new JPAQueryFactory(entityManager);
QMember member = QMember.member;
Member result = queryFactory
.selectFrom(member)
.where(member.username.eq("ํ๊ธธ๋"))
.fetchOne();
3-2. ๋์ ์ฟผ๋ฆฌ ์์
public List<Member> search(String usernameCond, Integer ageCond) {
return queryFactory
.selectFrom(member)
.where(
usernameEq(usernameCond),
ageEq(ageCond)
)
.fetch();
}
private BooleanExpression usernameEq(String username) {
return StringUtils.hasText(username) ? member.username.eq(username) : null;
}
private BooleanExpression ageEq(Integer age) {
return age != null ? member.age.eq(age) : null;
}
QueryDSL ๋ฉ์๋ ์ข ๋ฅ
๋ชฉ์ | ๋ฉ์๋ | ์ค๋ช |
SELECT | select(), selectFrom() | ์กฐํํ ์ปฌ๋ผ ๋๋ ์ํฐํฐ๋ฅผ ์ง์ |
FROM | from() | ์กฐํํ ํ ์ด๋ธ(Qํ์ )์ ์ง์ (selectFrom์ select + from ๊ฒฐํฉ) |
WHERE | where() | ์กฐ๊ฑด์ ์ค์ (BooleanExpression ๋ฃ์) |
GROUP BY | groupBy() | ๊ทธ๋ฃนํ |
HAVING | having() | ๊ทธ๋ฃน ์กฐ๊ฑด |
ORDER BY | orderBy() | ์ ๋ ฌ ์กฐ๊ฑด |
JOIN | join(), leftJoin(), rightJoin() | ์กฐ์ธ (inner, left, right ๋ฑ) |
UPDATE | update() | ํ ์ด๋ธ์ ๋ํ ์์ ์ฟผ๋ฆฌ |
DELETE | delete() | ํ ์ด๋ธ์ ๋ํ ์ญ์ ์ฟผ๋ฆฌ |
INSERT | insert() | SQLQueryFactory ์ฌ์ฉ ์ ๊ฐ๋ฅ, JPA์์๋ ์ง์ ์ ์ผ๋ก ์ง์ X |
FETCH | fetch(), fetchOne(), fetchFirst(), fetchResults() | ๊ฒฐ๊ณผ ๋ฐํ ๋ฉ์๋ |
WHERE ์กฐ๊ฑด์ ๋ฉ์๋ (BooleanExpression)
SQL ํํ | QueryDSL ํํ | ์ค๋ช |
= | eq() | ๊ฐ์ |
!= | ne() | ๋ค๋ฆ |
> | gt() | ํฌ๋ค (greater than) |
< | lt() | ์๋ค (less than) |
>= | goe() | ํฌ๊ฑฐ๋ ๊ฐ๋ค (greater or equal) |
<= | loe() | ์๊ฑฐ๋ ๊ฐ๋ค (less or equal) |
IN (...) | in() | ํฌํจ๋จ |
NOT IN (...) | notIn() | ํฌํจ๋์ง ์์ |
LIKE | like(), contains(), startsWith() | ๋ฌธ์์ด ํฌํจ ์ฌ๋ถ |
IS NULL | isNull() | null์ธ์ง ํ์ธ |
IS NOT NULL | isNotNull() | null์ด ์๋์ง ํ์ธ |
AND, OR | .and(), .or() | ์กฐ๊ฑด ์กฐํฉ |
NOT | .not() | ๋ถ์ ์กฐ๊ฑด |
BETWEEN A AND B | between() | ๋ฒ์ ์กฐ๊ฑด |
์์ ์ฝ๋
QMember member = QMember.member;
// SELECT * FROM member WHERE username = 'ํ๊ธธ๋'
queryFactory.selectFrom(member)
.where(member.username.eq("ํ๊ธธ๋"))
.fetch();
// WHERE age > 20 AND age < 30
queryFactory.selectFrom(member)
.where(member.age.gt(20).and(member.age.lt(30)))
.fetch();
// WHERE username IN ('a', 'b', 'c')
queryFactory.selectFrom(member)
.where(member.username.in("a", "b", "c"))
.fetch();
// WHERE age IS NOT NULL
queryFactory.selectFrom(member)
.where(member.age.isNotNull())
.fetch();
// UPDATE member SET username = '๋ณ๊ฒฝ๋จ' WHERE age < 20
queryFactory.update(member)
.set(member.username, "๋ณ๊ฒฝ๋จ")
.where(member.age.lt(20))
.execute();
// DELETE FROM member WHERE age > 60
queryFactory.delete(member)
.where(member.age.gt(60))
.execute();