1

Criteria Query API + RAD/Dali が自動生成した、WebSphere v8.0.0.5 の OpenJPA 2.1.2-SNAPSHOT 用の静的正規メタモデルを使用して、次の作業クエリを複製しようとしています。

`SELECT *
FROM CENTER c
    INNER JOIN STATE s ON s.ID = c.STATE_ID
    INNER JOIN HOURS_OF_OPERATION h ON h.CENTER_ID = c.ID
WHERE c.CITY = '<city_name_here>'
ORDER BY h.WEEKDAY_NUMBER;`

このクエリのコアを形成する 4 つのエンティティがあります。

  • Center.java
  • 状態.java
  • HoursOfOperation.java
  • HoursOfOperationPK.java

州ごとに多くのセンターがあります。センターごとに多くの HoursOfOperations があります。センター情報、州の省略形、および月曜から日曜を表す曜日番号 1 から 7 で ASC でソートされたセンターの営業時間で構成される結果セットを返す必要があります。

これが私の方法です:

public Center getCenterInfo(String centerCityName) {
    CriteriaBuilder cb = em.getCriteriaBuilder();

    CriteriaQuery<Center> cq = cb.createQuery(Center.class);
    Metamodel m = em.getMetamodel();
    EntityType<Center> _center = m.entity(Center.class);
    EntityType<State> _state = m.entity(State.class);

    Root<Center> center = cq.from( _center );
    Join<Center, State> state = center.join( Center_.state );
    Join<Center, HoursOfOperation> hop = center.join( Center_.hoursOfOperations );
    cq.select(center).distinct(true);
    Predicate predicate = cb.equal(center.get(Center_.city), centerCityName);
    cq.where(predicate);
    //cq.orderBy(cb.asc(hop.get(HoursOfOperation_.id)));<---Can't access PK field here
    center.fetch( Center_.state );
    center.fetch( Center_.hoursOfOperations );

    TypedQuery<Center> query = em.createQuery( cq );
    Center centerInfo = query.getSingleResult();

    return centerInfo;
}

私は立ち往生している行をコメントアウトしました。私が使用した方法と同じように、何らかのメソッドを呼び出して、ある種の HoursOfOperationPK インスタンスをインスタンス化する必要があると思いますJoin<Center, HoursOfOperation> hop。そうすることで、次のようなものを使用できるようになると思いcq.orderBy(cb.asc(hopPk.get(HoursOfOperationPK_.weekdayNumber))); ます。

次に、 を使用しない場合cq.select(center).distinct(true);、7 レコードではなく 49 レコードが返されます。何故ですか?レコード数を 7 に減らす唯一の方法は、select に個別のメソッドを追加することです。DISTINCT が SQL で何をするかは理解していますが、ANSI スタイルの SQL 構文では 7 レコードしか返されません。

OpenJPA ログ出力は、OrderBy が HoursOfOperation.centerId に適用されていることを示しています。

HoursOfOperation および HoursOfOperationPK エンティティの関連部分は次のとおりです。

@Entity
@Table(name="HOURS_OF_OPERATION")
public class HoursOfOperation implements Serializable {
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    private HoursOfOperationPK id;

    public HoursOfOperationPK getId() {
        return this.id;
    }

    public void setId(HoursOfOperationPK id) {
        this.id = id;
    }
}

@Embeddable
public class HoursOfOperationPK implements Serializable {
    //default serial version id, required for serializable classes.
    private static final long serialVersionUID = 1L;

    @Column(name="CENTER_ID", unique=true, nullable=false)
    private long centerId;

    @Column(name="WEEKDAY_NUMBER", unique=true, nullable=false)
    private long weekdayNumber;

    public HoursOfOperationPK() {
    }
    public long getCenterId() {
        return this.centerId;
    }
    public void setCenterId(long centerId) {
        this.centerId = centerId;
    }
    public long getWeekdayNumber() {
        return this.weekdayNumber;
    }
    public void setWeekdayNumber(long weekdayNumber) {
        this.weekdayNumber = weekdayNumber;
    }
}

EDIT @perissf ASC を使用して明示的な順序なしで目的の結果を生成することができました (weekdayNumber が営業時間テーブルの複合主キーの一部であるため、ソートが暗黙的に発生するようです。明示的なソートが必要です)ただし、それほど幸運ではない可能性がある他のクエリに役立つ可能性があるためです):

    CriteriaBuilder cb = em.getCriteriaBuilder();

    CriteriaQuery<Center> cq = cb.createQuery(Center.class);
    Metamodel m = em.getMetamodel();
    EntityType<Center> _center = m.entity(Center.class);

    Root<Center> center = cq.from( _center );
    Expression<List<HoursOfOperation>> hop = center.get( Center_.hoursOfOperations );
    cq.select(center);
    Predicate predicate = cb.equal(center.get(Center_.city), centerCityName);
    cq.where(predicate);

    center.fetch( Center_.state );
    center.fetch( Center_.hoursOfOperations );

    TypedQuery<Center> query = em.createQuery( cq );
    Center centerInfo = query.getSingleResult();

ただし、次を使用して目的の SQL を生成することもできましたが、唯一の問題は、センターの HoursOfOperation が設定されていないことです (遅延読み込みが原因で、center.Fetch(Center_.hoursOfOperation)重複したレコードが作成されました。解決策はありますか?):

    CriteriaBuilder cb = em.getCriteriaBuilder();
    CriteriaQuery<Center> cq = cb.createQuery(Center.class);
    Metamodel m = em.getMetamodel();
    EntityType<Center> _center = m.entity(Center.class);
    Root<Center> center = cq.from( _center );
    EntityType<HoursOfOperation> _hoo = m.entity(HoursOfOperation.class);
    Root<HoursOfOperation> hoo = cq.from( _hoo );
    cq.select(center).distinct(true);
    Predicate predicate = cb.and(cb.equal(center.get(Center_.city), centerCityName), 
            cb.equal(center, hoo.get(HoursOfOperation_.center)));
    cq.where(predicate);
    cq.orderBy(cb.asc(hoo.get( HoursOfOperation_.id ).get(HoursOfOperationPK_.weekdayNumber)));
    center.fetch( Center_.state );

    TypedQuery<Center> query = em.createQuery( cq );
    Center centerInfo = query.getSingleResult();
4

1 に答える 1

1

EclipseLink + MySql でテストしたところ、次のコードが探しているクエリを生成することを確認できます。

Root<Center> center = cq.from(Center.class);
Join<Center, HoursOfOperation> hoursOfOperation = center.join(Center_.hoursOfOperations);
Join<Center, State> state = center.join(Center_.stateId);
Path<HoursOfOperationPK> hoursOfOperationPK = hoursOfOperation.get(HoursOfOperation_.hoursOfOperationPK);
cq.where(cb.equal(center.get(Center_.city), "cityName"));
cq.orderBy(cb.asc(hoursOfOperationPK.get(HoursOfOperationPK_.weekdayNumber)));
TypedQuery<Center> query = em.createQuery(cq);

結果のクエリ:

SELECT t1.id, t1.city, t1.state_id 
FROM state t0, hours_of_operation t2, center t1 
WHERE ((t1.city = ?) AND ((t0.id = t1.state_id) AND (t2.center_id = t1.id))) 
ORDER BY t2.weekday_number ASC

結果の行が予想に比べて多すぎる場合、これは State エンティティとの結合が原因であり、 where によってフィルター処理されていないPredicateため、結果の行は 2 つのテーブルのデカルト積になります。

于 2013-02-07T20:10:21.070 に答える