第二次执行相同的criteriaQuery会生成错误的sql

时间:2012-05-18 07:04:36

标签: sql jpa criteria-api

我不是JPA持久性标准API专家,有时候使用它会让我头痛不已。

昨天我发现了一个非常奇怪的新行为。我将发布的代码是现有功能代码的改编,因此不要关注琐碎的错误。我正在使用glassfish 3.1.1和相应的eclipse持久性插件和Mysql DB。

我编写了一个criteriaQuery来过滤来自不同表的数据。如果此条件查询第二次执行两次,则会生成错误的SQL查询。我无法弄清楚原因。

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();

生成的sql是

// 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))

如果没有人知道它为什么会发生,我可以尝试以更简单的方式重现它。

由于 菲利普

2 个答案:

答案 0 :(得分:0)

问题似乎与EntityManager.createQuery(CriteriaQuery)方法有关。我查看了文档,但没有提到这个方法是否修改了CriteriaQuery。通常,人们希望此方法不会修改传递的参数。但是,您得到的结果表明EntityManager.createQuery(CriteriaQuery)方法修改了传递的参数。

如果是这种情况,您需要在每次调用代码中的getEntityManager()之前调用createQuery4Count(EntityManager.createQuery(CriteriaQuery))。

答案 1 :(得分:0)

您可能发现了实施中的错误。稍微相似(或者说更复杂但不同)的情况与Hibernate一起工作正常。此外,JPA Specification中的“ 6.8查询修改”部分鼓励重用CriteriaQuery

  

可以在之前或之后修改CriteriaQuery对象   已经创建并执行了TypedQuery对象。对于   例如,这种修改可能需要更换地点   谓词或选择列表。因此,修改可能导致   相同的CriteriaQuery“base”被重用于多个查询实例。