2013-06-20 82 views
5

我有以下實體結構:多態性JPA查詢與CriteriaQuery中API

+-----------+    +-------------+   
    | User  | -------------> | Role  | 
    +-----------+    +-------------+ 
            ^
             | 
           +-------+---------+ 
           |     | 
         +-----------+  +-----------+   +------------+ 
         | Role1 |  | Role2 |--------> | SomeEntity | 
         +-----------+  +-----------+   +------------+ 

,我想所有的用戶,其role2所具有的特定屬性值SomeEntity的名字。 我需要用JPA標準API來做到這一點。

我做了什麼至今:

CriteriaBuilder cb = ... 
CriteriaQuery<String> query = cb.createQuery(String.class); 
Root<User> user = query.from(User.class); 
SetJoin<User, Role> userRolesJoin = user.join(User_.roles); 

// As you can see the userRolesJoin is of type Role and a Role doesn't have 
// an property someEntity. So how to "cast" the userRolesJoin into an 
// SetJoin<User, Role2>. 

如何在這裏做多態查詢?有什麼建議麼?

不幸的是,JPA標準API並不像Hibernate的critera API那樣直觀。

+0

SQL不是一個選項嗎?它只是所有這些表之間的一個非常簡單的內部連接... –

+0

可能是的,但它也必須可能與JPA我猜,我不想在代碼中現在引入sql依賴項。 –

回答

5

使用子查詢

我創建基礎上,基於role2類型的子查詢解決了這個問題,用加入了它的「 SomeEntity「實體和 應用謂詞。然後,我使用匹配子查詢謂詞的Role2.id將子查詢連接到「主」查詢。

CriteriaBuilder cb = ... 
CriteriaQuery<String> query = cb.createQuery(String.class); 
Root<User> user = query.from(User.class); 
SetJoin<User, Role> userRolesJoin = user.join(User_.roles); 
Path<String> roleIdPath = userRolesJoin.get(User_.id); 

Subquery<String> subquery = query.subquery(String.class); 
Root<Role2> role2Root = subquery.from(Role2.class); 
Join<Role2, SomeEntity> someEntityJoin = role2Root.join(Role2_.someEntity); 
Path<String> someEntityPropertyPath = someEntityJoin.get(SomeEntity_.aProperty); 
Predicate someEntityPropertyPredicate = cb.equal(someEntityPropertyPath, 
      "a property value"); 
subquery.select(role2Root.get(Role2_.id)); 
subquery.where(someEntityPropertyPredicate); 

In<String> idInSubqueryIds = cb.in(roleIdPath).value(subquery); 

query.select(user.get(User_.username)); 
query.where(idInSubqueryIds); 

EntityManager entityManager = ...; 
TypedQuery<String> query = entityManager.createQuery(query); 
List<String> usernames = query.getResultList(); 
2

您可以在標準中使用as()來施放。

((Path)user.join(User_.roles).as(Role2.class)).join(Role2_.someEntity) 

這可能是供應商的特定先前對JPA 2.1,但我認爲這是JPA 2.1標準。

在JPQL中,您可以使用TREAT操作。

http://wiki.eclipse.org/EclipseLink/UserGuide/JPA/Basic_JPA_Development/Querying/JPQL#Special_Operators

您也可以使用從第二(),

Root<Role2> role2 = query.from(Role2.class); 
... cb.equal(user.join(User_.roles), role2) 
+1

是的,我也找到了這個解決方案,但它只適用於eclipselink。它在openjpa中不起作用,因爲在OpenJPA中,「as」方法返回Expressions $ CastAs對象,並且不實現Path。我使用openjpa,因此它不是我的解決方案。 –

+1

嘗試治療,而不是。 'AS'作爲未正式化的早期版本向下轉播。該規範與Treat相反。 – Chris