0

わかりました。次の(省略形の)3つのエンティティクラスとHibernateUtilクラスがあります。

public class Tag {
    @Id
    BigDecimal id;

    String tag

    @ManyToMany( mappedBy="tags" )
    List<Label> labels;
}

public class Label {
    @Id
    BigDecimal id;

    String label;

    @ManyToMany( targetEntity=Tag.class )
    List<Tag> tags;
}

public class Data {
    @Id
    BigDecimal id;

    BigDecimal data;

    @ManyToOne
    Label label;
}


public class HibernateUtil {

    public static List pagedQuery(DetachedCriteria detachedCriteria, Integer start, Integer size) throws WebApplicationException {
        Session session = getSession();
        try {
            Transaction transaction = session.beginTransaction();

            List records = detachedCriteria.getExecutableCriteria(session)
                    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                    .setFirstResult(start)
                    .setMaxResults(size)
                    .list();

            transaction.commit();

            return records;
        } catch (Exception e) {
        // Place Logger here...
            throw new WebApplicationException(e);
        } finally {
            session.close();
        }
    }

}

私が抱えている問題は、HibernateUtil.pagedQuery(detatchedCriteria、start、size)を使用してDataクラスをクエリしようとすると、結果リストがsizeパラメーターと一致しないことです。この理由は、hibernateがタグ(Data.Label.Tags)を含めるようにクエリを作成する方法であることがわかりました。

たとえば、ラベルに複数のタグが関連付けられている場合、完全なページ分割クエリで使用されるデータオブジェクトサブクエリの結果リストは次のようになります(これは、Hibernateがコンソールに吐き出すSQLを解析することで見つかりました)

  1. データ-1;ラベル:タグ-1
  2. データ-1;ラベル;タグ-2
  3. データ-2;ラベル;タグ-1
  4. データ-2;ラベル;タグ-2
  5. 等...

これをsize=3で呼び出すと、返される結果セットは次のようになります。

  1. データ-1;ラベル:タグ-1
  2. データ-1;ラベル;タグ-2
  3. データ-2;ラベル;タグ-1

ただし、Hibernateは最初の2つの行をグループ化し(同じDataオブジェクトであるため)、返されたListオブジェクトのサイズは2(Data-1とData-2)になります。

setResultTransformerメソッドをGoogleで見つけたProjectionアプローチに置き換えようとしましたが、データオブジェクトのIDしか返されませんでした。

誰かアドバイスはありますか?ここからどこへ行けばいいのかわからない…

4

1 に答える 1

2

Hibernateでページ付けする一般的な問題に直面しています。resultTransformerは「Java」側で適用されるため、ページネーションはすでにDB側で行われています。

最も簡単な(おそらく最も最適化されていない)のは、2つのクエリを実行することです。1つはプロジェクションとページ付け(既に行ったものと同様)を使用し、もう1つはプロジェクションIDを使用します。次に例を示します。

//get the projection    
Criteria criteria = factory.getCurrentSession().createCriteria(getEntityClass());
    criteria.setProjection(Projections.distinct((Projections.projectionList().add(Projections.id()).add(Projections.property("name")))));
//paginate the results
    criteria.setMaxResults(pageSize);
    criteria.setFirstResult(first);

List<Object[]> idList = criteria.list();
//get the id's from the projection
        List<Long> longList = new ArrayList<Long>();
        for (Object[] long1 : idList) {
            Object[] record = long1;
            longList.add((Long) record[0]);
        }

if (longList.size() > 0) {
//get all the id's corresponding to the projection, 
//then apply distinct root entity
            criteria = factory.getCurrentSession().createCriteria(getEntityClass());
            criteria.add(Restrictions.in("id", longList));
            criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
        } else {
//no results, so let's ommit the second query to the DB
            return new ArrayList<E>();
        }

return criteria.list();
于 2012-11-12T12:30:48.557 に答える