Spring Data リポジトリ: リストとストリーム 質問する

Spring Data リポジトリ: リストとストリーム 質問する

Spring Data リポジトリでメソッドを定義する場合の推奨事項は何ですかlist?stream

クエリストリーミング

例:

interface UserRepository extends Repository<User, Long> {

  List<User> findAllByLastName(String lastName);

  Stream<User> streamAllByFirstName(String firstName);                    
         
  // Other methods defined.
}

ここで私が尋ねているのはページスライス- それらは私にとって明らかであり、私はそれらの説明をドキュメンテーション


私の推測(間違っているでしょうか?):

  1. ストリームはすべてのレコードを Java ヒープにロードしません。代わりに、kレコードをヒープにロードして 1 つずつ処理し、次に別のkレコードをロードする、という処理を繰り返します。

  2. リストはすべてのレコードを一度に Java ヒープにロードします。

  3. バックグラウンド バッチ ジョブ (たとえば、分析の計算) が必要な場合は、すべてのレコードを一度にヒープ内にロードしないため、ストリーム操作を使用できます。

  4. すべてのレコードを含む REST 応答を返す必要がある場合は、いずれにせよそれらを RAM にロードして JSON にシリアル化する必要があります。この場合、リストを一度にロードするのが理にかなっています。


応答を返す前にストリームをリストに収集する開発者もいるようです。

class UserController {

    public ResponseEntity<List<User>> getUsers() {
        return new ResponseEntity(
                repository.streamByFirstName()
                        // OK, for mapper - it is nice syntactic sugar. 
                        // Let's imagine there is not map for now...
                        // .map(someMapper)  
                       .collect(Collectors.toList()), 
                HttpStatus.OK);
    }
}

この場合、Stream の利点は見当たりませんが、使用してもlist同じ最終結果になります。

それでは、使用が正当化される例はありますかlist?

ベストアンサー1

要約

CollectionVSの主な違いはStream次の 2 つの点です。

  1. 最初の結果が出るまでの時間– クライアント コードは最初の要素をいつ確認しますか?
  2. 処理中のリソースの状態- ストリームが処理されている間、基盤となるインフラストラクチャ リソースはどのような状態ですか?

コレクションの操作

例を使って説明しましょう。リポジトリから 10 万個のインスタンスを読み取る必要があるとしますCustomer。結果を処理する方法 (処理する必要がある方法) によって、上記の両方の側面についてヒントが得られます。

List<Customer> result = repository.findAllBy();

クライアントコードはそのリストを一度受信します全て要素は基盤となるデータストアから完全に読み取られており、それ以前の瞬間ではありません。また、基盤となるデータベース接続もできる閉じられています。つまり、たとえば Spring Data JPA アプリケーションでは、周囲のメソッドに をアノテーションするか を使用するなどしてEntityManager、より広いスコープで積極的に保持しない限り、基礎となる が閉じられ、エンティティが切り離されていることがわかります。また、リソースを積極的に閉じる必要はありません。@TransactionalOpenEntityManagerInViewFilter

ストリームの操作

ストリームは次のように処理する必要があります。

@Transactional
void someMethod() {

  try (Stream result = repository.streamAllBy()) {
    // … processing goes here
  }
}

を使用するとStream、最初の要素 (データベースの行など) が到着してマップされるとすぐに処理を開始できます。つまり、結果セットの他の要素がまだ処理されている間に、要素を消費することができます。また、基礎となるリソースは、リポジトリ メソッドの呼び出しにバインドされるため、アクティブに開いたままにしておく必要があります。 も、基礎となるStreamリソースをバインドするため、アクティブに閉じる必要があることに注意してください (try-with-resources)。そのため、何らかの方法で、閉じるようにシグナルを送信する必要があります。

JPA では、メソッドの戻り時に基礎が閉じられるため、 がないと@Transactional適切Streamに処理できませんEntityManager。いくつかの要素が処理されますが、処理の途中で例外が発生します。

下流での使用

理論的には、例えばJSON配列を効率的に構築するために を使うことはできますがStream、書き終わるまでコアリソースをオープンにしておく必要があるため、状況はかなり複雑になります。全て要素。これは通常、オブジェクトを JSON にマップするコードを記述し、それらを手動でレスポンスに書き込むことを意味します (たとえば、Jackson や などを使用ObjectMapper) HttpServletResponse

メモリフットプリント

ResultSetメモリフットプリントは改善される可能性が高いですが、これは主に、マッピングステップ( -> Customer-> - > JSONオブジェクト)でのコレクションの中間作成と追加コレクションを回避したいという事実に起因していますCustomerDTO。すでに処理された要素ない他の理由で保持される可能性があるため、メモリから確実に削除される。繰り返しになるが、例えばJPAではリソースのライフサイクルを制御するため、オープンにしておく必要がありEntityManager、すべての要素はそれにバインドされEntityManager、次の条件が満たされるまで保持される。全て要素が処理されます。

おすすめ記事