Query Dslを使用して複雑な構造のデータクエリーを処理する
8762 ワード
概要
エッジプロジェクトを行う過程で、いくつかの厄介なデータ構造に関連しています.
データ構造は階層構造であり、A->B->C構造に属する.
AにBがあり、BにCの構造があり、jpa repositoryで問い合わせるとN+1の問題も発生します.
悶える
テーブル構造を根本的に変更できるかどうかを考慮し,継承関係マッピングを考慮したが,親クラスのデータは次のサブクラスでは利用できない.つまり,これは拡張の概念ではないので,この方法を用いないことにした.
そうであれば、既存のデータ構造を維持し、より効率的なクエリーを作成する必要があります.
JPQL vs QueryDsl
JPQLについては、照会はStringで直接記入する必要があるため、煩わしい.
だからコンパイル中にエラーをキャプチャできません...個人的には可読性が非常に悪いと思います
そこでQueryDslを用いて解決することにした.
既存のコード
// JpaRepository에서 호출
repository.findByEmailAndSubjectId(email, id);
結果
Hibernate:
/* select
generatedAlias0
from
UserCheckList as generatedAlias0
where
(
generatedAlias0.email=:param0
)
and (
generatedAlias0.subjectId=:param1
) */ select
usercheckl0_.id as id1_6_,
usercheckl0_.email as email2_6_,
usercheckl0_.subject_id as subject_3_6_
from
user_check_list usercheckl0_
where
usercheckl0_.email=?
and usercheckl0_.subject_id=?
Hibernate:
select
usercheckl0_.user_check_list_id as user_che3_8_0_,
usercheckl0_.id as id1_8_0_,
usercheckl0_.id as id1_8_1_,
usercheckl0_.section_title as section_2_8_1_,
usercheckl0_.user_check_list_id as user_che3_8_1_
from
user_check_list_section usercheckl0_
where
usercheckl0_.user_check_list_id=?
Hibernate:
select
elements0_.user_check_list_section_id as user_che4_7_0_,
elements0_.id as id1_7_0_,
elements0_.id as id1_7_1_,
elements0_.element_title as element_2_7_1_,
elements0_.is_checked as is_check3_7_1_,
elements0_.user_check_list_section_id as user_che4_7_1_
from
user_check_list_element elements0_
where
elements0_.user_check_list_section_id=?
関連関係のfetchTypeはLazyであるため、必要に応じてn回のクエリがさらに失敗するかどうかをチェックすることができます.これはまだオンラインになっていないサービスですが、問題になる部分には十分です.
QueryDslでこの部分を優雅に処理しましょう.
QueryDsl
fun findByEmailAndSubjectId(email: String, subjectId: Long): UserCheckList? {
val userChecklist = QUserCheckList.userCheckList
val section = QUserCheckListSection.userCheckListSection
val userCheckLists = query.selectFrom(userChecklist)
.leftJoin(userChecklist.userCheckListSections, section)
.fetchJoin()
.leftJoin(section.elements)
.fetchJoin()
.where(
QUserCheckList.userCheckList.email.eq(email)
.and(QUserCheckList.userCheckList.subjectId.eq(subjectId))
)
.fetch()
// join을 하게되면 테이블이 합쳐지게 되는데 이 과정에서 중복 데이터가 발생한다.
// 따라서 직접 중복 제거를 한다.
.stream()
.distinct()
.collect(Collectors.toList())
return if(userCheckLists.isEmpty()) null else userCheckLists[0]
}
結果
Hibernate:
*/ select
usercheckl0_.id as id1_6_0_,
usercheckl1_.id as id1_8_1_,
elements2_.id as id1_7_2_,
usercheckl0_.email as email2_6_0_,
usercheckl0_.subject_id as subject_3_6_0_,
usercheckl1_.section_title as section_2_8_1_,
usercheckl1_.user_check_list_id as user_che3_8_1_,
usercheckl1_.user_check_list_id as user_che3_8_0__,
usercheckl1_.id as id1_8_0__,
elements2_.element_title as element_2_7_2_,
elements2_.is_checked as is_check3_7_2_,
elements2_.user_check_list_section_id as user_che4_7_2_,
elements2_.user_check_list_section_id as user_che4_7_1__,
elements2_.id as id1_7_1__
from
user_check_list usercheckl0_
left outer join
user_check_list_section usercheckl1_
on usercheckl0_.id=usercheckl1_.user_check_list_id
left outer join
user_check_list_element elements2_
on usercheckl1_.id=elements2_.user_check_list_section_id
where
usercheckl0_.email=?
and usercheckl0_.subject_id=?
クエリーは一度だけ外に出るのが見えます.また、これらのデータは一度にインポートするデータであり、インポートするカラムは個別に指定されていません.
ポスト
JPQLの代わりにQueryDslを使ってみましたが、既存のJPQLについては
"select * from data " +
"where id = blah"
上記のように記入するのは大変です.しかしQueryDslを導入することで,利便性と可読性をもたらすことができる.
QueryDslの処理は未熟ですが、勉強でもっとうまく使えたら、とても良いライブラリになるはずです.
付言
間違ったところを指摘してください
Reference
この問題について(Query Dslを使用して複雑な構造のデータクエリーを処理する), 我々は、より多くの情報をここで見つけました https://velog.io/@devdynam0507/QueryDsl로-N1-문제-해결하기テキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol