2012-05-18 81 views
2

我不是JPA持久性标准API大师,有时候我会因为使用它而感到非常头疼。第二次执行相同的criteriaQuery会生成错误的sql

昨天我注意到了一个新奇怪的行为。我将发布的代码是对现有功能代码的改编,因此不要着重于简单的错误。我使用glassfish 3.1.1和相应的eclipse持久性插件和Mysql DB。

我写了一个criteriaQuery过滤来自不同表格的数据。如果这个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)) 

如果没有人对为什么会发生,我可以尝试重现它在一个简单的方式的想法。

感谢 菲利波

+0

我想补充一点,一切正常,如果我产生了第二查询执行的,而不是使用现有的一个相同的CriteriaQuery中。我这样做是为了节省计算时间。 – Filippo

+0

你有没有想过这一个?我有非常类似的问题,除非我的'count'查询有时是正确的,有时是错误的(随机!)。如果错误,则与您的类似方法是错误的(表格在FROM部分使用不同的别名(t0和t1)命名两次)。 – olafure

回答

0

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

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

0

看起来您可能发现了实施中的错误。 Hibernate有点类似(或者说更复杂但不同)的情况。另外,从JPA Specification部“6.8查询修改”鼓励CriteriaQuery重用:

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

相关问题