76

他のテーブルへの結合があるHibernateCriteriaクエリで行ベースの制限(例:setFirstResult(5)および)を使用してページングを実装しようとしています。setMaxResults(10)

当然のことながら、データはランダムに切断されています。その理由はここで説明されています。

解決策として、このページでは、結合の代わりに「2番目のSQL選択」を使用することを提案しています。

createAlias()既存の条件クエリ(を使用して結合している)を変換して、代わりにネストされた選択を使用するにはどうすればよいですか?

4

10 に答える 10

107

個別のハイドレート オブジェクトのリストではなく、個別の ID のリストを要求することで、目的の結果を得ることができます。

これを基準に追加するだけです:

criteria.setProjection(Projections.distinct(Projections.property("id")));

これで、行ベースの制限に従って正しい数の結果が得られます。これが機能する理由は、SQL クエリの実行に結果を区別するために結果をフィルタリングするという ResultTransformer の代わりに、プロジェクションが SQL クエリの一部として区別チェックを実行するためです。

注目に値するのは、オブジェクトのリストを取得する代わりに、後で休止状態からオブジェクトをハイドレートするために使用できる ID のリストを取得することです。

于 2008-11-19T00:53:36.903 に答える
43

私は自分のコードでこれを使用しています。

これを基準に追加するだけです:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

そのコードは、ネイティブ SQL のテーブルからの個別の選択 * のようになります。これが役立つことを願っています。

于 2009-12-03T06:56:52.433 に答える
29

FishBoy の提案に基づくわずかな改善。

この種のクエリは、2 つの段階ではなく、1 回のヒットで実行できます。つまり、以下の単一のクエリは、個別の結果を正しくページングし、ID だけでなくエンティティも返します。

サブクエリとして id プロジェクションを持つ DetachedCriteria を使用し、メインの Criteria オブジェクトにページング値を追加するだけです。

次のようになります。

DetachedCriteria idsOnlyCriteria = DetachedCriteria.forClass(MyClass.class);
//add other joins and query params here
idsOnlyCriteria.setProjection(Projections.distinct(Projections.id()));

Criteria criteria = getSession().createCriteria(myClass);
criteria.add(Subqueries.propertyIn("id", idsOnlyCriteria));
criteria.setFirstResult(0).setMaxResults(50);
return criteria.list();
于 2011-10-27T04:43:57.447 に答える
5

ソリューション:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

非常にうまく機能します。

于 2010-04-16T11:07:31.047 に答える
4
session = (Session) getEntityManager().getDelegate();
Criteria criteria = session.createCriteria(ComputedProdDaily.class);
ProjectionList projList = Projections.projectionList();
projList.add(Projections.property("user.id"), "userid");
projList.add(Projections.property("loanState"), "state");
criteria.setProjection(Projections.distinct(projList));
criteria.add(Restrictions.isNotNull("this.loanState"));
criteria.setResultTransformer(Transformers.aliasToBean(UserStateTransformer.class));

これは私を助けました:D

于 2013-01-24T13:12:50.603 に答える
3

ORDER BY を使用する場合は、次を追加します。

criteria.setProjection(
    Projections.distinct(
        Projections.projectionList()
        .add(Projections.id())
        .add(Projections.property("the property that you want to ordered by"))
    )
);
于 2014-07-14T16:01:59.207 に答える
1

ここで、アイテムの重複や抑制の問題を発生させることなく、通常のクエリとページ付けの方法を使用できる別の解決策について説明します。

このソリューションには、次のような進歩があります。

  • この記事で言及されているPKIDソリューションよりも高速です
  • 順序を保持し、PKのおそらく大きなデータセットで「in句」を使用しないでください

完全な記事は私のブログで見つけることができます

Hibernateを使用すると、設計時だけでなく、実行時にクエリ実行によってアソシエーションフェッチメソッドを定義できます。したがって、このアプローチを単純なリフレクションと組み合わせて使用​​し、コレクションプロパティに対してのみクエリプロパティフェッチアルゴリズムを変更するプロセスを自動化することもできます。

まず、エンティティクラスのすべてのコレクションプロパティを解決するメソッドを作成します。

public static List<String> resolveCollectionProperties(Class<?> type) {
  List<String> ret = new ArrayList<String>();
  try {
   BeanInfo beanInfo = Introspector.getBeanInfo(type);
   for (PropertyDescriptor pd : beanInfo.getPropertyDescriptors()) {
     if (Collection.class.isAssignableFrom(pd.getPropertyType()))
     ret.add(pd.getName());
   }
  } catch (IntrospectionException e) {
    e.printStackTrace();
  }
  return ret;
}

