テーブルPerson
(name
、firstname
、およびage
を持つ)の各行が読み取られます。
EntityManager em = emf.createEntityManager();
Session s = (Session) em.getDelegate();
Criteria criteria = s.createCriteria(Person.class);
criteria.setFetchMode("age", FetchMode.SELECT);
しかし、SQLは
Hibernate:
select
person0_.name,
person0_.firstname,
person0_.age
from
SCOPE.PERSON person0_
年齢を怠惰にする方法[〜#〜] only [〜#〜]基準は??
レイジーモードはアソシエーションでのみ意味があると思います。プレーンテーブルにアクセスしている場合は、すべてのフィールドが読み込まれます。
age
フィールドをSQLに表示せず、メモリにロードしないようにする場合は、プロジェクションを使用します。
Criteria crit = session.createCriteria(Person.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.property("name"));
projList.add(Projections.property("firstname"));
crit.setProjection(projList);
条件に「age」プロパティのFetchModeを設定しても効果はありません。これは、この時点でのフェッチ戦略は関連するオブジェクトのみを対象としており、プロパティは対象としていないためです。 Hibernateドキュメントのセクション 20.1。フェッチ戦略 を参照してください。
Hibernateは、アプリケーションが関連付けをナビゲートする必要がある場合、フェッチ戦略を使用して関連付けられたオブジェクトを取得します。フェッチ戦略は、O/Rマッピングメタデータで宣言するか、特定のHQLまたはCriteriaクエリでオーバーライドできます。
プロパティを遅延ロードする唯一の方法は、@Basic
アノテーションをFetchType.LAZY
に設定することです。 here を参照してください。または、マッピングに.hbm.xmlファイルを使用する場合はlazy=true
を使用してください。休止状態のドキュメントの this セクションを参照してください。
@Basicアノテーションを使用すると、プロパティのフェッチ戦略を宣言できます。 LAZYに設定されている場合、インスタンス変数が最初にアクセスされたときにこのプロパティを遅延フェッチする必要があることを指定します。ビルド時のバイトコードインストルメンテーションが必要です。クラスがインストルメントされていない場合、プロパティレベルの遅延読み込みは黙って無視されます。
プロパティの遅延読み込みもbuildtime bytecode instumentationを使用します(hibernateはコンパイル後にエンティティクラスを変更して、プロパティの遅延読み込みを許可します)。読む 20.1.8。レイジープロパティフェッチの使用
問題に対する他の可能な解決策(他のすべての解決策を除く)は、より単純なPersonクラスを作成し、 コンストラクタークエリ のように使用することです。
public class PersonDTO {
private String name;
private String firstname;
private Person(String name, String firstname) {
this.name = name;
this.firstname = firstname;
}
// getters & setters
}
Query q = session.createQuery("select new your.package.name.PersonDTO("
+ "p.name, p.firstname) from Person p");
q.list();
既存のPersonクラスを使用して、適切なコンストラクターで拡張することもできますが、明示的にしたいと思います。
ただし、ここで紹介するすべてのソリューションは、age
属性の遅延読み込みを実装しているわけではありません。これを行う唯一の方法は@Basic
annotationです。そうでない場合は、独自の遅延読み込みを実装する必要があります。
次の属性のみを含む同じSimplePerson
データベーステーブルにマップされた新しいエンティティpersons
を定義するだけです。
このように、CriteriaとHQLの両方でSimplePerson
を選択すると、年齢列は取得されません。
もう1つの方法は、 基本属性の遅延読み込み を使用することですが、複数のサブエンティティを同じデータベーステーブルにマッピングする方がはるかに柔軟です。
あなたの推論は有効です(一般的に、しかし、age
フィールドの特定の例について議論することができます)が、残念ながら、これに対する簡単な解決策はありません。実際、Hibernateには フェッチプロファイル の概念がありますが、現在は非常に制限されています(デフォルトのフェッチプラン/ストラテジーは、結合スタイルのフェッチプロファイルでのみオーバーライドできます)。
したがって、問題に対する可能な回避策は次のようになります。
1)age
を別のエンティティに移動し、Person
エンティティを怠惰な1対1の関係で関連付けます。
@Entity
class PersonAge {
private Integer age;
}
@Entity
class Person {
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, optional = false)
@JoinColumn(name = "PERSON_AGE_ID")
private PersonAge personAge;
public Integer getAge() {
return personAge.getAge();
}
public void setAge(Integer age) {
personAge.setAge(age);
}
}
2)デフォルトのプロファイルを上書きするフェッチプロファイルを定義します。
@FetchProfile(name = "person-with-age", fetchOverrides = {
@FetchProfile.FetchOverride(entity = Person.class, association = "personAge", mode = FetchMode.JOIN)
})
3)アプリケーションのeachセッションでこのプロファイルを有効にします。
session.enableFetchProfile("person-with-age");
使用するフレームワークに応じて、クレートされた各セッション(トランザクション)のプロファイルを有効にするために使用する簡単なフック/インターセプターが必要です。たとえば、Springでのアプローチは、使用中のトランザクションマネージャーの AbstractPlatformTransactionManager.doBegin をオーバーライドすることです。
このようにして、フェッチプロファイルが明示的に無効にされていない限り、personAge
はアプリケーションのすべてのセッションに熱心にロードされます。
4)Disable目的のCriteriaクエリを使用するセッションのフェッチプロファイル:
session.disableFetchProfile("person-with-age");
このようにして、デフォルトのフェッチプラン/戦略が使用されます(エンティティマッピングで指定されます)。これは、PersonAge
の遅延読み込みです。
年齢が@DraganのPersonAgeのようなオブジェクトである場合は、エンティティではなく、フェクトモードを基準に関連付けることができます。
したがって、3つの選択肢があると思います。
Projectionの場合、ResultTransformerを使用して
Criteria crit = session.createCriteria(Person.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.property("name"));
projList.add(Projections.property("firstname"));
crit.setProjection(projList);
crit.setResultTransformer(new ResultTransformer() {
@Override
public Object transformTuple(Object[] Tuple, String[] aliases) {
String name = (Long) Tuple[0];
String firstName = (String) Tuple[1];
return new Person(name , firstName);
}
@Override
public List<Reference> transformList(List collection) {
return collection;
}
});
年齢を取得するためのクエリをトリガーするPersonProxyを自分で作成できると思いますが、これはちょっとひどいです。
@Override
public Object transformTuple(Object[] Tuple, String[] aliases) {
String name = (Long) Tuple[0];
String firstName = (String) Tuple[1];
return new PersonProxy(name , firstName);
}
class PersonProxy {
Person realPerson;
public getAge(){
// create a query with realPerson.id for retrieve the age.
}
}