Я хочу понять, как применять определенные условия с использованием API критериев вместо JPQL, особенно если критерии могут использоваться для рекурсивного получения дочерних объектов из Joins в том же способ, которым JPQL может, через иерархии Join.JPA2 - Получение дочерних сущностей объединенных сущностей рекурсивно с использованием API критериев в качестве динамического запроса
UPDATE
Комментарии от Крошки и Криса побудили меня сначала быть ясно, что я пытаюсь достичь:
Моего примера имеет 4 сущности согласно приведенной ниже схеме. Enitemshas a ManyToOne отношения с Ensources. Энтопика имеет отношения OneToMany с Enitems. Энтопика имеет отношения OneToMany с SegmentsNew.
Я строю страницу поиска, где пользователь может найти выбранный элемент, введя столько или как мало, в критериях поиска. В приведенном ниже примере поиск «Корпоративного права» должен вернуть все элементы сегмента корпоративного права (даже если ничего не введено).
Мои объекты:
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();
}
Важно указать if (entityName! = 0), а не if (entityName! = Null), который я ранее делал, и это заставило приложение требовать, чтобы все параметры были заполнены пользователем. Вероятно, это потому, что целочисленное значение null на самом деле является номером 0?
Сгенерированный 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 в начале метода. SO приложение работает только при перезапуске. Нужно ли мне ссылаться на объекты?
"* Правильно ли вы используете соединение между' Entopics', 'Ensource',' Segmentsnew', чтобы получить связанные элементы в списке типа 'Enitems'? * 'Is это единственный реальный вопрос? Вы можете подтвердить это, внимательно посмотрев на сгенерированный оператор SQL. Сделайте это настойчиво для каждого запроса, который вы формулируете с использованием критериев или JPQL. (Кстати, два 'Root' не нужны. Вероятно, вы уже знаете, что' Join' может быть скован рекурсивно. Это зависит от функциональных требований вашего проекта). – Tiny
Спасибо, и прости, если я не понимаю. Нет, это не единственный вопрос. Мой вопрос заключается в том, как рекурсивно цепочки в Criteria Api a d, если такой же синтаксис «entity.subentity.object» используется как в JPQL? Я отредактировал вопрос и уточнил, что я хочу. –
. Вы все еще не ясно показываете, что хотите, и из-за исключения указывается, что в enitems нет атрибута «themes». Критерии могут быть сформированы из почти любого оператора JPQL, поэтому, возможно, вы увидите свой запрос JPQL, если он у вас есть. Использование «nameOfJoin.source» эквивалентно, например, использованию nameOfJoin.get («source»), поэтому «nameOfJoin.source.sourceid =: sourceid» может быть qb.equal (nameOfJoin.get («source»). Get (" sourceid "), cb.parameter (Integer.class," sourceid ")) – Chris