我正在尝试使用 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();