问题
I'm not a JPA persistence criteria API guru and sometimes I get very terrible headaches using it.
Yesterday I noticed a new very weird behaviour. The code I will post is an adaptation of existing functioning code, so don't focus on trivial errors. I'm using glassfish 3.1.1 and the corresponding eclipse persistence plugin and Mysql DB.
I have written a criteriaQuery which filters data from different tables. If this criteriaquery is executed twice the second time it generates wrong SQL query. I cannot figure out why.
public CriteriaQuery createQuery4Count(EntityManager em) {
Calendar lastDate4Search = GregorianCalendar.getInstance();
javax.persistence.criteria.CriteriaBuilder cb = em.getCriteriaBuilder();
javax.persistence.criteria.CriteriaQuery cq = cb.createQuery();
javax.persistence.criteria.Root<Permessimercepath> checkPointRt = cq.from(Permessimercepath.class);
javax.persistence.criteria.Path<Permessimerce> permessimerceClass = checkPointRt.get(Permessimercepath_.permessimerce);
Predicate checkPointDatePredicate = cb.isNull(checkPointRt.get(Permessimercepath_.dataTransito));
Predicate checkPointAreaPredicate = cb.equal(checkPointRt.get(Permessimercepath_.iDArea), area);
Predicate datePredicate = cb.greaterThanOrEqualTo(permessimerceClass.get(Permessimerce_.datafine), lastDate4Search.getTime());
Predicate isValidPredicate = cb.lt(permessimerceClass.get(Permessimerce_.statopermesso), Permessimerce.COMPLETED);
cq.where(cb.and(checkPointAreaPredicate, checkPointDatePredicate, datePredicate, isValidPredicate));
cq.select(cb.countDistinct(checkPointRt));
return cq;
}
CriteriaQuery myCriteriaQuery = createQuery4Count(getEntityManager())
javax.persistence.Query q = getEntityManager().createQuery(myCriteriaQuery );
Long Result = ((Long) q.getSingleResult()).intValue();
// second query created with the same criteriaQuery
q = getEntityManager().createQuery(myCriteriaQuery );
Long Result2 = ((Long) q.getSingleResult()).intValue();
The generated sql is
// First and correct one
SELECT COUNT(t0.ID_permesso) FROM permessimercepath t0 WHERE EXISTS (SELECT t1.ID_permesso FROM permessimerce t2, permessimercepath t1 WHERE ((((t0.ID_permesso = t1.ID_permesso) AND (t0.CheckPointIndex = t1.CheckPointIndex)) AND ((((t1.ID_Area = ?) AND (t1.DataTransito IS NULL)) AND (t2.Data_fine >= ?)) AND (t2.Stato_permesso < ?))) AND (t2.ID_permesso = t1.ID_permesso)))
bind => [3 parameters bound]
// Second and wrong one
SELECT COUNT(t0.ID_permesso) FROM permessimercepath t0, permessimerce t2, permessimercepath t1 WHERE (((((t1.ID_Area = ?) AND (t1.DataTransito IS NULL)) AND (t2.Data_fine >= ?)) AND (t2.Stato_permesso < ?)) AND (t2.ID_permesso = t1.ID_permesso))
If nobody has an idea on why it happens I can try to reproduce it in a simpler way.
Thanks Filippo
回答1:
The problem seems to related with EntityManager.createQuery(CriteriaQuery)
method. I checked the documentation, but nothing is mentioned about whether this method modifies CriteriaQuery or not. Normally one expects this method to not modify the passed parameter. However, what you are getting suggests that EntityManager.createQuery(CriteriaQuery)
method modifies the passed parameter.
If this is the case you will need to call createQuery4Count(getEntityManager()
) before calling EntityManager.createQuery(CriteriaQuery)
in your code each time.
回答2:
It looks likely that you spotted bug in implementation. Slightly similar (or so to say more complex but different) case works fine with Hibernate. Also, section "6.8 Query Modification" from JPA Specification encourages reuse of CriteriaQuery
:
A CriteriaQuery object may be modified, either before or after TypedQuery objects have been created and executed from it. For example, such modification may entail replacement of the where predicate or the select list. Modifications may thus result in the same CriteriaQuery “base” being reused for several query instances.
来源:https://stackoverflow.com/questions/10648142/second-execution-of-the-same-criteriaquery-generates-wrong-sql