web-dev-qa-db-ja.com

HibernateCriteriaを使用してフィールドを遅延フェッチする方法

テーブルPersonnamefirstname、および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 [〜#〜]基準は??

15
Peter Rader

レイジーモードはアソシエーションでのみ意味があると思います。プレーンテーブルにアクセスしている場合は、すべてのフィールドが読み込まれます。

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);
11
Paco Abato

条件に「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属性の遅延読み込みを実装しているわけではありません。これを行う唯一の方法は@Basicannotationです。そうでない場合は、独自の遅延読み込みを実装する必要があります。

9
Mathias Begert

次の属性のみを含む同じSimplePersonデータベーステーブルにマップされた新しいエンティティpersonsを定義するだけです。

  • id
  • 名前
  • ファーストネーム

このように、CriteriaとHQLの両方でSimplePersonを選択すると、年齢列は取得されません。

もう1つの方法は、 基本属性の遅延読み込み を使用することですが、複数のサブエンティティを同じデータベーステーブルにマッピングする方がはるかに柔軟です。

4
Vlad Mihalcea

あなたの推論は有効です(一般的に、しかし、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の遅延読み込みです。

4

年齢が@DraganのPersonAgeのようなオブジェクトである場合は、エンティティではなく、フェクトモードを基準に関連付けることができます。

したがって、3つの選択肢があると思います。

  1. @Pacoが言うように原始的で投影としての年齢(Person.ageはnullであり、プロキシではありません、あなたはあなたが望む怠惰さを失います)
  2. 射影のないプリミティブとしての年齢(ワイヤ内のバイト数が多い)
  3. personAgeとしての年齢+ criteria.setFetchMode(追加のオブジェクト/テーブル/マッピングを犠牲にして、必要な怠惰を取得します)

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. 
    }
  }
4
gabrielgiussi