728x90
UserRepositoryCustomImpl 작성하기(방식 1)

Config 파일
@Configuration
class QuerydslConfig(
private val em: EntityManager
) {
@Bean
fun querydsl(): JPAQueryFactory{
return JPAQueryFactory(em)
}
}
- JPAQueryFactory는 entityManager를 파라미터로 받아 사용한다.
- 해당 빈을 등록해두어 QueryDsl을 사용하는 곳에서 주입받아서 사용하면 된다.
UserRepositroy
interface UserRepository : JpaRepository<User, Long>, UserRepositoryCustom{
fun findByName(name: String): User?
}
- 더 이상 @Query 만드는 코드가 없다.
- UserRepositoryCustom을 상속받는다
UserRepositoryCustom
interface UserRepositoryCustom {
fun findAllWithHistories():List<User>
}
- 추가로 구현해줄 메서드를 인터페이스로 작성한다
UserRepositoryCustomImpl
class UserRepositoryCustomImpl(
private val queryFactory: JPAQueryFactory
) : UserRepositoryCustom {
override fun findAllWithHistories(): List<User> {
return queryFactory.select(user).distinct().from(user)
//.leftJoin(user.userLoanHistories) //방법 1.
.leftJoin(userLoanHistory).on(userLoanHistory.user.id.eq(user.id)).fetchJoin().fetch() //방법2
}
}
- 빈으로 JPAQueryFactory를 주입받는다.
- UserRepositoryCustom을 상속받아 구현한다.
- Q파일을 사용할 수 있으며 static import를 사용하여 객체처럼 사용한다
- join방법은 2가지가 있다.
- leftJoin(조인 필드)
- leftJoin(조인할 Q파일).on(조인 컬럼.eq(대상 컬럼))
장점은 서비스 단에서 UserRepository를 하나만 사용할 수 있다.
단점은 인터페이스와 클래스를 항상 같이 만들어주어야 하는 것이 부담이고, 번거롭다
BookQuerydslRepository 작성하기(방식 2)
@Component
class BookQuerydslRepository(
private val queryFactory: JPAQueryFactory,
) {
fun getStats(): List<BookStatResponse> {
return queryFactory.select(
Projections.constructor(
BookStatResponse::class.java,
book.type,
book.id.count()
)
)
.from(book)
.groupBy(book.type)
.fetch()
}
}
- 패키지 위치는 domain 하위가 아닌 가장 상위 패키지에 repository를 만들어서 해당 파일을 만든다.
- 해당 파일을 스프링이 스캔할 수 있도록 @Component 애노테이션을 붙인다.(@Repository를 붙여도 무방)
- Projections.constructor()를 사용해 첫 번째 파라미터로 받은 클래스의 생성자를 통해 새로운 객체를 생성해 넣어준다.
BookService
@Service
class BookService(
private val bookRepository: BookRepository,
private val bookQuerydslRepository: BookQuerydslRepository, //추가된 주입된 클래스
private val userRepository: UserRepository,
private val userLoanHistoryRepository: UserLoanHistoryRepository,
) {
//...
@Transactional(readOnly = true)
fun getBookStatistics(): List<BookStatResponse> {
return bookQuerydslRepository.getStats()
}
}
- 더 이상 BookRepository 기능이 아닌 새로 만든 Repository 기능을 사용한다.
장점은 클래스만 바로 만들면 되어 간결하다.
단점은 서비스단에서 필요에 따라 두 Repository를 모두 사용해주어야 한다.
두 방식 모두 장단이 있지만, 강사는 두 번째 방법을 선호 -> 멀티 모듈을 사용할 경우, 모듈별로 각기 다른 Repository를 사용하는 경우가 많아 단점이 상쇄되고 장점이 극대화되기 때문이다
728x90
'실전! 코틀린과 스프링 부트로 도서관리 애플리케이션 개발하기' 카테고리의 다른 글
| Ch04. 요구사항 추가(책 통계, QueryDSL) - UserLoanHistoryRepository 리펙토링 (1) | 2022.11.08 |
|---|---|
| Ch04. 요구사항 추가(책 통계, QueryDSL) - QueryDSL 도입하기 (0) | 2022.11.08 |
| Ch04. 요구사항 추가(책 통계, QueryDSL) - 애플리케이션 대신 DB로 기능 구현 (0) | 2022.11.08 |
| Ch04. 요구사항 추가(책 통계, QueryDSL) - 책 통계 테스트 코드와 리펙토링 (0) | 2022.11.07 |
| Ch04. 요구사항 추가(책 통계, QueryDSL) - 책 통계 요구사항 추가 (0) | 2022.11.07 |