その後、この小さなヘルパーメソッドを使用して、条件オブジェクトにそのクエリでFetchModeをSELECTに変更するようにアドバイスします。

Criteria criteria = …

//    … add your expression here  …

// set fetchmode for every Collection Property to SELECT
for (String property : ReflectUtil.resolveCollectionProperties(YourEntity.class)) {
  criteria.setFetchMode(property, org.hibernate.FetchMode.SELECT);
}
criteria.setFirstResult(firstResult);
criteria.setMaxResults(maxResults);
criteria.list();

これを行うことは、設計時にエンティティのFetchModeを定義することとは異なります。したがって、UIのページングアルゴリズムで通常の結合関連付けフェッチを使用できます。これは、ほとんどの場合、重要な部分ではなく、結果をできるだけ早く取得することがより重要であるためです。

于 2012-07-06T14:44:45.617 に答える
0

以下は、複数の投影を実行して Distinct を実行する方法です。

    package org.hibernate.criterion;

import org.hibernate.Criteria;
import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.type.Type;

/**
* A count for style :  count (distinct (a || b || c))
*/
public class MultipleCountProjection extends AggregateProjection {

   private boolean distinct;

   protected MultipleCountProjection(String prop) {
      super("count", prop);
   }

   public String toString() {
      if(distinct) {
         return "distinct " + super.toString();
      } else {
         return super.toString();
      }
   }

   public Type[] getTypes(Criteria criteria, CriteriaQuery criteriaQuery) 
   throws HibernateException {
      return new Type[] { Hibernate.INTEGER };
   }

   public String toSqlString(Criteria criteria, int position, CriteriaQuery criteriaQuery) 
   throws HibernateException {
      StringBuffer buf = new StringBuffer();
      buf.append("count(");
      if (distinct) buf.append("distinct ");
        String[] properties = propertyName.split(";");
        for (int i = 0; i < properties.length; i++) {
           buf.append( criteriaQuery.getColumn(criteria, properties[i]) );
             if(i != properties.length - 1) 
                buf.append(" || ");
        }
        buf.append(") as y");
        buf.append(position);
        buf.append('_');
        return buf.toString();
   }

   public MultipleCountProjection setDistinct() {
      distinct = true;
      return this;
   }

}

ExtraProjections.java

package org.hibernate.criterion; 

public final class ExtraProjections
{ 
    public static MultipleCountProjection countMultipleDistinct(String propertyNames) {
        return new MultipleCountProjection(propertyNames).setDistinct();
    }
}

サンプル使用法:

String propertyNames = "titleName;titleDescr;titleVersion"

criteria countCriteria = ....

countCriteria.setProjection(ExtraProjections.countMultipleDistinct(propertyNames);

https://forum.hibernate.org/viewtopic.php?t=964506から参照

于 2013-01-08T07:21:11.380 に答える
-1

NullPointerExceptionある場合には!criteria.setProjection(Projections.distinct(Projections.property("id"))) すべてのクエリがなくてもうまくいきます!この解決策は悪いです!

もう 1 つの方法は、SQLQuery を使用することです。私の場合、次のコードは正常に動作します。

List result = getSession().createSQLQuery(
"SELECT distinct u.id as usrId, b.currentBillingAccountType as oldUser_type,"
+ " r.accountTypeWhenRegister as newUser_type, count(r.accountTypeWhenRegister) as numOfRegUsers"
+ " FROM recommendations r, users u, billing_accounts b WHERE "
+ " r.user_fk = u.id and"
+ " b.user_fk = u.id and"
+ " r.activated = true and"
+ " r.audit_CD > :monthAgo and"
+ " r.bonusExceeded is null and"
+ " group by u.id, r.accountTypeWhenRegister")
.addScalar("usrId", Hibernate.LONG)
.addScalar("oldUser_type", Hibernate.INTEGER)
.addScalar("newUser_type", Hibernate.INTEGER)
.addScalar("numOfRegUsers", Hibernate.BIG_INTEGER)
.setParameter("monthAgo", monthAgo)
.setMaxResults(20)
.list();

判別はデータベースで!反対に:

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

エンティティをロードした後、メモリ内で区別が行われます。

于 2010-05-19T09:46:14.810 に答える