์ผ๋ฐ์ ์ผ๋ก Spring Boot ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ, Spring Data JPA๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ณธ CRUD ๋ฉ์๋์ Named Query ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์๋๋ ๊ฒฝ์ฐ๋ JPQL์ด๋ Native Query๋ก ์์ฑํ๊ฒ ๋๋ค. ํ์ง๋ง ๋ณต์กํ ์ฟผ๋ฆฌ ๋ก์ง์ ๊ฐ์ง ๊ฒฝ์ฐ์๋ ๋ฉ์๋๋ ์ฟผ๋ฆฌ๋ฌธ์ด ๊ธธ์ด์ ธ์ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๊ฒ ๋๋ค. ํนํ Paging์ด๋ Sorting์ ๊ตฌํํ๊ฒ ๋ ๊ฒฝ์ฐ, ๊ตฌํ์ด ๋ณต์กํด์ง๊ฒ ๋๋ค.
๐จ ๋ฌธ์ ์ํฉ
๋์ ๊ฒฝ์ฐ์๋ ์ด์ ํ๋ก์ ํธ๊น์ง Spring Data JPA๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ CRUD ๋ฉ์๋์ Named Query๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ก์ง์ ๊ตฌํํ์๊ณ , ์กฐ๊ธ ๋ ๋ณต์กํ ์ฟผ๋ฆฌ์ ๊ฒฝ์ฐ์๋ ๊ฐ๋ ์ฑ์ ์กฐ๊ธ์ ๋จ์ด์ง์ง๋ง Native Query๋ก ๋ณ ๋ฌธ์ ์์ด ๊ตฌํ์ ํ์๋ค. ํ์ง๋ง ์ต๊ทผ ํ๋ก์ ํธ์์ ๊ฒ์ ์กฐ๊ฑด์ด ๋ง๊ณ , Paging๋ ํ๊ณ , Sorting๋ ํ๊ณ , ์ด ๊ฒ์ ๊ฒฐ๊ณผ ๊ฐ์๊น์ง ๋ฐํํด์ผ ํ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ์๋ค.
์ด๊ธฐ ๊ตฌํ์์๋ Paging๊ณผ Counting์ด ์์ด์, ๊ฐ๋ ์ฑ์ด ์ ์ข์์ด๋ named Query๋ก Sorting์ ๊ตฌํํ์๋ค. ํ์ง๋ง Paging๊ณผ Counting์ด ์ถ๊ฐ๋๋ฉด์ JpaRepository์์์ ๋ฉ์๋๋ ๋ณต์กํด์ง๊ธฐ ์์ํ์ผ๋ฉฐ, ๊ทธ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ Service ๋จ์ ๋ก์ง๋ ๋ณต์กํด์ง๊ธฐ ์์ํ๋ค. ์ด์ฐ์ด์ฐ ๊ตฌํ์ ํ ์ ์๊ฒ ์ง๋ง, ๋ก์ง์ด ๋ณต์กํ๊ณ ์ฝ๋๊ฐ ๊ธธ์ด ๋นํจ์จ์ ์ด๊ณ ๊ฐ๋นํ ์ ์๊ฒ ๋์๋ค. QueryDSL์ ์ฒ์ ๋์ ํด๋ณด๋ ๊ฒ์ด๊ณ , ํนํ Page ๊ฐ์ฒด๋ ๋ฐ๋ก ์์ด์ ์ด๋ ค์๋ณด์์ง๋ง.. ๋ ๋์์ฌ ์ ์๋ ๊ฐ์ ๊ฑด๋๊ธฐ ์ ์ QueryDSL๋ก ์ ํํ๊ธฐ๋ก ํ๋ค. ๋๋ถ์ QueryDSL์ ํ์์ฑ๊ณผ ์ค์์ฑ์ ๋๋ผ๊ฒ ๋ ๊ฒ ๊ฐ๋ค!
QueryDSL ๋์ ์ (+ Paging, Counting ๊ตฌํ ์ )
Repository
์ ๋ ฌ์ ๊ตฌํ๋ ํ์ด์ง ๋์ ์ ์ฝ๋์ด๋ค.
public interface TicketRepository extends JpaRepository<Ticket, Long> {
//priceRow
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByPriceAsc(String departure, String destination, String date, int minPrice, int maxPrice);
//depTimeEarly
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeAsc(String departure, String destination, String date, int minPrice, int maxPrice);
//depTimeLate
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeDesc(String departure, String destination, String date, int minPrice, int maxPrice);
//flightTimeRow
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByTotalTimeNumAsc(String departure, String destination, String date, int minPrice, int maxPrice);
}
๊ฐ ์ ๋ ฌ ์กฐ๊ฑด ๋ณ๋ก Named Query๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์๋๋ค์ ์์ฑ์ ํ๋๋ฐ ๊ต์ฅํ ๊ธธ๊ณ ๊ฐ๋ ์ฑ์ด ์ข์ง ์๋ค..
Service
Switch Case ๋ฌธ์ผ๋ก ์ ๋ ฌ ์กฐ๊ฑด์ ๋ฐ๋ผ ๊ตฌํ๋ Repository ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
@Service
public class TicketServiceImpl implements TicketService {
@Autowired
TicketRepository ticketRepository;
@Override
public List<TicketRes> getTicketList(OneWayTicketReq ticketReq) {
...
//๊ฒ์
if(direct[0]) { //๊ฒฝ์ ํํฐ : ์ ์ฒด
switch (sortType) {
case "priceRow" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByPriceAsc(departure, destination, date, minPrice, maxPrice);
break;
}
case "gapHigh" : {
//๋น์ ๊ตฌํ์ค
break;
}
case "depTimeEarly" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeAsc(departure, destination, date, minPrice, maxPrice);
break;
}
case "depTimeLate" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeDesc(departure, destination, date, minPrice, maxPrice);
break;
}
case "flightTimeRow" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByTotalTimeNumAsc(departure, destination, date, minPrice, maxPrice);
break;
}
}
} else {
...
}
//๊ฒฐ๊ณผ๊ฐ ๋ฐํ
...
return result;
}
}
๐ง ์ค์ ํ๊ธฐ
Gradle
Spring Data JPA์ ๊ฒฝ์ฐ, Gradle์ ํ ์ค๋ง ์ถ๊ฐํ๋ฉด ๋๋ค.
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
...
}
QueryDSL์ ์ถ๊ฐํ๋ค๋ฉด ์ด๊ธฐ ์ค์ ์ด ๋ณต์กํ๋ค.
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
...
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
...
dependencies {
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
...
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
...
QClass ์์ฑํ๊ธฐ
Gradle > other > compileQuerydsl ํด๋ฆญํ๋ค.
๊ทธ๋ฌ๋ฉด ํ๋ก์ ํธ์ build > generated > querydsl ํด๋๊ฐ ์๊ธฐ๊ณ , Entity์ QClass๊ฐ ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.


Repository
public interface TicketRepository extends JpaRepository<Ticket, Long>, TicketRepositoryCustom {
}
๊ธฐ์กด์ JPA๋ฅผ ์ฌ์ฉํ๋ค๋ฉด Repository interface๊ฐ ์์ ๊ฒ์ด๋ค. ์ฌ๊ธฐ์ ๊ธฐ๋ณธ Crud์ named Query๋ฅผ ์ํ JpaRepository๋ฟ๋ง ์๋๋ผ, ์ง์ ๋ง๋ RepositoryCustom์ ์์๋ฐ๋๋ค.
RepositoryCustom
public interface TicketRepositoryCustom {
Page<Ticket> findAllTickets(Pageable pageable, String departure, String destination, String date, int minPrice, int maxPrice);
}
QueryDSL๋ก ๊ตฌํํ ๋ฉ์๋๋ฅผ ์์ฑํด์ค๋ค.
RepositoryCustomImpl
์ค๋ฒ๋ผ์ด๋ฉํ ๋ฉ์๋ ์์ QueryDSL์ ์์ฑํด์ค๋ค.
public class TicketRepositoryImpl implements TicketRepositoryCustom {
private final JPAQueryFactory queryFactory;
public TicketRepositoryImpl(EntityManager em){
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public Page<Ticket> findAllTickets(Pageable pageable, String departure, String destination, String date, int minPrice, int maxPrice) {
JPAQuery<Ticket> query = queryFactory
.selectFrom(ticket)
.leftJoin(ticket.flightList, flight)
.leftJoin(ticket.tendency, tendency)
.where(ticket.depCode.eq(departure),
ticket.arrCode.eq(destination),
ticket.depDate.eq(date),
ticket.price.between(minPrice, maxPrice)
)
.distinct();
sortAndOrder(query, pageable);
List<Ticket> ticketList = query
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
Long totalCount = queryFactory
.select(ticket.countDistinct())
.from(ticket)
.leftJoin(ticket.flightList, flight)
.leftJoin(ticket.tendency, tendency)
.where(ticket.depCode.eq(departure),
ticket.arrCode.eq(destination),
ticket.depDate.eq(date),
ticket.price.between(minPrice, maxPrice)
)
.fetchOne();
return new PageImpl<>(ticketList, pageable, totalCount);
}
private void sortAndOrder(JPAQuery<Ticket> query, Pageable pageable) {
for (Sort.Order o : pageable.getSort()) {
if(o.getProperty().equals("updown")) {
PathBuilder pathBuilder = new PathBuilder(tendency.getType(), tendency.getMetadata());
query.orderBy(new OrderSpecifier(o.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.get(o.getProperty())), new OrderSpecifier(Order.ASC,
ticket.depTime), new OrderSpecifier(Order.ASC,
ticket.arrTime));
} else {
PathBuilder pathBuilder = new PathBuilder(ticket.getType(), ticket.getMetadata());
query.orderBy(new OrderSpecifier(o.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.get(o.getProperty())), new OrderSpecifier(Order.ASC,
ticket.depTime));
}
}
}
}
๐ QueryDSL ๋ฉ์๋
- select(ticket) : ticket์ ๋ชจ๋ ์นผ๋ผ์ ์กฐํํ๋ค.
- from(ticket) : ticket ํ ์ด๋ธ์์ ๊ฐ์ ธ์จ๋ค.
- selectFrom() : select์ from์ ํฉ์น ๋ฉ์๋๋ค.
- join(), leftJoin() : ๊ทธ๋ฅ join์ innerJoin์ด ๋๊ณ , outer join์ ํ๊ณ ์ถ๋ค๋ฉด left, right๋ฅผ ๋ถ์ฌ์ค์ผ ํ๋ค.
- where() : ์กฐ๊ฑด์ ๋ฃ๋๋ค
- eq()
- between(a, b)
- distict() : ์ค๋ณต๋๋ ์นผ๋ผ์ ์ ๊ฑฐํ๋ค.
- fetch() : query๋ฅผ ์์ฑํด ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
ํ์ด์ง
- offset() : ์ป๊ณ ์ ํ๋ ๊ฒฐ๊ณผ์ ํ์ด์ง ๋ฒํธ
- limit() : ์ด ํ์ด์ง ์
์ ๋ ฌ
- orderBy() : ์ํ๋ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ ๋ ฌํ๋ค.
๐ ๊ฒฐ๊ณผ
QueryDSL ๊ตฌํ์ ์ํด ์ค์ ํด์ผ ํ ๊ฒ๋ค์ด ๋ง๊ณ , QueryDSL์ ๋ฉ์๋๋ค๋ ์ตํ์ผํด์ ์ง์ ์ฅ๋ฒฝ์ด ์ข ์์๋ค.
ํ์ง๋ง Repository์์ ๊ฐ์ข named Query๋ก ๋์ฒ๋ฌ๋ ์ฝ๋์ Service ๋จ์์ Switch ๋ฌธ์ผ๋ก ๋์ฒ๋๋ ์ฝ๋๊ฐ ์ค์ด๋ค์ด ํจ์จ์ ์ด๊ฒ ๋๊ณ , ๊ฐ๋ ์ฑ๋ ์ข์์ง๊ฒ ๋์๋ค!
๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ฅ์ ์ผ๋ก๋ ์ฟผ๋ฆฌ ์ค๋ฅ๋ฅผ ์ปดํ์ผ ํ์์ ์ก์๋ผ ์ ์๋ค๋ ๊ฒ์ด๋ค.
Repository
public interface TicketRepository extends JpaRepository<Ticket, Long>, TicketRepositoryCustom {
}
Service
@Service
public class TicketServiceImpl implements TicketService {
@Autowired
TicketRepository ticketRepository;
@Autowired
MemberRepository memberRepository;
@Autowired
OnewayTicketLikeRepository onewayTicketLikeRepository;
@Autowired
RoundTicketLikeRepository roundTicketLikeRepository;
@Override
public OnewayTicketListRes getOneWayTicketList(OnewayTicketReq ticketReq) {
...
PageRequest pageRequest = PageRequest.of(page, 10, Sort.Direction.fromString(orderBy), sortType);
//๊ฒ์
long totalCount = 0;
List<Ticket> ticketList = new ArrayList<>();
if(direct[0]) { //๊ฒฝ์ ํํฐ : ์ ์ฒด
Page<Ticket> result = ticketRepository.findAllTickets(pageRequest, departure, destination, date, minPrice, maxPrice);
ticketList = result.getContent();
totalCount += result.getTotalElements();
}else {
...
}
//๊ฒฐ๊ณผ๊ฐ ๋ฐํ
...
return onewayTicketListRes;
}
}
๐ ์ฐธ๊ณ ์๋ฃ
https://velog.io/@jkijki12/Spring-QueryDSL-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
[Spring] QueryDSL ์๋ฒฝ ์ดํดํ๊ธฐ
QueryDsl !
velog.io
[ClassFlix] EP 17. QueryDSL ๋์ (ํ์ด์ง, ์ ๋ ฌ)
Querydsl ๋์ ๊ณํ build.gradle ์์ฑ, JPAQueryFactory๋ฑ Querydsl ๋์ ๊ธฐ์กด์ ์๋ jpql์ querydsl๋ก ๋ฐ๊พธ๊ธฐ ํ ํ๋ฉด์์ ํ์ด์ง, ์ ๋ ฌ ๊ธฐ๋ฅ (๋๋ ๊ฐ์ ๋ฐ์ดํฐ ์ถ๊ฐ, ddl ์ค์ ๋ณ๊ฒฝ) ๊ฐ์ ๊ฒ์๊ธฐ๋ฅ์ถ๊ฐ (ํ์ด์ง
ksabs.tistory.com
'Backend > Spring Boot' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
์ผ๋ฐ์ ์ผ๋ก Spring Boot ํ๊ฒฝ์์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ฐ, Spring Data JPA๊ฐ ์ ๊ณตํ๋ ๊ธฐ๋ณธ CRUD ๋ฉ์๋์ Named Query ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์๋๋ ๊ฒฝ์ฐ๋ JPQL์ด๋ Native Query๋ก ์์ฑํ๊ฒ ๋๋ค. ํ์ง๋ง ๋ณต์กํ ์ฟผ๋ฆฌ ๋ก์ง์ ๊ฐ์ง ๊ฒฝ์ฐ์๋ ๋ฉ์๋๋ ์ฟผ๋ฆฌ๋ฌธ์ด ๊ธธ์ด์ ธ์ ๊ฐ๋ ์ฑ์ด ๋จ์ด์ง๊ฒ ๋๋ค. ํนํ Paging์ด๋ Sorting์ ๊ตฌํํ๊ฒ ๋ ๊ฒฝ์ฐ, ๊ตฌํ์ด ๋ณต์กํด์ง๊ฒ ๋๋ค.
๐จ ๋ฌธ์ ์ํฉ
๋์ ๊ฒฝ์ฐ์๋ ์ด์ ํ๋ก์ ํธ๊น์ง Spring Data JPA๊ฐ ๊ธฐ๋ณธ์ ์ผ๋ก ์ ๊ณตํ๋ CRUD ๋ฉ์๋์ Named Query๋ก ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์ค๋ ๋ก์ง์ ๊ตฌํํ์๊ณ , ์กฐ๊ธ ๋ ๋ณต์กํ ์ฟผ๋ฆฌ์ ๊ฒฝ์ฐ์๋ ๊ฐ๋ ์ฑ์ ์กฐ๊ธ์ ๋จ์ด์ง์ง๋ง Native Query๋ก ๋ณ ๋ฌธ์ ์์ด ๊ตฌํ์ ํ์๋ค. ํ์ง๋ง ์ต๊ทผ ํ๋ก์ ํธ์์ ๊ฒ์ ์กฐ๊ฑด์ด ๋ง๊ณ , Paging๋ ํ๊ณ , Sorting๋ ํ๊ณ , ์ด ๊ฒ์ ๊ฒฐ๊ณผ ๊ฐ์๊น์ง ๋ฐํํด์ผ ํ๋ ๋ฉ์๋๋ฅผ ๊ตฌํํด์ผ ํ์๋ค.
์ด๊ธฐ ๊ตฌํ์์๋ Paging๊ณผ Counting์ด ์์ด์, ๊ฐ๋ ์ฑ์ด ์ ์ข์์ด๋ named Query๋ก Sorting์ ๊ตฌํํ์๋ค. ํ์ง๋ง Paging๊ณผ Counting์ด ์ถ๊ฐ๋๋ฉด์ JpaRepository์์์ ๋ฉ์๋๋ ๋ณต์กํด์ง๊ธฐ ์์ํ์ผ๋ฉฐ, ๊ทธ ์ฟผ๋ฆฌ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ Service ๋จ์ ๋ก์ง๋ ๋ณต์กํด์ง๊ธฐ ์์ํ๋ค. ์ด์ฐ์ด์ฐ ๊ตฌํ์ ํ ์ ์๊ฒ ์ง๋ง, ๋ก์ง์ด ๋ณต์กํ๊ณ ์ฝ๋๊ฐ ๊ธธ์ด ๋นํจ์จ์ ์ด๊ณ ๊ฐ๋นํ ์ ์๊ฒ ๋์๋ค. QueryDSL์ ์ฒ์ ๋์ ํด๋ณด๋ ๊ฒ์ด๊ณ , ํนํ Page ๊ฐ์ฒด๋ ๋ฐ๋ก ์์ด์ ์ด๋ ค์๋ณด์์ง๋ง.. ๋ ๋์์ฌ ์ ์๋ ๊ฐ์ ๊ฑด๋๊ธฐ ์ ์ QueryDSL๋ก ์ ํํ๊ธฐ๋ก ํ๋ค. ๋๋ถ์ QueryDSL์ ํ์์ฑ๊ณผ ์ค์์ฑ์ ๋๋ผ๊ฒ ๋ ๊ฒ ๊ฐ๋ค!
QueryDSL ๋์ ์ (+ Paging, Counting ๊ตฌํ ์ )
Repository
์ ๋ ฌ์ ๊ตฌํ๋ ํ์ด์ง ๋์ ์ ์ฝ๋์ด๋ค.
public interface TicketRepository extends JpaRepository<Ticket, Long> {
//priceRow
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByPriceAsc(String departure, String destination, String date, int minPrice, int maxPrice);
//depTimeEarly
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeAsc(String departure, String destination, String date, int minPrice, int maxPrice);
//depTimeLate
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeDesc(String departure, String destination, String date, int minPrice, int maxPrice);
//flightTimeRow
List<Ticket> findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByTotalTimeNumAsc(String departure, String destination, String date, int minPrice, int maxPrice);
}
๊ฐ ์ ๋ ฌ ์กฐ๊ฑด ๋ณ๋ก Named Query๋ฅผ ์ฌ์ฉํ์ฌ ๋ฉ์๋๋ค์ ์์ฑ์ ํ๋๋ฐ ๊ต์ฅํ ๊ธธ๊ณ ๊ฐ๋ ์ฑ์ด ์ข์ง ์๋ค..
Service
Switch Case ๋ฌธ์ผ๋ก ์ ๋ ฌ ์กฐ๊ฑด์ ๋ฐ๋ผ ๊ตฌํ๋ Repository ๋ฉ์๋๋ฅผ ํธ์ถํ๋ค.
@Service
public class TicketServiceImpl implements TicketService {
@Autowired
TicketRepository ticketRepository;
@Override
public List<TicketRes> getTicketList(OneWayTicketReq ticketReq) {
...
//๊ฒ์
if(direct[0]) { //๊ฒฝ์ ํํฐ : ์ ์ฒด
switch (sortType) {
case "priceRow" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByPriceAsc(departure, destination, date, minPrice, maxPrice);
break;
}
case "gapHigh" : {
//๋น์ ๊ตฌํ์ค
break;
}
case "depTimeEarly" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeAsc(departure, destination, date, minPrice, maxPrice);
break;
}
case "depTimeLate" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByDepTimeDesc(departure, destination, date, minPrice, maxPrice);
break;
}
case "flightTimeRow" : {
ticketList = ticketRepository.findByDepCodeAndArrCodeAndDepDateAndPriceBetweenOrderByTotalTimeNumAsc(departure, destination, date, minPrice, maxPrice);
break;
}
}
} else {
...
}
//๊ฒฐ๊ณผ๊ฐ ๋ฐํ
...
return result;
}
}
๐ง ์ค์ ํ๊ธฐ
Gradle
Spring Data JPA์ ๊ฒฝ์ฐ, Gradle์ ํ ์ค๋ง ์ถ๊ฐํ๋ฉด ๋๋ค.
dependencies {
...
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
...
}
QueryDSL์ ์ถ๊ฐํ๋ค๋ฉด ์ด๊ธฐ ์ค์ ์ด ๋ณต์กํ๋ค.
buildscript {
ext {
queryDslVersion = "5.0.0"
}
}
plugins {
...
id "com.ewerk.gradle.plugins.querydsl" version "1.0.10"
}
...
dependencies {
implementation "com.querydsl:querydsl-jpa:${queryDslVersion}"
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}"
...
}
def querydslDir = "$buildDir/generated/querydsl"
querydsl {
jpa = true
querydslSourcesDir = querydslDir
}
sourceSets {
main.java.srcDir querydslDir
}
configurations {
querydsl.extendsFrom compileClasspath
}
compileQuerydsl {
options.annotationProcessorPath = configurations.querydsl
}
...
QClass ์์ฑํ๊ธฐ
Gradle > other > compileQuerydsl ํด๋ฆญํ๋ค.
๊ทธ๋ฌ๋ฉด ํ๋ก์ ํธ์ build > generated > querydsl ํด๋๊ฐ ์๊ธฐ๊ณ , Entity์ QClass๊ฐ ์์ฑ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค.


