エンティティやDTOを使用する場合

鍋の外側ギャングスター:いいえ社会的関心ありません

デイリープッシュ記事の外国優れた技術翻訳、インスピレーション国内のヘルプ開発者より良い成長!

JPAHibernateにあなたを可能にJPQLし、Criteriaクエリで使用DTOしてEntityマップとして。私は私のオンライントレーニングやセミナーで述べたようにHibernate、パフォーマンス中に、私は多くの場合、適切なマッピングが重要である使用するかどうかを選択し、尋ねましたか?答えは:はい!正しいマッピングをあなたのユースケースのパフォーマンスに大きな影響を選択します。私はあなたが必要とするデータのみを選択します。もちろん、不要な情報の選択は、あなたのための任意のパフォーマンス上の利点をもたらすことはありません。

1.DTOとエンティティ間の主な違い

Entityそして、DTO差がしばしば見過ごされている間- Entity管理下の永続コンテキスト(永続コンテキスト)があります。更新したい場合はEntity、時間を、ちょうど呼び出しsetter、新しい値を設定する方法を。Hibernateあなたは、SQL文を処理するために必要な、データベースに変更を書き込みます。

空きランチはありません。Hibernate変更がデータベースに保存する必要があるかどうかを判断するために、すべての管理対象エンティティ(管理対象エンティティ)のためのダーティチェック(汚れチェック)を実行する必要があります。これは非常に時間がかかる、あなただけの全く不要であるクライアントに情報の少量を送りたいです。

また、覚えておく必要がある、Hibernateと他のJPAすべての管理対象エンティティがキャッシュに格納されている実装します。これは良いことのようです。Hibernateの書き込みを最適化する必要がある、繰り返しクエリの実行を防ぐことができます。しかし、時間は、エンティティのクエリ数百または数千もの問題が発生する可能性があれば、キャッシュを管理するために必要な。

使用すると、Entityオーバーヘッド生成されます、そして、あなたは使用することができDTO、このオーバーヘッドを回避します。しかし、それはあなたが使用するべきではないどういう意味しますかEntity明らかではありません。

2.書き込み操作突起

プロジェクションエンティティ(エンティティ予測は)すべての書き込み操作に適用されます。Hibernateだけでなく、他のJPA状態のエンティティ管理を実現するため、およびデータベースの変更に保存され、必要なSQL文を作成します。これは、ほとんどは、作成、更新、および非常にシンプルかつ効果的な達成するための操作を削除することができます。

EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

Author a = em.find(Author.class, 1L);
a.setFirstName("Thorben");

em.getTransaction().commit();
em.close();

前記読み出し動作投影

しかし、読み取り専用(読み取り専用)操作ハンドルさまざまな方法を使用します。あなたは、データベースからデータを読みたい場合は、それがHibernate汚れや管理状況のチェックを行いません。そのため、理論的には、データを読み取るために、DTO投影がより良い選択です。しかし、実際に違うのですか?私はこの質問に答えるために、小さなパフォーマンステストをしました。

3.1テストのセットアップ

私はテストのために、以下のドメインモデルを使用します。これは、構成さAuthorBook1つのアソシエーションを使用して、エンティティ(多対1)。だから、すべての本は、一つの著者によって書かれています。

@Entity
public class Author {     @Id     @GeneratedValue(strategy = GenerationType.AUTO)     @Column(name = "id", updatable = false, nullable = false)     private Long id;     @Version     private int version;     private String firstName;     private String lastName;     @OneToMany(mappedBy = "author")     private List bookList = new ArrayList();     ... } 

それは確実にするためにHibernate、追加のデータを取得していない、私はセットあなたは読むことができ JPA FetchTypesの紹介を異なる取得の詳しい情報とその効果を。@ManyToOneFetchTypeLAZHFetchType

@Entity
public class Book {     @Id     @GeneratedValue(strategy = GenerationType.AUTO)     @Column(name = "id", updatable = false, nullable = false)     private Long id;     @Version     private int version;     private String title;     @ManyToOne(fetch = FetchType.LAZY)     @JoinColumn(name = "fk_author")     private Author author;     ... } 

私は、テストデータベースの10、それらの各10冊の本を書かれているで作成、データベースは100冊の合計が含まれています。各テストでは、私は、クエリを照会し、実行するために異なる投影100本を使用し、しっかりするのに必要な時間を測定します。副作用の影響を低減するために、私はそれを1000倍を行うと、平均時間を測定します。OK、始めましょう。

3.2。クエリエンティティ

ほとんどのアプリケーションでは、エンティティの投影(エンティティプロジェクション)が最も人気があります。ではEntityJPAあなたは簡単に投影としてそれらを使用することができます。この小さなテストでは、100を検索を実行し、測定するBookエンティティのために必要な時間を。

