2012-05-18 71 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「基礎」被重複用於多個查詢實例。

相關問題