๐ก Querydsl์ด๋?
QueryDSL์ ์ ์ ํ์ ์ ์ด์ฉํด์ SQL๊ณผ ๊ฐ์ ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๋๋ก ํด ์ฃผ๋ ์คํ์์ค ํ๋ ์์ํฌ์ ๋๋ค.
์ฟผ๋ฆฌ๋ฅผ ๋ฌธ์์ด๋ก ์์ฑํ๊ฑฐ๋ ์์ฑํ๋ ๊ฒ์ด ์๋, QueryDSL์ด ์ ๊ณตํ๋ Fluent API๋ฅผ ์ด์ฉํด ์ฝ๋ ์์ฑ์ ํ์์ผ๋ก ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ์ ์๊ฒ ๋์์ค๋๋ค.
ํ ์คํธ์ฝ๋๋ฅผ ์์ฑํ์ฌ ๋ ๊ฐ์ ์ฐจ์ด์ ์ ์์๋ณด๊ฒ ์ต๋๋ค.
- JPQL
@Test
public void startJPQL() {
//member1์ ์ฐพ์๋ผ.
String qlString =
"select m from Member m " +
"where m.username = :username";
Member findMember = em.createQuery(qlString, Member.class)
.setParameter("username", "member1")
.getSingleResult();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
- Qeurydsl
@Test
public void startQuerydsl() {
//member1์ ์ฐพ์๋ผ.
JPAQueryFactory queryFactory = new JPAQueryFactory(em);
QMember m = new QMember("m");
Member findMember = queryFactory
.select(m)
.from(m)
.where(m.username.eq("member1"))//ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ ์ฒ๋ฆฌ
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
JPQL์ผ๋ก ๋ฌธ์๋ก ์์ฑ๋๊ณ Querydsl์ ์ฝ๋๋ก ์์ฑ๋ฉ๋๋ค.
JPQL์ ์คํ์ ํด์ผ ์ค๋ฅ๋ฅผ ๋ฐ๊ฒฌํ ์ ์์ง๋ง Querydsl์ ์ปดํ์ผ์์ ์ ์ค๋ฅ๋ฅผ ์ก์ ์ ์์ต๋๋ค.
๋ํ, JPQL์ ํ๋ผ๋ฏธํฐ๋ฅผ ์ง์ ๋ฐ์ธ๋ฉํด์ผํ์ง๋ง, Querydsl์ ํ๋ผ๋ฏธํฐ ๋ฐ์ธ๋ฉ์ ์๋์ฒ๋ฆฌ ํด์ค๋๋ค.
querydsl์ ์ฌ์ฉํ๊ธฐ ์ํด์ .gradleํ์ผ์ ๊ด๋ จ ์์กด์ฑ์ ์ถ๊ฐํด์ค๋๋ค.
//Querydsl ์ถ๊ฐ
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor """com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"""
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"
์ํฐํฐ๋ฅผ ํ๋ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
@Entity
@Getter
@Setter
public class Hello {
@Id
@GeneratedValue
private Long id;
}
๋น๋ ํ ํ๋ก์ ํธ ํ์ผ์ ํ์ธํด๋ณด๋ฉด Q ํ์
์ด ์์ฑ๋๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๋จผ์ , querydsl์ ์ฌ์ฉํ๊ธฐ ์ํ ๊ธฐ๋ณธ ๊ตฌ์กฐ์ ๋ํด ์ดํด๋ณด๊ฒ ์ต๋๋ค.
@SpringBootTest
@Transactional
public class QuerydslBasicTest {
@PersistenceContext
EntityManager em;
JPAQueryFactory queryFactory;
@BeforeEach
public void before() {
queryFactory = new JPAQueryFactory(em);
//...
}
@Test
public void startQuerydsl2() {
//member1์ ์ฐพ์๋ผ.
QMember m = new QMember("m"); //Qํ์
ํด๋์ค ๊ฐ์ฒด ์์ฑ
Member findMember = queryFactory
.select(m) // select ๋ฉ์๋
.from(m) // from ๋ฉ์๋
.where(m.username.eq("member1")) // where ๋ฉ์๋
.fetchOne(); //fetch๋ฉ์๋
assertThat(findMember.getUsername()).isEqualTo("member1");
}
}
JPAQeuryFactory
๊ฐ์ฒด๋ฅผ ์์ฑํฉ๋๋ค.๋น๋ ํจํด ๊ตฌ์กฐ
๋ก ์ด๋ฃจ์ด์ ธ ์์ต๋๋ค.Qํ์
ํด๋์ค ๊ฐ์ฒด
๋ก ์ ๋ฌํฉ๋๋ค.๐ก JPAQueryFactory๋ฅผ ํ๋๋ก ์ ๊ณตํ๋ฉด ๋์์ฑ ๋ฌธ์ ๋ ์ด๋ป๊ฒ ๋ ๊น?
๋์์ฑ ๋ฌธ์ ๋ JPAQueryFactory๋ฅผ ์์ฑ ํ ๋ ์ ๊ณตํ๋ EntityManager(em)์ ๋ฌ๋ ค์์ต๋๋ค.
์คํ๋ง ํ๋ ์์ํฌ๋ ์ฌ๋ฌ ์ฐ๋ ๋์์ ๋์์ ๊ฐ์ EntityManager์ ์ ๊ทผํด๋, ํธ๋์ญ์ ๋ง๋ค ๋ณ๋์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ ๊ณตํ๊ธฐ ๋๋ฌธ์, ๋์์ฑ ๋ฌธ์ ๋ ๊ฑฑ์ ํ์ง ์์๋ ๋ฉ๋๋ค!!!
Qํด๋์ค ์ธ์คํด์ค๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ์ฌ๋ฌ๊ฐ์ง๊ฐ ์์ต๋๋ค.
1. ๋ณ์นญ์ ์ง์ ์ง์ ํ๋ ๋ฐฉ๋ฒ
QMember qMember = new QMember("m");
Qํด๋์ค๋ฅผ ์์ฑํ ๋ ์ง์ ๋ณ์นญ์ ์ง์ ํ์ฌ ์์ฑํ ์ ์์ต๋๋ค.
2. ๊ธฐ๋ณธ ์ธ์คํด์ค ์ฌ์ฉ
Qํด๋์ค๋ฅผ ์ดํด๋ณด๋ฉด ๋ด๋ถ์ ๊ธฐ๋ณธ ์ธ์คํด์ค๊ฐ ์ ์ธ๋์ด ์๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
๊ฐ์ ํ
์ด๋ธ์ ์กฐ์ธํ๋ ๊ฒฝ์ฐ๊ฐ ์๋๋ผ๋ฉด ๊ธฐ๋ณธ ์ธ์คํด์ค
๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
QMember qMember = QMember.member;
3. ๊ธฐ๋ณธ ์ธ์คํด์ค๋ฅผ static import์ ํจ๊ป ์ฌ์ฉ
import static study.querydsl.entity.QMember.*;
@Test
public void startQuerydsl3() {
//member1์ ์ฐพ์๋ผ.
Member findMember = queryFactory
.select(member)
.from(member)
.where(member.username.eq("member1"))
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
์์ ๊ฐ์ด static import๋ฅผ ์ฌ์ฉํ๋ฉด ๋ฐ๋ก ์ ์ธ์ ํ์ง ์์๋ ๋ฐ๋ก ์ธ์คํด์ค๋ฅผ ์ฐธ์กฐํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋น๊ต ์ฐ์ฐ
member.username.eq("member1") // username = 'member1'
member.username.ne("member1") //username != 'member1'
member.username.eq("member1").not() // username != 'member1'
null ๊ฒ์ฌ
member.username.isNotNull() //์ด๋ฆ์ด is not null
๋ฒ์ ๋น๊ต
member.age.in(10, 20) // age in (10,20)
member.age.notIn(10, 20) // age not in (10, 20)
member.age.between(10,30) //between 10, 30
ํฌ๊ธฐ ๋น๊ต
member.age.goe(30) // age >= 30
member.age.gt(30) // age > 30
member.age.loe(30) // age <= 30
member.age.lt(30) // age < 30
like ์ฐ์ฐ
member.username.like("member%") //like ๊ฒ์
member.username.contains("member") // like โ%member%โ ๊ฒ์
member.username.startsWith("member") //like โmember%โ ๊ฒ์
๋ฉ์๋ ์ฒด์ธ
์ผ๋ก ์ฐ๊ฒฐํ ์ ์์ต๋๋ค. @Test
public void search() {
Member findMember = queryFactory
.selectFrom(member) // select, from์ selectFrom์ผ๋ก ํฉ์น ์ ์์
.where(member.username.eq("member1")
.and(member.age.eq(10)))
.fetchOne();
assertThat(findMember.getUsername()).isEqualTo("member1");
}
.and() ๋ฉ์๋๋ฅผ ์๋ตํ๊ณ where()์ ํ๋ผ๋ฏธํฐ๋ก ๊ฒ์์กฐ๊ฑด์ ์ถ๊ฐ ํ ์๋ ์์ต๋๋ค.
์ด ๊ฒฝ์ฐ null ๊ฐ์ ๋ฌด์๋๋ฉฐ ๋ฉ์๋ ์ถ์ถ์ ํ์ฉํด์ ๋์ ์ฟผ๋ฆฌ๋ฅผ ๊น๋ํ๊ฒ ๋ง๋ค ์ ์์ต๋๋ค.
@Test
public void searchAndParam() {
List<Member> result1 = queryFactory
.selectFrom(member)
.where(member.username.eq("member1"),
member.age.eq(10))
.fetch();
assertThat(result1.size()).isEqualTo(1);
}
fetch()
: ๋ฆฌ์คํธ ์กฐํ, ๋ฐ์ดํฐ ์์ผ๋ฉด ๋น ๋ฆฌ์คํธ ๋ฐํ List<Member> fetch = queryFactory
.selectFrom(member)
.fetch();
fetchOne()
: ๋จ ๊ฑด ์กฐํnull
com.querydsl.core.NonUniqueResultException
Member findMember1 = queryFactory
.selectFrom(member)
fetchFirst()
: ์ฒ์ํ๊ฑด ์กฐํ = limit(1).fetchOne()
Member findMember2 = queryFactory
.selectFrom(member)
.fetchFirst();
fetchResults()
: ํ์ด์ง ์ ๋ณด ํฌํจ, total count ์ฟผ๋ฆฌ ์ถ๊ฐ ์คํ QueryResults<Member> results = queryFactory
.selectFrom(member)
.fetchResults();
fetchCount()
: count ์ฟผ๋ฆฌ๋ก ๋ณ๊ฒฝํด์ count ์ ์กฐํ long count = queryFactory
.selectFrom(member)
.fetchCount();
.orderBy()
desc()
, asc()
: ์ผ๋ฐ ์ ๋ ฌnullsLast()
, nullsFirst()
: null ๋ฐ์ดํฐ ์์ ๋ถ์ฌ/**
*ํ์ ์ ๋ ฌ ์์
* 1. ํ์ ๋์ด ๋ด๋ฆผ์ฐจ์(desc)
* 2. ํ์ ์ด๋ฆ ์ฌ๋ฆผ์ฐจ์(asc)
* ๋จ 2์์ ํ์ ์ด๋ฆ์ด ์์ผ๋ฉด ๋ง์ง๋ง์ ์ถ๋ ฅ(nulls last) */
@Test
public void sort() {
em.persist(new Member(null, 100));
em.persist(new Member("member5", 100));
em.persist(new Member("member6", 100));
List<Member> result = queryFactory
.selectFrom(member)
.where(member.age.eq(100))
.orderBy(member.age.desc(), member.username.asc().nullsLast())
.fetch();
Member member5 = result.get(0);
Member member6 = result.get(1);
Member memberNull = result.get(2);
assertThat(member5.getUsername()).isEqualTo("member5");
assertThat(member6.getUsername()).isEqualTo("member6");
assertThat(memberNull.getUsername()).isNull();
}
: ์กฐํ ๊ฑด์ ์ ํ
@Test
public void paging1() {
List<Member> result = queryFactory
.selectFrom(member)
.orderBy(member.username.desc()) .offset(1) //0๋ถํฐ ์์(zero index)
.limit(2) //์ต๋ 2๊ฑด ์กฐํ
.fetch();
assertThat(result.size()).isEqualTo(2);
}
๐ก ํ์ด์ง ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ๋, ๋ฐ์ดํฐ๋ฅผ ์กฐํํ๋ ์ฟผ๋ฆฌ๋ ์ฌ๋ฌ ํ ์ด๋ธ์ ์กฐ์ธํด์ผ ํ์ง๋ง, count ์ฟผ๋ฆฌ๋ ์กฐ์ธ์ด ํ์ ์๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. ๊ทธ๋ฐ๋ฐ ์๋ํ๋ count ์ฟผ๋ฆฌ(.fetchResults์ ๊ฐ์)๋ ์๋ณธ ์ฟผ๋ฆฌ์ ๊ฐ์ด ๋ชจ๋ ์กฐ์ธ์ ํด๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ด ์๋์ฌ ์ ์์ต๋๋ค. count ์ฟผ๋ฆฌ์ ์กฐ์ธ์ด ํ์์๋ ์ฑ๋ฅ ์ต์ ํ๊ฐ ํ์ํ๋ค๋ฉด, count ์ ์ฉ ์ฟผ๋ฆฌ๋ฅผ ๋ณ๋๋ก ์์ฑํด์ผ ํฉ๋๋ค.
@Test
public void aggregation() throws Exception {
List<Tuple> result = queryFactory
.select(member.count(),
member.age.sum(),
member.age.avg(),
member.age.max(),
member.age.min())
.from(member)
.fetch();
Tuple tuple = result.get(0);
assertThat(tuple.get(member.count())).isEqualTo(4);
assertThat(tuple.get(member.age.sum())).isEqualTo(100);
assertThat(tuple.get(member.age.avg())).isEqualTo(25);
assertThat(tuple.get(member.age.max())).isEqualTo(40);
assertThat(tuple.get(member.age.min())).isEqualTo(10);
}
ํ๋ก์ ์ ๋์์ด ๋ ์ด์์ผ ๊ฒฝ์ฐ ํํ์ ์ฌ์ฉํฉ๋๋ค.
@Test
public void group() throws Exception {
List<Tuple> result = queryFactory
.select(team.name, member.age.avg())
.from(member)
.join(member.team, team)
.groupBy(team.name)
.fetch();
Tuple teamA = result.get(0);
Tuple teamB = result.get(1);
assertThat(teamA.get(team.name)).isEqualTo("teamA");
assertThat(teamA.get(member.age.avg())).isEqualTo(15);
assertThat(teamB.get(team.name)).isEqualTo("teamB");
assertThat(teamB.get(member.age.avg())).isEqualTo(35);
}
๐ก having
์ ์ด์ฉํ์ฌ ๊ทธ๋ฃนํ๋ ๊ฒฐ๊ณผ๋ฅผ ์ ํํ ์๋ ์์ต๋๋ค.
.groupBy(item.price)
.having(item.price.gt(1000))