我想了解如何使用Criteria API而不是JPQL來應用某些條件,特別是如果可以使用Criteria以遞歸方式從聯接中獲取子實體JPQL可以通過Join層次結構來實現。JPA2 - 使用Criteria API以遞歸方式獲取聯合實體的子實體作爲動態查詢
UPDATE
從微小的和克里斯的評論促使我首先要清楚我想要實現:
我的例子有4個實體按照如下圖。 Enitems與Ensources擁有ManyToOne關係。 Entopics與Enitems具有OneToMany關係。 Entopics與SegmentsNew具有OneToMany關係。
我建立一個搜索頁面,用戶可以通過輸入搜索條件儘可能多或儘可能少找一個選擇項。在下面的例子中,搜索「公司法」應該返回公司法細分市場中的所有項目(即使沒有其他輸入項目)。
我的實體:
Enitems:
@Entity
@Table(name = "enitem")
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "itemid")
private Integer itemid;
@Size(max = 500)
@Column(name = "itemname")
private String itemname;
@Column(name = "daterec")
@Temporal(TemporalType.DATE)
private Date daterec;
@Lob
@Size(max = 65535)
@Column(name = "itemdetails")
private String itemdetails;
private String enteredby;
@OneToMany(mappedBy = "items")
private Collection<Endatamaster> endatamasterCollection;
@JoinColumn(name = "topicid", referencedColumnName = "topicid")
@ManyToOne
private Entopic topics;
@JoinColumn(name = "sourceid", referencedColumnName = "sourceid")
@ManyToOne
private Ensource source;
Entopics:
public class Entopic implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "topicid")
private Integer topicid;
@Size(max = 500)
@Column(name = "topicname")
private String topicname;
@Size(max = 500)
@Column(name = "description")
private String description;
@Column(name = "locksec")
private boolean locksec;
@JoinColumn(name = "segmentid", referencedColumnName = "SEGMENTID")
@ManyToOne
private Segmentnew segments;
@JoinColumn(name = "marketid", referencedColumnName = "marketid")
@ManyToOne
private Enmarkets markets;
@OneToMany(mappedBy = "topics")
private Collection<Enitem> enitemCollection;
Ensource:
@Entity
@Table(name = "ensource")
@NamedQueries({
@NamedQuery(name = "Ensource.findAll", query = "SELECT e FROM Ensource e")})
public class Ensource implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "sourceid")
private Integer sourceid;
@Size(max = 500)
@Column(name = "sourcename")
private String sourcename;
@Size(max = 500)
@Column(name = "description")
private String description;
@JoinColumn(name = "typeid", referencedColumnName = "typeid")
@ManyToOne
private Enitemtype entype;
Segmentsnew:
public class Segmentnew implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Basic(optional = false)
@NotNull
@Column(name = "SEGMENTID")
private Integer segmentid;
@Size(max = 255)
@Column(name = "SEGMENTNAME")
private String segmentname;
@OneToMany(mappedBy = "segments")
private Collection<Entopic> entopicCollection;
@JoinColumn(name = "sectorId", referencedColumnName = "SECTORID")
@ManyToOne
private Sectorsnew sectors;
這方面的一個期望JPQL字符串表示應該是:
Select e FROM Enitems e WHERE e.topics.topicid = :topicid
AND e.source.sourceid = :sourceid; AND e.topics.segments.segmentid = :segmentid
搜索頁面使用JSF Primefaces:
<p:panelGrid columns="2">
<p:outputLabel value="Sectors: "/>
<p:selectOneMenu value="#{sectorBean.sectorid}"
filter="true">
<f:selectItem itemValue="0" itemLabel="NULL"/>
<f:selectItems value="#{sectorBean.secList}"
var="sect"
itemLabel="#{sect.sectorname}"
itemValue="#{sect.sectorid}"/>
<f:ajax listener="#{segmentsBean.segFromSec}" render="segs"/>
</p:selectOneMenu>
<p:outputLabel value="Segments: "/>
<p:panel id="segs">
<p:selectOneMenu value="#{queryBean.segmentid}"
rendered="#{not empty segmentsBean.menuNormList}">
<f:selectItems value="#{segmentsBean.menuNormList}"
var="segs"
itemLabel="#{segs.segmentname}"
itemValue="#{segs.segmentid}"/>
</p:selectOneMenu>
</p:panel>
<p:outputLabel value="Topics: "/>
<p:selectOneMenu value="#{queryBean.topicid}" filter="true">
<f:selectItem itemValue="" itemLabel="NULL"/>
<f:selectItems value="#{clienTopicBean.publicTopMenu}"
var="pubs"
itemLabel="#{pubs.topicname}"
itemValue="#{pubs.topicid}"/>
</p:selectOneMenu>
<p:outputLabel value="Type: "/>
<p:selectOneMenu value="#{typeBean.typeid}" filter="true">
<f:selectItem itemValue="" itemLabel="NULL"/>
<f:selectItems value="#{typeBean.menuList}"
var="type"
itemLabel="#{type.typename}"
itemValue="#{type.typeid}"/>
<f:ajax listener="#{sourceBean.sourceFromType}" render="src"/>
</p:selectOneMenu>
<p:outputLabel value="Sources: "/>
<p:panel id="src">
<p:selectOneMenu value="#{queryBean.sourceid}"
rendered="#{not empty sourceBean.sourceListNorm}">
<f:selectItems value="#{sourceBean.sourceListNorm}"
var="srcs"
itemLabel="#{srcs.sourcename}"
itemValue="#{srcs.sourceid}"/>
</p:selectOneMenu>
</p:panel>
</p:panelGrid>
這是我使用的嘗試CAPI:
public List<Enitem> superQ(Integer topicid, Integer sourceid,
Integer segmentid) {
em.clear();
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery cq = cb.createQuery(Enitem.class);
Root<Enitem> rt = cq.from(Enitem.class);
cq.select(rt);
cq.distinct(true);
List<Predicate> criteria = new ArrayList<Predicate>();
Predicate whereClause = cb.conjunction();
// if() {
Path<Entopic> topJoin = rt.get("topics");
if (topicid != 0) {
ParameterExpression<Integer> p
= cb.parameter(Integer.class, "topicid");
whereClause = cb.and(whereClause, cb.equal(topJoin.get("topicid"), p));
}
if (segmentid != 0) {
ParameterExpression<Integer> p
= cb.parameter(Integer.class, "segmentid");
whereClause = cb.and(whereClause, cb.equal(topJoin.get("segments").get("segmentid"), p));
}
//}
if (sourceid != 0) {
ParameterExpression<Integer> p
= cb.parameter(Integer.class, "sourceid");
whereClause = cb.and(whereClause, cb.equal(rt.get("source").get("sourceid"), p));
}
// if(whereClause.getExpressions().isEmpty()) {
// throw new RuntimeException("no criteria");
// }
cq.where(whereClause);
TypedQuery q = em.createQuery(cq);
if (topicid != 0) {
q.setParameter("topicid", topicid);
}
if (segmentid != 0) {
q.setParameter("segmentid", segmentid);
}
if (sourceid != 0) {
q.setParameter("sourceid", sourceid);
}
return q.getResultList();
}
如果(entityName!= 0)而不是if(entityName!= null)是我很重要的事情,我之前在做這些事情,這導致應用程序需要用戶填充所有參數。這可能是因爲null的整數值實際上是數字零?
生成的SQL:
SELECT DISTINCT t1.itemid, t1.daterec, t1.ENTEREDBY, t1.itemdetails, t1.itemname,
t1.sourceid, t1.topicid FROM enitem t1 LEFT OUTER JOIN entopic t0 ON (t0.topicid
= t1.topicid) LEFT OUTER JOIN ensource t2 ON (t2.sourceid = t1.sourceid) WHERE
(((t0.topicid = ?) AND (t2.sourceid = ?)) AND (t0.segmentid = ?))
應用在動態表現,用戶只需要輸入在搜索頁面中的任何一個值和一個列表,返回與該值對應。 我現在遇到的問題是,如果我執行第二個查詢,即使我已經在方法的開始清除了EntityManager,也會返回相同的結果。所以應用程序只有在重新啓動時纔有效。我需要引用實體嗎?
「*以上是否正確使用'Entopics','Ensource','Segmentsnew'之間的連接來獲取類型爲'Enitems'的列表中的相關項?*」Is這是唯一真正的問題?您可以通過仔細查看生成的SQL語句來確認這一點。對於使用條件或JPQL制定的每個查詢,堅持不懈地執行此操作。 (順便說一下,不需要兩個'Root',你可能已經知道'Join'可以被遞歸鏈接,這取決於你的項目的功能需求。 – Tiny
如果我不清楚,謝謝你,對不起。不,它不是唯一的問題。我的問題是,如果在JPQL中使用相同的'entity.subentity.object'語法,那麼實際上如何遞歸地鏈接到Criteria Api和d?我編輯了這個問題,並進一步澄清了我想要的內容 –
您仍然沒有清楚地顯示出您想要的內容,並且從例外情況中可以看出,enitems沒有定義「主題」屬性。 Criteria可以從幾乎所有的JPQL語句中形成,所以如果你有JPQL查詢,可以顯示你的JPQL查詢。例如,使用「nameOfJoin.source」相當於使用nameOfJoin.get(「source」),所以「nameOfJoin.source.sourceid =:sourceid」可能是qb.equal(nameOfJoin.get(「source」)。get(「 sourceid「),cb.parameter(Integer.class,」sourceid「)) – Chris