【问题标题】:Second execution of the same criteriaQuery generates wrong sql第二次执行相同的criteriaQuery生成错误的sql
【发布时间】:2012-05-25 18:18:52
【问题描述】:

我不是 JPA 持久性标准 API 专家,有时我使用它会感到非常头疼。

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

我编写了一个条件查询来过滤来自不同表的数据。如果第二次执行此条件查询两次,则会生成错误的 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))

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

谢谢 菲利波

【问题讨论】:

  • 我想补充一点,如果我为第二个查询执行生成一个相同的条件查询而不是使用现有的条件查询,那么一切都会很好。我这样做是为了节省计算时间。
  • 你有想过这个吗?我遇到了非常相似的问题,除了我的“计数”查询有时是对的,有时是错的(随机的!)。当它出错时,它的错误方式与您的相似(在 FROM 部分中使用不同别名(t0 和 t1)命名两次的表。

标签: sql jpa criteria-api


【解决方案1】:

您似乎在实现中发现了错误。稍微相似(或者可以说更复杂但不同)的情况适用于 Hibernate。此外,JPA Specification 中的“6.8 查询修改”部分鼓励重用 CriteriaQuery

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

【讨论】:

    【解决方案2】:

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

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

    【讨论】:

      猜你喜欢
      • 2012-05-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-02
      • 2020-03-07
      • 2018-01-27
      相关资源
      最近更新 更多