long timeTx = 0;
long timeQuery = 0;
long iterations = 1000; // Perform 1000 iterations for (int i = 0; i < iterations; i++) {     EntityManager em = emf.createEntityManager();     long startTx = System.currentTimeMillis();     em.getTransaction().begin();     // Execute Query     long startQuery = System.currentTimeMillis();     List<Book> books = em.createQuery("SELECT b FROM Book b").getResultList();     long endQuery = System.currentTimeMillis();     timeQuery += endQuery - startQuery;     em.getTransaction().commit();     long endTx = System.currentTimeMillis();     em.close();     timeTx += endTx - startTx; } System.out.println("Transaction: total " + timeTx + " per iteration " + timeTx / (double)iterations); System.out.println("Query: total " + timeQuery + " per iteration " + timeQuery / (double)iterations); 

平均して、検索結果をクエリを実行し、100にマップBookするエンティティ2msのに必要です。あなたは、トランザクション処理が含まれる場合は、2.89msました。小さく、あまり新しいラップトップのためにも良いです。

Transaction: total 2890 per iteration 2.89
Query: total 2000 per iteration 2.0

影響力の3.3のデフォルトは一対一関連するFetchType

私はあなたにブックエンティティを表示するとき、私は私が考えていることを指摘しFetchTypeセットLAZY、さらに調査を避けるために。デフォルトでは、To-one関連するFetchtType市はEAGER、それは伝えHibernate、すぐに初期の関連付けを。

あなたのクエリが複数のエンティティを選択した場合、これは、追加の問い合わせを必要とし、それがパフォーマンスに大きな影響を持つことになります。のは、変更してみましょうBookデフォルトを使用することを企業にFetchTypeして同じテストを実行します。

@Entity
public class Book {     @ManyToOne     @JoinColumn(name = "fk_author")     private Author author;     ... } 

この小さな変更は、テストケースの実行時間が3倍以上にしています。それは7.797msが現在のクエリを実行し、その結果ではなく、2ミリ秒をマッピングしました。トランザクションあたりの時間は8.681ミリ秒の代わりに、2.89ミリ秒に増加しました。

Transaction: total 8681 per iteration 8.681
Query: total 7797 per iteration 7.797

したがって、それは確実にするために最善であるTo-one関連付けが設定されているFetchTypeLAZY

3.4。@Immutableエンティティを選択

ジョアンCharnetはあなたがテストで不変の実体(エンティティ不変)を追加したいコメントで教えてください。興味深い質問がある:戻り値の@Immutableエンティティのアノテーション、クエリのパフォーマンスが良くなりますか?

Hibernate彼らは不変であるので汚れは、これらのエンティティのいずれかのチェックを実行する必要はありません。これは、より良いパフォーマンスにつながる可能性があります。だから、私たちは試してみましょう。

私は、テストに以下の追加ImmutableBookのエンティティ。

@Entity
@Table(name = "book")
@Immutable
public class ImmutableBook {     @Id     @GeneratedValue(strategy = GenerationType.AUTO)     @Column(name = "id", updatable = false, nullable = false)     private Long id;     @Version     private int version;     private String title;     @ManyToOne(fetch = FetchType.LAZY)     @JoinColumn(name = "fk_author")     private Author author;     ... } 

それはBook二つの追加のコメントで、エンティティのコピー。@Immutableノートは言うHibernateこのエンティティはなってきていません、。そして、@Table(name =“book”)エンティティはにマッピングされているbookテーブル。したがって、我々は以前に同じテストを実行したのと同じデータを使用することができます。

long timeTx = 0;
long timeQuery = 0;
long iterations = 1000; // Perform 1000 iterations for (int i = 0; i < iterations; i++) {     EntityManager em = emf.createEntityManager();     long startTx = System.currentTimeMillis();     em.getTransaction().begin();     // Execute Query     long startQuery = System.currentTimeMillis();     List<Book> books = em.createQuery("SELECT b FROM ImmutableBook b")             .getResultList();     long endQuery = System.currentTimeMillis();     timeQuery += endQuery - startQuery;     em.getTransaction().commit();     long endTx = System.currentTimeMillis();     em.close();     timeTx += endTx - startTx; } System.out.println("Transaction: total " + timeTx + " per iteration " + timeTx / (double)iterations); System.out.println("Query: total " + timeQuery + " per iteration " + timeQuery / (double)iterations); 

エンティティが不変であれば興味深いことに、クエリに違いはありません。トランザクションおよび照会の平均実行時間は、以前の試験とほぼ同じ測定します。

Transaction: total 2879 per iteration 2.879
Query: total 2047 per iteration 2.047

QueryHints.HINT_READONLYを使用して3.5。エンティティのクエリ

アンドリュー・ブルジョワは、読み取り専用のテストに含ま勧告を照会します。だから、これを見て。

このテストでは、記事の冒頭でお見せするために私を使用Bookするエンティティ。しかし、それはテストケースを変更する必要があります。

