web-dev-qa-db-ja.com

Hibernateを使用したSpring Data JPAで、インターフェースプロジェクションがコンストラクタプロジェクションおよびエンティティプロジェクションよりもはるかに遅いのはなぜですか?

私はどの種類の投影法を使用するべきか疑問に思っていたので、5種類の投影法(ドキュメントに基づく: https://docs.spring.io/spring-data/jpa)をカバーする小さなテストを行いました/ docs/current/reference/html /#projections ):

1。エンティティ投影

これは、Spring Dataリポジトリによって提供される標準のfindAll()にすぎません。ここで空想は何もありません。

サービス:

_List<SampleEntity> projections = sampleRepository.findAll();
_

エンティティ:

_@Entity
@Table(name = "SAMPLE_ENTITIES")
public class SampleEntity {
    @Id
    private Long id;
    private String name;
    private String city;
    private Integer age;
}
_

2。コンストラクタープロジェクション

サービス:

_List<NameOnlyDTO> projections = sampleRepository.findAllNameOnlyConstructorProjection();
_

リポジトリ:

_@Query("select new path.to.dto.NameOnlyDTO(e.name) from SampleEntity e")
List<NameOnlyDTO> findAllNameOnlyConstructorProjection();
_

データ転送オブジェクト:

_@NoArgsConstructor
@AllArgsConstructor
public class NameOnlyDTO {
    private String name;
}
_

3。インターフェース投影

サービス:

_List<NameOnly> projections = sampleRepository.findAllNameOnlyBy();
_

リポジトリ:

_List<NameOnly> findAllNameOnlyBy();
_

インターフェース:

_public interface NameOnly {
    String getName();
}
_

4。タプル投影

サービス:

_List<Tuple> projections = sampleRepository.findAllNameOnlyTupleProjection();
_

リポジトリ:

_@Query("select e.name as name from SampleEntity e")
List<Tuple> findAllNameOnlyTupleProjection();
_

5。動的投影

サービス:

_List<DynamicProjectionDTO> projections = sampleRepository.findAllBy(DynamicProjectionDTO.class);
_

リポジトリ:

_<T> List<T> findAllBy(Class<T> type);
_

データ転送オブジェクト:

_public class DynamicProjectionDTO {

    private String name;

    public DynamicProjectionDTO(String name) {
        this.name = name;
    }
}
_


追加情報:

プロジェクトは、内部でSpring 5.0.8を使用するGradle Spring Bootプラグイン(バージョン2.0.4)を使用してビルドされました。データベース:メモリ内のH2。

結果:

_Entity projections took 161.61 ms on average out of 100 iterations.
Constructor projections took 24.84 ms on average out of 100 iterations.
Interface projections took 252.26 ms on average out of 100 iterations.
Tuple projections took 21.41 ms on average out of 100 iterations.
Dynamic projections took 23.62 ms on average out of 100 iterations.
-----------------------------------------------------------------------
One iteration retrieved (from DB) and projected 100 000 objects.
-----------------------------------------------------------------------
_

注:

エンティティの取得には時間がかかることは理解できます。 Hibernateはこれらのオブジェクトを追跡して、変更や遅延読み込みなどを行います。

コンストラクタープロジェクションは非常に高速で、DTO側に制限はありませんが、_@Query_アノテーションで手動でオブジェクトを作成する必要があります。

インターフェースの予測は本当に遅いことが判明しました。質問を参照してください。

タプルプロジェクションは最も高速でしたが、操作するのに最も便利ではありません。 JPQLにはエイリアスが必要であり、.get("name")ではなく.getName()を呼び出してデータを取得する必要があります。

動的プロジェクションは見た目がとてもクールで高速ですが、コンストラクタを1つだけ持つ必要があります。それ以上でもそれ以下でもありません。それ以外の場合、Spring Dataは、どちらを使用するかわからないため、例外をスローします(コンストラクターパラメーターを使用して、DBから取得するデータを決定します)。

質問:

エンティティの取得よりもインターフェースの予測に時間がかかるのはなぜですか?返される各インターフェースプロジェクションは、実際にはプロキシです。そのプロキシを作成するのはそれほど高価ですか?もしそうなら、それはプロジェクションの主な目的を打ち破っていませんか?他の投影法は素晴らしいです。私はこれについていくつかの洞察が本当に好きです。ありがとうございました。

EDIT:これがテストリポジトリです: https://github.com/aurora-software-ks/spring-boot-projections- test 自分で実行したい場合。セットアップはとても簡単です。 Readmeには、知っておく必要のあるものがすべて含まれています。

16
Sikor

以前のバージョンのSpring Dataで同様の動作を経験しましたが、これは私の考えでした: https://blog.arnoldgalovics.com/how-much-projections-can-help/

私はOliver Gierke(Spring Dataリード)と話し合い、彼はいくつかの改善を行いました(そのため、「良い」結果が得られます:-))ですが、基本的に、抽象化と手動でのコーディングには常にコストがかかります。

これは他のすべてと同じようにトレードオフです。一方で、柔軟性、開発の容易さ、メンテナンスの軽減(願わくば)が得られ、他方で、完全に制御された、やや醜いクエリモデルが得られます。

7
galovics