null値を使用したJPA / Hibernateクエリの最適化
-
03-07-2019 - |
質問
私はHibernateのJPAの実装を使用していますが、フェッチされる各エンティティに対して複数のSQLクエリが発行されるため、パフォーマンスが低下しています。結合されたJPAクエリを使用すると、SQLクエリが1つだけ生成されますが、行がnull関係を見つけられません。
例、この単純なスキーマを検討してください。人は住所に住んでおり、会社に雇用されています。住所と雇用者の両方はオプションであるため、nullにすることができます。
@Entity
public class Person {
public name;
@ManyToOne
@Column(nullable=true)
public Address address
@ManyToOne
@Column(nullable=true)
public Company employer
}
@Entity
public class Address {
address attributes ...
}
@Entity
public class Company {
company attributes ...
}
上記には示されていませんが、各JPAエンティティには何らかのID(キー)があります:
@Id
public Integer id;
私が見ている問題は、Personに対する単一のJPAクエリがデータベース上に複数のSQLクエリをもたらすということです。たとえば、次のJPAクエリ:
select p from Person p where ...
SQLクエリの結果:
select ... from Person where ...
および取得した各人に対する次のSQLクエリのペア:
select ... from Address a where a.id=xxx
select ... from Company c where c.id=yyy
これはパフォーマンスに大きな影響を与えます。クエリ結果セットが1000人の場合、1 + 1000 + 1000 = 2001 SQLクエリが生成されます。
それで、強制的にJPAクエリを最適化してみました:
select p from Person p join p.address a join p.employer e where ...
または:
select p, a, e from Person p join p.address a join p.employer e where ...
これにより、1つのSQLクエリに多数の結合が含まれます。問題は、住所または雇用主がnullの場合、結合クエリがそれを見つけられないことです。
だから、遅い結合レスクエリを使用するか、行を取得しない高速結合クエリを使用すると、リレーションシップがnullになります。ここに何かが欠けているに違いない。確かに高速で完全なクエリを実行する方法があります。
解決
私の推測では、左結合が必要だと思います。つまり、
SELECT p FROM Person p LEFT JOIN p.address a LEFT JOIN p.employer e WHERE...
実際にJPAでこれを試したわけではないことに注意してください。しかし、多くの点でJPA標準の基礎であるHQLでうまく動作します。
プレーン結合で機能しない理由は、デフォルトが内部結合であるためです。
他のヒント
AddressおよびCompanyエンティティにバッチサイズ(@BatchSize)を設定してみてください。結合でそれらをロードすることはありません(それはあなたが後のものですか?)が、ロードされるたびにそれらの束をロードします。バッチサイズは、必要なことが判明したときにロードする必要のある数を示します。
バッチサイズが1(デフォルト)で、10人をロードする場合。次に、住所と会社のアイテムを読み取って繰り返し、hibernateは10人のユーザーに対して1つのクエリを実行し、それらのユーザーの1人に対して住所または会社が必要になるたびに、その人のアドレスに対してクエリを実行します。
アドレスエンティティでバッチサイズ7を設定した場合、最初のアドレスを読み取ると、現在プロキシされているアドレスが7つ以上あることがわかり、7つのアドレスが取得されます。
BatchSizeが7のAddressとCompanyの両方があり、10人を反復処理している場合、現時点で21人が取得するのではなく、5つのクエリが発生します。それでも、結合によって得られる1ではありません。ただし、Personオブジェクトだけが必要で、それらに埋め込まれたAddress / Companyエンティティに触れない場合は、結合が遅くなります(たとえば、Person IDのリストを取得するか、男性/女性)
ご覧ください: http://hibernate.org/hib_docs/v3/reference/en/ html / performance.html