Repository
public interface TicketRepository extends JpaRepository<Ticket, Long>, TicketRepositoryCustom {
}
๊ธฐ์กด์ JPA๋ฅผ ์ฌ์ฉํ๋ค๋ฉด Repository interface๊ฐ ์์ ๊ฒ์ด๋ค. ์ฌ๊ธฐ์ ๊ธฐ๋ณธ Crud์ named Query๋ฅผ ์ํ JpaRepository๋ฟ๋ง ์๋๋ผ, ์ง์ ๋ง๋ RepositoryCustom์ ์์๋ฐ๋๋ค.
RepositoryCustom
public interface TicketRepositoryCustom {
Page<Ticket> findAllTickets(Pageable pageable, String departure, String destination, String date, int minPrice, int maxPrice);
}
QueryDSL๋ก ๊ตฌํํ ๋ฉ์๋๋ฅผ ์์ฑํด์ค๋ค.
RepositoryCustomImpl
์ค๋ฒ๋ผ์ด๋ฉํ ๋ฉ์๋ ์์ QueryDSL์ ์์ฑํด์ค๋ค.
public class TicketRepositoryImpl implements TicketRepositoryCustom {
private final JPAQueryFactory queryFactory;
public TicketRepositoryImpl(EntityManager em){
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public Page<Ticket> findAllTickets(Pageable pageable, String departure, String destination, String date, int minPrice, int maxPrice) {
JPAQuery<Ticket> query = queryFactory
.selectFrom(ticket)
.leftJoin(ticket.flightList, flight)
.leftJoin(ticket.tendency, tendency)
.where(ticket.depCode.eq(departure),
ticket.arrCode.eq(destination),
ticket.depDate.eq(date),
ticket.price.between(minPrice, maxPrice)
)
.distinct();
sortAndOrder(query, pageable);
List<Ticket> ticketList = query
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.fetch();
Long totalCount = queryFactory
.select(ticket.countDistinct())
.from(ticket)
.leftJoin(ticket.flightList, flight)
.leftJoin(ticket.tendency, tendency)
.where(ticket.depCode.eq(departure),
ticket.arrCode.eq(destination),
ticket.depDate.eq(date),
ticket.price.between(minPrice, maxPrice)
)
.fetchOne();
return new PageImpl<>(ticketList, pageable, totalCount);
}
private void sortAndOrder(JPAQuery<Ticket> query, Pageable pageable) {
for (Sort.Order o : pageable.getSort()) {
if(o.getProperty().equals("updown")) {
PathBuilder pathBuilder = new PathBuilder(tendency.getType(), tendency.getMetadata());
query.orderBy(new OrderSpecifier(o.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.get(o.getProperty())), new OrderSpecifier(Order.ASC,
ticket.depTime), new OrderSpecifier(Order.ASC,
ticket.arrTime));
} else {
PathBuilder pathBuilder = new PathBuilder(ticket.getType(), ticket.getMetadata());
query.orderBy(new OrderSpecifier(o.isAscending() ? Order.ASC : Order.DESC,
pathBuilder.get(o.getProperty())), new OrderSpecifier(Order.ASC,
ticket.depTime));
}
}
}
}
๐ QueryDSL ๋ฉ์๋
- select(ticket) : ticket์ ๋ชจ๋ ์นผ๋ผ์ ์กฐํํ๋ค.
- from(ticket) : ticket ํ ์ด๋ธ์์ ๊ฐ์ ธ์จ๋ค.
- selectFrom() : select์ from์ ํฉ์น ๋ฉ์๋๋ค.
- join(), leftJoin() : ๊ทธ๋ฅ join์ innerJoin์ด ๋๊ณ , outer join์ ํ๊ณ ์ถ๋ค๋ฉด left, right๋ฅผ ๋ถ์ฌ์ค์ผ ํ๋ค.
- where() : ์กฐ๊ฑด์ ๋ฃ๋๋ค
- eq()
- between(a, b)
- distict() : ์ค๋ณต๋๋ ์นผ๋ผ์ ์ ๊ฑฐํ๋ค.
- fetch() : query๋ฅผ ์์ฑํด ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
ํ์ด์ง
- offset() : ์ป๊ณ ์ ํ๋ ๊ฒฐ๊ณผ์ ํ์ด์ง ๋ฒํธ
- limit() : ์ด ํ์ด์ง ์
์ ๋ ฌ
- orderBy() : ์ํ๋ ์กฐ๊ฑด์ ๋ฐ๋ผ ์ ๋ ฌํ๋ค.
๐ ๊ฒฐ๊ณผ
QueryDSL ๊ตฌํ์ ์ํด ์ค์ ํด์ผ ํ ๊ฒ๋ค์ด ๋ง๊ณ , QueryDSL์ ๋ฉ์๋๋ค๋ ์ตํ์ผํด์ ์ง์ ์ฅ๋ฒฝ์ด ์ข ์์๋ค.
ํ์ง๋ง Repository์์ ๊ฐ์ข named Query๋ก ๋์ฒ๋ฌ๋ ์ฝ๋์ Service ๋จ์์ Switch ๋ฌธ์ผ๋ก ๋์ฒ๋๋ ์ฝ๋๊ฐ ์ค์ด๋ค์ด ํจ์จ์ ์ด๊ฒ ๋๊ณ , ๊ฐ๋ ์ฑ๋ ์ข์์ง๊ฒ ๋์๋ค!
๊ทธ๋ฆฌ๊ณ ๋ค๋ฅธ ์ฅ์ ์ผ๋ก๋ ์ฟผ๋ฆฌ ์ค๋ฅ๋ฅผ ์ปดํ์ผ ํ์์ ์ก์๋ผ ์ ์๋ค๋ ๊ฒ์ด๋ค.
Repository
public interface TicketRepository extends JpaRepository<Ticket, Long>, TicketRepositoryCustom {
}
Service
@Service
public class TicketServiceImpl implements TicketService {
@Autowired
TicketRepository ticketRepository;
@Autowired
MemberRepository memberRepository;
@Autowired
OnewayTicketLikeRepository onewayTicketLikeRepository;
@Autowired
RoundTicketLikeRepository roundTicketLikeRepository;
@Override
public OnewayTicketListRes getOneWayTicketList(OnewayTicketReq ticketReq) {
...
PageRequest pageRequest = PageRequest.of(page, 10, Sort.Direction.fromString(orderBy), sortType);
//๊ฒ์
long totalCount = 0;
List<Ticket> ticketList = new ArrayList<>();
if(direct[0]) { //๊ฒฝ์ ํํฐ : ์ ์ฒด
Page<Ticket> result = ticketRepository.findAllTickets(pageRequest, departure, destination, date, minPrice, maxPrice);
ticketList = result.getContent();
totalCount += result.getTotalElements();
}else {
...
}
//๊ฒฐ๊ณผ๊ฐ ๋ฐํ
...
return onewayTicketListRes;
}
}
๐ ์ฐธ๊ณ ์๋ฃ
https://velog.io/@jkijki12/Spring-QueryDSL-%EC%99%84%EB%B2%BD-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B8%B0
[Spring] QueryDSL ์๋ฒฝ ์ดํดํ๊ธฐ
QueryDsl !
velog.io
[ClassFlix] EP 17. QueryDSL ๋์ (ํ์ด์ง, ์ ๋ ฌ)
Querydsl ๋์ ๊ณํ build.gradle ์์ฑ, JPAQueryFactory๋ฑ Querydsl ๋์ ๊ธฐ์กด์ ์๋ jpql์ querydsl๋ก ๋ฐ๊พธ๊ธฐ ํ ํ๋ฉด์์ ํ์ด์ง, ์ ๋ ฌ ๊ธฐ๋ฅ (๋๋ ๊ฐ์ ๋ฐ์ดํฐ ์ถ๊ฐ, ddl ์ค์ ๋ณ๊ฒฝ) ๊ฐ์ ๊ฒ์๊ธฐ๋ฅ์ถ๊ฐ (ํ์ด์ง
ksabs.tistory.com