JPAそして、Hibernateクエリヒント(ヒット)をサポートし、クエリとその実装のモダリティに関する追加情報を提供することができます。クエリヒントをQueryHints.HINT_READONLY教えてくれHibernate読み取り専用モードクエリエンティティに。そのため、Hibernateそれらの上に任意の汚れチェックを実行する必要性は、また、他の最適化にも適用することができます。

次の方法でできるQueryインターフェイスで呼び出しsetHint、このプロンプト方法を設定します。

long timeTx = 0;
long timeQuery = 0;
long iterations = 1000; // Perform 1000 iterations for (int i = 0; i < iterations; i++) {     EntityManager em = emf.createEntityManager();     long startTx = System.currentTimeMillis();     em.getTransaction().begin();     // Execute Query     long startQuery = System.currentTimeMillis();     Query query = em.createQuery("SELECT b FROM Book b");     query.setHint(QueryHints.HINT_READONLY, true);     query.getResultList();     long endQuery = System.currentTimeMillis();     timeQuery += endQuery - startQuery;     em.getTransaction().commit();     long endTx = System.currentTimeMillis();     em.close();     timeTx += endTx - startTx; } System.out.println("Transaction: total " + timeTx + " per iteration " + timeTx / (double)iterations); System.out.println("Query: total " + timeQuery + " per iteration " + timeQuery / (double)iterations); 

-あなたは、パフォーマンスの大幅な改善を行うために、クエリ読み取り専用に設定することをお勧めしますHibernate以下の作業を行ったので、速くする必要があります。

あなたは以下を参照することができますとしてではなく、実行時間は、前回のテストとほぼ同じです。少なくとも、このテストシナリオでは、QueryHints.HINT_READONLYセットには、trueパフォーマンスが改善されません。

Transaction: total 2842 per iteration 2.842
Query: total 2006 per iteration 2.006

3.6。クエリDTO

約100冊のエンティティが2msをロードします。のは、見てみましょうJPQLコンストラクタ式を使用すると、同じデータがパフォーマンスが向上するかどうかのクエリを取得します。

もちろん、あなたもでできCriteriaコンストラクタ発現クエリを使用します。

long timeTx = 0;
long timeQuery = 0;
long iterations = 1000; // Perform 1000 iterations for (int i = 0; i < iterations; i++) {     EntityManager em = emf.createEntityManager();     long startTx = System.currentTimeMillis();     em.getTransaction().begin();     // Execute the query     long startQuery = System.currentTimeMillis();     List<BookValue> books = em.createQuery("SELECT new org.thoughts.on.java.model.BookValue(b.id, b.title) FROM Book b").getResultList();     long endQuery = System.currentTimeMillis();     timeQuery += endQuery - startQuery;     em.getTransaction().commit();     long endTx = System.currentTimeMillis();     em.close();     timeTx += endTx - startTx; } System.out.println("Transaction: total " + timeTx + " per iteration " + timeTx / (double)iterations); System.out.println("Query: total " + timeQuery + " per iteration " + timeQuery / (double)iterations); 

予想されるように、DTO投影された比率は、实体(Entity)良好に機能すると予測しました。

Transaction: total 1678 per iteration 1.678
Query: total 1143 per iteration 1.143

平均すると、クエリが1.143msを必要とする実行し、執行部は1.678msを必要とします。43%のクエリのパフォーマンスの向上は、トランザクションの性能が約42%増加しました。

小さな変更を実現するために時間がかかるために、これは非常に良いされています。

ほとんどのプロジェクトでは、DTO投影パフォーマンスが高くなります。それはあなたではなく、エンティティマッピングのすべての属性よりも、所望のデータの使用例を選択することができます。ほとんど常に少ないデータを選択して、より良いパフォーマンスにつながります。

4.まとめ

あなたのユースケースは、あなたが思うよりも簡単に、より重要なのために右のプロジェクターを選択します。

投影として書き込み動作を実現するために、それが使用されるべき実体(エンティティ)。Hibernateその状態を管理します、あなただけのビジネスロジックでそのプロパティを更新する必要があります。その後Hibernate、我々は残りを処理します。

あなたは私の小さなパフォーマンステストの結果を見てきました。私のラップトップは、それは確かに本番環境よりも遅くなり、これらのテストを実行するための最良の環境ではないかもしれません。パフォーマンスの改善は非常に大きいですが、あなたがプロジェクターを使用すべきかは明らかです。

ファイル

 

使用してDTO選択クエリの実体よりも約40%高速化投影問い合わせを。したがって、それはあなたの読み取り専用操作のための最高の作成に余分な労力かかるDTO投影として、使用を。

また、すべての関連付けを使用してくださいFetchType.LAZYあなたも熱心に取得するために、テストで見ることができるようにto-one相関演算を、あなたはまだトリプル実行時間を照会することがあります。そのため、使用することをお勧めしFetchType.LAZY、ご希望のユースケースと初期化の関係を

オリジナルリンク:thoughts-on-java.org/entities-dt ...

著者:Thorbenヤンセン

翻訳:Yunooa

 

おすすめ

転載: www.cnblogs.com/liululee/p/10971314.html