2011-04-07 53 views
4

我有以下標準建造的查詢JPA criteriabuilder加入地圖

CriteriaBuilder cb = em.getCriteriaBuilder(); 
    CriteriaQuery<Object> critQuery = cb.createQuery(); 

    Root<Role> role = critQuery.from(Role.class); 

    //create a join between role and permission 
    MapJoin<Role,String,Permission> perm = role.joinMap("permissions"); 
    critQuery.multiselect(role.get("label"), perm.key(), perm.value()); 

    //this line throws NPE 
    Query query = em.createQuery(critQuery); 

最後一行拋出一個空指針異常。

java.lang.NullPointerException 
at org.hibernate.ejb.criteria.path.AbstractPathImpl.prepareAlias(AbstractPathImpl.java:246) 
at org.hibernate.ejb.criteria.path.AbstractPathImpl.render(AbstractPathImpl.java:253) 
at org.hibernate.ejb.criteria.path.AbstractPathImpl.renderProjection(AbstractPathImpl.java:261) 
+0

非常有趣!檢查[源代碼](http://grepcode.com/file/repository.jboss。org/nexus/content/repositories/releases/org.hibernate/hibernate-entitymanager/3.5.0-CR-1/org/hibernate/ejb/criteria/path/AbstractPathImpl.java#AbstractPathImpl.getPathSource%28%29),I認爲這是一些類路徑問題。 – CMR 2011-04-07 13:19:19

回答

9

我有完全相同的問題。經過幾個小時的處理後,在調試Hibernate源代碼之後,並且在反覆檢查書籍和JPA 2.0規範中的示例之後,我決定在EclipseLink中嘗試一下。

因此,我創建了一個非常簡單的例子:一個帶有電話號碼地圖的員工,其中密鑰是電話類型(家庭,辦公室,移動電話),值是電話號碼。

@ElementCollection(fetch=FetchType.EAGER) 
@CollectionTable(name="emp_phone") 
@MapKeyColumn(name="phone_type") 
@Column(name="phone_num") 
private Map<String, String> phoneNumbers; 

我可以證實這工作完全與2.1的EclipseLink和OpenJPA的2.1.0,但它在休眠3.5.3,3.6.1。失敗,3.6.3

CriteriaBuilder builder = entityManager.getCriteriaBuilder(); 
CriteriaQuery<Employee> criteria = builder.createQuery(Employee.class); 
Root<Employee> employeeRoot = criteria.from(Employee.class); 
criteria.select(employeeRoot); 
MapJoin<Employee, String, String> phoneRoot = employeeRoot.joinMap("phoneNumbers"); 

criteria.where(builder.equal(phoneRoot.key(), "HOME")); 

System.out.println(entityManager.createQuery(criteria).getResultList()); 

我想,以及如果Criteria API失敗,也許我可以用命名查詢來完成。有趣的是,Hibernate不支持KEY,VALUE或ENTRY關鍵字,因此查詢證明格式不正確。

http://opensource.atlassian.com/projects/hibernate/browse/HHH-5396

這是運行:

String query = "SELECT e FROM Employee e JOIN e.phoneNumbers p WHERE KEY(p) IN ('HOME')"; 
System.out.println(entityManager.createQuery(query, Employee.class).getResultList()); 

在冬眠它生成以下SQL查詢:

select 
     employee0_.id as id0_, 
     employee0_.name as name0_ 
    from 
     Employee employee0_ 
    inner join 
     emp_phone phonenumbe1_ 
      on employee0_.id=phonenumbe1_.Employee_id 
    where 
     KEY(phonenumbe1_.phone_num) in (
      'HOME' 
     ) 

這顯然是畸形的。

再次,在EclipseLink和OpenJPA中這個工作。

因此,顯然,Hibernate一定會出錯。我在Hibernate的JIRA問題跟蹤

http://opensource.atlassian.com/projects/hibernate/browse/HHH-6103

提交了錯誤,並已經張貼的問題在Hibernate用戶論壇

https://forum.hibernate.org/viewtopic.php?f=1&t=1010411

+0

感謝您的所有努力。我相信很多人會發現它很有用 – user373201 2011-04-08 17:30:34

+0

我碰到同樣的問題,如果有適當的hibernate解決方案,我會非常高興。每個提示都歡迎:) – 2012-05-15 12:44:52

+0

@Tobias如果您查看Hibernate論壇,您會注意到我在那裏提出了兩種解決方法。 – 2012-05-15 13:08:34

2

對於那些被困在Hibernate的3.6,我能通過地圖連接並用謂詞限制結果。這個解決方案有一個重要的警告。這種技術只有在您感興趣的地圖鍵存在於每條記錄時纔有效。如果沒有,則會丟失結果集中您可能會預期的記錄。

我的情況與Edwin的例子非常相似。我們有一個帶有當前名稱映射的Person實體,其中映射關鍵字是一個名稱類型,值是一個Name對象。就我而言,我想要檢索一個人的當前標準名稱。

Root<Person> person = query.from(Person.class); 
Join currentName = person.join("currentNames"); 

NameLookup standardName = NameLookup.lookup("ST"); 

Predicate useridMatches = criteria.equal(person.get("userid"), "user1"); 
Predicate isStandardName = criteria.equal(currentName.get("nameType"), standardName); 
Predicate useridAndStandardName = criteria.and(useridMatches, isStandardName); 
query.where(useridAndStandardName); 

在JPQL中,我們通常使用WITH子句來限制連接。

inner join person.currentNames currentStandardName 
with currentStandardName.nameType.id = :standardNameLookupId