Joinを使用してDTO-Spring Data JPAにマッピング


新しいデータをロードし、既存のテーブルのデータと比較します.論理を変更する必要はありません.読み込んだデータを新しいテーブルに格納し、既存のテーブルと比較するため、2つのテーブルの列は完全に同じであり、2つのテーブルの間に関連関係がないため、結果値を取得するためにDTOを新規作成することにしました.

インプリメンテーションロジック


既存のLectureテーブルは、新規ロードデータを格納するためのNLectureテーブルを生成する.
2つのテーブルのデータは、20のコラムの年、学期(学期)、subject no(カリキュラム番号)、class div(クラス分け)コラムを比較することで、追加するか削除するか、修正するかをフィルタします.
これらの条件を比較すると、full outer joinである.
  • 既存テーブルデータがnull:新規コース
  • 新しいテーブルデータがnull:削除済みコース
  • の場合
  • 両方がnot null:情報を更新するコース
    私はあなたにこのような分類をあげます.
  • 完全に外部に関連しないテーブルを接続するには


    unionを使用してfull outer joinの結果を呼び出すことができます.
    select * from lecture
    left outer join nlecture
    on lecture.subject_no=nlecture.subject_no
    and lecture.class_div=nlecture.class_div
    where lecture.year=[년도] and lecture.term=[학기]
    union
    select * from lecture
    right outer join nlecture
    and lecture.year=nlecture.year
    and lecture.term=nlecture.term
    on lecture.subject_no=nlecture.subject_no
    and lecture.class_div=nlecture.class_div
    左側outer joinの結果と右側outer joinの結果をunionキーにマージします.

    Spring Data JPA - Projection

    @Query飛び出した結果をエンティティではなく他のオブジェクトにマッピングするには、Spring Data JPAの投影機能を使用する必要があります.名前によっては、投影(投影、シャドウ)でインポートできるアトリビュートは、エンティティの一部のみです.
    通常、エンティティのカラム全体が不要な場合に使用されます.

    Interface-based Projection


    返されるオブジェクトタイプをInterfaceと書き、インポートするフィールドgetterメソッドを宣言します.この場合、フィールド名が正しく一致する必要があります.
    public interface UserView {
        String getName();
        String getNickname();
    }
    public interface UserRepository extends JpaRepository<User, Long> {
        List<UserView> findUserByAge(Integer age);
    }
    これにより、実行時にUserViewタイプのプロキシインスタンスが作成され、UserViewで作成されたgetName()getNickname()メソッドが実際のUserオブジェクトに渡されます.

    Class-based Projection

    public class UserDTO {
        String name;
        String nickname;
        
        public UserDTO(String name, String nickname){
        	this.name = name;
            this.nickname = nickname;
        }
    }
    クラスを用いてDTOを記述する場合もある.この場合、代理オブジェクトとネスト投影は適用されません.
    このほか、ジュネーブを使用するDynamic Projection方式も存在する.

    DTOは複数のエンティティからなる


    私の場合、Projectionを使用してjoinクエリーを発行し、2つのエンティティ(Leach、Nlecture)として受信します.
    @Getter
    @Setter
    @NoArgsConstructor
    @AllArgsConstructor
    public class LectureJoinDTO {
    	private Lecture lecture;
    	private NLecture nLecture;
    }
    最初に、class形式を使用してDTOに構造エンティティとNLエンティティを作成します.
    しかし、このように作成およびクエリーを行うと、ConverterNotFoundExceptionエラーが発生しました.
    org.springframework.core.convert.ConverterNotFoundException: No converter found capable of converting from type [org.springframework.data.jpa.repository.query.AbstractJpaQuery$TupleConverter$TupleBackedMap] to type [com.uostime.server.dto.LectureJoinDTO]
    	at org.springframework.core.convert.support.GenericConversionService.handleConverterNotFound(GenericConversionService.java:322) ~[spring-core-5.3.16.jar:5.3.16]
    	at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:195) ~[spring-core-5.3.16.jar:5.3.16]
    	at
        ...
    このエラーは後で解決され、修正されます.
    だからインタフェースの形で書いてみました.
    public interface LectureJoinDTO {
    	Lecture getLecture();
    	NLecture getNLecture();
    }
    振り返ってみると、よく動いています.

    JPQLを使用したjoinクエリーの作成


    残念ながらJPQLはunionをサポートしていません.
    unionとright joinを使用するには、ネイティブクエリーが必要だそうです.
    したがって、各テーブルでleft joinを使用してフィルタリングされます.
    @Repository
    public interface LectureRepository extends JpaRepository<Lecture, Long>, JpaSpecificationExecutor<Lecture> {
    
    	@Query("select l as lecture, n as NLecture from Lecture l left outer join NLecture n " +
    			"on l.year = n.year and l.term = n.term and l.subjectNo = n.subjectNo and l.classDiv = n.classDiv " +
    			"where l.year = :year and l.term = :term and l.used = true")
    	List<LectureJoinDTO> lectureLeftJoin(String year, String term);
    }
  • Lecture-テーブルNLectureTableおよびleftouterjoinに基づいてDTOリストとして受信される.
  • @Repository
    public interface NLectureRepository extends JpaRepository<NLecture, Long> {
    
    	@Query("select l as lecture, n as NLecture from NLecture n left outer join Lecture l " +
    			"on l.year = n.year and l.term = n.term and l.subjectNo = n.subjectNo and l.classDiv = n.classDiv " +
    			"and l.used = true")
    	List<LectureJoinDTO> lectureRightJoin();
        
    }
  • NLectureテーブルに基づいてLectureTableおよびleftouterjoinを実行し、DTOリストとして受信する.

  • 1の結果値では、NLectureobjectnullチェックにより、削除、更新するデータを分類処理する

  • 2の結果値では、Lectureobjectnullチェックにより、新しく追加されたデータが見つかり、dbに格納されます.
  • 私が書いた論理が有効かどうか分かりません.
    まず自分の意図通りに動作する
    またnative queryを用いてfull outernal join方式で実現したい.

    参考文献


    Spring Data JPA Projections
    Why, When and How to Use DTO Projections with JPA and Hibernate
    Spring Data JPA - Join unrelated entities by defining projections Join Unrelated Entities and Map the Result to POJO with Spring Data JPA and Hibernate