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;`

我有四个实体构成了这个查询的核心:

  • 中心.java
  • 状态.java
  • HoursOfOperation.java
  • HoursOfOperationPK.java

每个州有许多中心。每个中心有很多 HoursOfOperations。我需要返回一个结果集,其中包括中心信息、州缩写和中心的营业时间,按工作日数字 1-7 排序,代表周一 - 周日。

这是我的方法:

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);,我会返回 49 条记录而不是 7 条记录。这是为什么?唯一将记录计数减少到 7 是附加到选择的不同方法。我了解 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;
    }
}

编辑 @perissf我能够生成所需的结果,而不需要ASC使用的显式顺序(由于 weekdayNumber 是 Hours of Operations 表的复合主键的一部分,因此似乎隐含地发生了排序。我宁愿有显式排序不过,因为它可以帮助我解决我可能不太幸运的其他问题):

    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,因此结果行是两个表的 carthesian 乘积。

于 2013-02-07T20:10:21.070 回答