1.データと構造設計[JPA,QueryDSL]


オブジェクト関連UML



Writerオブジェクトは、複数のDiabetesDiaryオブジェクトをリスト形式で保持します.
ライタ→DiabetesDiaryのみが一方向参照を生成します.DiabetesDiaryでWriterを参照する必要はなさそうです.
DiabetesDiaryオブジェクトはリスト形式でDietオブジェクトを保持します.これもDietがDiabetesDiaryを引用する必要がないと判断したため,一方向参照を選択した.
Dietオブジェクトはリスト形式でFoodオブジェクトを保持します.ここでは双方向参照とします.「食」の場合は逆に、「レシピ」の血糖値を参照する必要があります.
作成者の対象から見ると、ListfoodListという自分が食べる食べ物のリストがあります.このリストには、Foodオブジェクトの「参照」が含まれています.(保存名ではないので設定していません.)
著者らは、food Listおよびラベル(またはディックショーニー)を用いて、異なる食品の血糖値を知ることができる.
しかし、このような方法の問題は、血糖ログごとに食べ物の量が多いため、食べ物リストが肥大化する可能性が高いことです.後で最適化する必要があるようです.

DB ERD



JPAエンティティを複数対1の双方向マッピングに変更


オブジェクト関連UMLのUMLを見ると、DiabetesDiaryエンティティとDiaetエンティティは1:Nの一方向関係になります.
一対のマルチ一方向の欠点は、外部キーを持たない「一」エンティティが「マルチ」の外部キーを管理しなければならないことである.
この場合、関連関係を処理するためにupdate sqlを追加で実行する必要があります.解決策は,多対一双方向マッピングを用いることである.
これにより、管理する外部キーがマルチエンドエンティティに存在するため、追加のコストは発生しません.
Writer→DiabetesDiaryとWriter→Foodでも一対多一方向です.したがって、マルチペアの双方向マッピングに変更されます.

UMLとERDの変更


作者->食べ物の関連関係を削除した.理由は以下の通り.
著者の食品集合から食品エンティティを削除しても、レシピの食品エンティティを検索すると、食品エンティティには著者の情報が保持されます.
原因は不明ですが、db設計と一致しないため、関連関係を削除します.
db設計に適合するには、作成者から食べ物エンティティを削除すると、db内の食事チュートリアルの作成者の外部キーはnullでなければなりません.


このように,著者が食べる食べ物を知りたければ,結合(内部結合)が多すぎるという問題が生じる.

2回目の修正UMLとERD(識別関係による修正)



この問題を解決するために「非識別関係」→「識別関係」に変更する.(ほとんどの設計では、非識別関係を推奨します.)
識別関係に変換すると,要求が変化すれば,構造を柔軟に変えることは困難である.サブテーブルはプライマリ・キーを継承し続けるからです.
上の図で識別関係を用いると、食物表の複合結合の構成は4つに達する.
ただし、上図のテーブルは親テーブルが存在しなければサブテーブルが存在しない関係です.
△著者がいなければ血糖日誌はできないし、血糖日誌がなければレシピはできないし、レシピがなければ少量の食べ物はできない.
そして、ビジネスロジックを準備する際には、食べ物、レシピ、ログに関するロジックから「著者」が誰なのかを知ることが重要です.
食事やレシピに関する論理では、「日記」や「作者」がどのようなものかも重要です.(非認識の場合は、複数の結合が必要です.)
従って、非識別関係の使用が推奨されることが多いが、血糖ログ設計では識別関係がより適切であると考えられているため、識別関係に変更した.

QueryDSLの適用後に発生する問題と解決策


まず、QueryDSLはjoin(innerJoin()、leftJoin()などを明確に表す作成しない場合は、クロスジョイントが発生します.
クロスジョイントは、すべての場合の数を表すため、パフォーマンスが悪い.そのため、joinを明確に書くことが望ましい.

解決策


したがって,以下のコードのようにinnerJoin()を明示的に記述して上記の問題を解決する.
    @Override
  public List<Food> findFoodsInDiet(Long writerId, Long dietId) {
      //    @Query(value = "SELECT food FROM Food as food WHERE food.diet.diary.writer.writerId = :writer_id AND food.diet.diary.diaryId = :diaryId AND food.diet.dietId = :diet_id")
      return jpaQueryFactory.selectFrom(QFood.food)
              .innerJoin(QFood.food.diet, QDiet.diet)
              .on(QDiet.diet.diary.writer.writerId.eq(writerId).and(QDiet.diet.dietId.eq(dietId)))
              .fetch();
  }
Hibernate: select food0_.diary_id as diary_id0_2_, food0_.writer_id as writer_i0_2_, food0_.diet_id as diet_id0_2_, food0_.food_id as food_id1_2_, food0_.diary_id as diary_id3_2_, food0_.writer_id as writer_i4_2_, food0_.diet_id as diet_id5_2_, food0_.food_name as food_nam2_2_ 
from food food0_ 
inner join  (diet diet1_ 
inner join diabetes_diary diabetesdi2_ 
on diet1_.diary_id=diabetesdi2_.diary_id and diet1_.writer_id=diabetesdi2_.writer_id)
on food0_.diary_id=diet1_.diary_id and food0_.writer_id=diet1_.writer_id and food0_.diet_id=diet1_.diet_id and (diabetesdi2_.writer_id=? and diet1_.diet_id=?)

評価


逆にinner joinは2回発生します.
非識別関係では,3回の内部結合が予想されるが,現在は識別関係で2回の内部結合が発生しているため,結合演算が減少している.