web-dev-qa-db-ja.com

多くのフィールドと結合を使用したクエリ

多くのフィールドとリレーションを持つテーブルを持つレガシーデータベースをマップする必要があります。たとえば(簡略化されたコード)、

@Entity
@Table(name = "VISIT")
public class VisitEntity {
   @Id
   private Integer Id;

   @ManyToOne
   @JoinColumn(name = "CD_COMPANY")
   private CompanyEntity company;

   @ManyToOne
   @JoinColumn(name = "CD_EMPLOYEE")
   private EmployeeEntity employee;

   @ManyToOne
   @JoinColumn(name = "CD_VISIT_TYPE")
   private VisitTypeEntity visitType;

   // A lot more fields and relations
}

VisitEntityで使用される他のすべてのエンティティにも、多くのフィールドとより多くの関係があります。たとえば、EmployeeEntityは次のようにマップされます。

@Entity
@Table(name = "EMPLOYEE")
public class EmployeeEntity {
   @Id
   private Integer Id;

   @Column(name = "EMP_NAME")
   private String empName;

   @ManyToOne
   @JoinColumn(name = "CD_DEPARTMENT")
   private DepartmentEntity department;

   @ManyToOne
   @JoinColumn(name = "CD_CITY")
   private CityEntity city;

   @ManyToOne
   @JoinColumn(name = "CD_CATEGORY")
   private CategoryEntity category;

   // A lot more fields and relations
}

このクラスでは、VisitEntityの単純なfind()により、400を超える列と40を超える結合を含むクエリが生成されます。将来的には、これがパフォーマンスの問題となるのではないかと心配です。この数を減らす方法は2つあります(VisitEntityを使用する場合、EmployeeEntityのフィールドIDと名前だけが必要であると仮定します)。

  1. EntityManagerのfindメソッドの使用を意図して、関係テーブルのフィールドのサブセットのみを参照するTypedQueryを使用します。例えば:
SELECT v, e.id, e.empName, <more subsets from other tables> FROM VisitEntity v LEFT JOIN EmplooyEEEntity e WHERE v.id = :id

フィールドのリストが少ないクエリを使用しても、JPAはすべての主要なエンティティを作成する必要があると思います。

  1. 補助エンティティを使用します。たとえば、EmployeeEntityのunsingの代わりに、IdフィールドとNameフィールドのみを持つVisitEmployeeEntityを作成します。このように、find()メソッドは、EMPLOYEEテーブルの2つのフィールドのみをクエリします。

2つ目のアプローチは、たとえ少人数のクラスを作成する必要があることを示しているとしても、感謝しています。

私はORMの世界にも慣れていないので、この件に関してあなたの経験や意見を教えてください。

2
javlacerda

新しいアプリケーションを作成してORMの使用を決定する場合でも、特にORMの動作に慣れていない場合は、非常に扱いにくい場合があります。 ORMを使用すると、データベースを扱いやすくなるように見えますが、これは単純なスキーマの場合にのみ当てはまります。状況がさらに複雑になると、データベースだけでなくORMレイヤーも理解する必要があります。

あなたのコード例では、あなたはそれを知らないかもしれませんが、@ManyToOne関係にはデフォルトの FetchType of EAGERがあります。つまり、単一のVisitEntityをロードすると、ORMレイヤーはさらにいくつかのクエリを実行して、それらの熱心な関係と結果のエンティティグラフを取得します巨大になります。

フェッチタイプの問題は、遅延関係を積極的にフェッチするためにJPQLクエリを作成できることですが、その逆はできません。つまり、最初にすべきことは、@ManyToOne関係を遅延として宣言することです。これは、デフォルトの熱心な関係によるエンティティグラフの爆発を少なくとも防ぎます。

コンストラクタクエリ など、使用できる他の多くのトリックもあり、エンティティをスキーマに直接マップしただけでも、必要なデータをより効率的にフェッチできます(コードの観点からは役に立たない)。

あなたはあなたの前にたくさんの仕事をしていて(特にORMが初めての場合)、エンティティ/グラフによっては、他のものとは異なるアプローチを使用する方がよい場合があります。いつものように、単純な解決策はありません。

フィールドのリストが少ないクエリを使用しても、JPAはすべての主要なエンティティを作成する必要があると思います。

エンティティは、select句にない限り(またはエンティティグラフに含まれるように積極的にフェッチされない限り)作成されません。 e.empNameを選択しても、EmployeeEntityが作成されるわけではありません。

2
Kayaman

ORMは、データベースからの情報の更新、挿入、選択、削除に関連する多くのことを抽象化するのに役立つツールです。

ほとんどのアプリケーションにはパフォーマンスの強い要件がないため、人気があると思います。ほとんどの場合、それらのほとんどは単純であり、ネットワークやデータベースの仕様は、更新/選択などを行うときにエンティティからのこの「余分な」情報に対応するのに十分余裕があるためです。

しかし、このORMは非常に非常に寛容です。彼らは、アプリケーションのパフォーマンスに非常に悪いいくつかのデフォルトの動作を想定できます。そう:

このクラスでは、VisitEntityの単純なfind()により、400を超える列と40を超える結合を含むクエリが生成されます

これは、@OneToOneおよび@ManyToOneは、JPA仕様ではデフォルトでEAGERです。 JPAの使用経験があるチームは、これをalwaysLAZYで脅かします。 Hibernateドキュメントが推奨するように:

Hibernateの推奨事項は静的にすべての関連付けを怠惰にマークするであり、熱意のために動的フェッチ戦略を使用することです。残念ながら、これはすべての1対1および多対1の関連付けをデフォルトで積極的にフェッチする必要があると定義しているJPA仕様と対立します。 JPAプロバイダーとしてのHibernateは、そのデフォルトを受け入れます。

この単純な変更により、将来の多くのパフォーマンスの問題を防ぐことができます。

2番目のアプローチは、選択したパフォーマンスを向上させるための戦略の1つです。これは、アプリケーションで非常に重要なクエリから最高のパフォーマンスを抽出する必要がある場合に特に便利です。そうでなければ、私がしたいさまざまな選択を表すために多くのクラスを作成するのは非常に苦痛だと思います。

0
Dherik