2010-09-28 47 views
2

假設我有以下實體域:JPA2條件查詢在實體層次

@Entity 
@Inheritance(strategy=InheritanceType.SINGLE_TABLE) 
@DiscriminatorColumn(name="TYPE") 
public abstract class Entity1 { 
//some attributes 
} 

@Entity 
@DiscriminatorValue("T1") 
public class Entity2 extends Entity1 { 
    @OneToMany(fetch=FetchType.EAGER, cascade = { CascadeType.ALL }, mappedBy="parent") 
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN) 
    private Set<Entity1Detail> details = new HashSet<Entity1Detail>(); 
} 

@Entity 
public class Entity1Detail { 
    @ManyToOne 
    @JoinColumn(name="REF") 
    private Entity2 parent; 

    @Basic 
    private Integer quantity; 
} 

@Entity 
@DiscriminatorValue("T2") 
public class Entity3 extends Entity1 { 
//some other attributes 
} 

當我做了JPQL查詢:

select e from Entity1 e left join e.details d where d.quantity > 1 

運行狀況良好(左連接; P)。然而,當我嘗試使用JPA2標準API來構建相同的查詢:

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery q = builder.createQuery(); 
Root r = q.from(Entity1.class); 
q.select(r); 
q.where(builder.gt(r.join("details", JoinType.LEFT).get("quantity"), 1)); 

我的「加盟」,因爲屬性的「詳細信息」不屬於ENTITY1(實際上是真正得到NPE,我必須選擇在Entity2.class上)。事情是,當我必須使用Criteria API構建我的動態查詢時,我並不真正瞭解層次結構的任何內容,只是傳遞了一個Class。

我明白,Criteria API是類型安全的,所有這一切,但有沒有辦法解決這個問題?有可能別名(爲我所用的Hibernate API的標準之前,穿越與別名連接):

Criteria c = session.createCriteria(Entity1.class); 
c.createAlias("details", "d"); 
c.add(Restrictions.ge("d.quantity", 1)); 
+0

實際上,我有點不對。它是JPA2的hibernate實現,允許您運行上面提到的JPQL查詢。如果您使用OpenJPA(例如),則在兩種情況下都會失敗。 – 2010-10-13 15:42:38

回答

1

你需要立足於entity2.details查詢。由於標準的API是類型安全的,它捕獲的是使用實體沒有命名的字段「細節」

CriteriaBuilder builder = em.getCriteriaBuilder(); 
CriteriaQuery q = builder.createQuery(); 
Root r = q.from(Entity2.class); // Must use subclass as root 
q.select(r); 
q.where(builder.gt(r.join("details", JoinType.LEFT).get("quantity"), 1)); 

由於ENTITY2延伸ENTITY1,你可以投你的結果父類型安全。例如:

CriteriaQuery<Entity1> q = builder.createQuery(Entity1.class); 
Root r = q.from(Entity2.class); // Must use subclass as root 

將返回ENTITY1

CriteriaQuery<Entity2> q = builder.createQuery(Entity2.class); 
Root r = q.from(Entity2.class); // Must use subclass as root 

名單將返回ENTITY2

列表

編輯:

我想我在這裏誤解的目標。如果你想要所有的Entity1,除非它們是Entity2的details.quantity < = 1,你需要做更多。

您不能使用從Entity1Detail到Entity1的左連接,因爲這不是嚴格類型安全的。相反,您需要以某種方式將Entity2加入Entity1Detail。可能這裏使用的最佳工具是相關子查詢

CriteriaQuery<Entity1> q = builder.createQuery(Entity1.class); 
Root<Entity1> ent1 = q.from(Entity1.class); 

SubQuery<Entity2> subq = q.subquery(Entity2.class); 
Root<Entity2> ent2 = subq.from(Entity2.class); 
Path<Integer> quantity = ent2.join("details", JoinType.LEFT).get("quantity"); 
Predicate lessThan = builder.lte(quantity,1); 
Predicate correlatedSubqJoin = cb.equal(ent1,ent2) 
subq.where(lessThan, correlatedSubqJoin); 

q.select(ent1); 
q.where(builder.exists(subq).not()); 

的標準API不知道你是單表繼承,所以你必須編寫查詢所有繼承策略,包括連接繼承策略。

+0

「你需要基於你的查詢entity2.details」我知道,但我不能因爲創建上面的類層次結構的全部要點不知道你的對象是哪個具體類是實例。 – 2011-03-26 14:11:42

+0

實際上,如果你仔細閱讀我的文章,我確切地說你在回覆中寫的是什麼,並解釋爲什麼它不是我想要的;)。 – 2011-03-26 14:15:19

+0

對不起,我誤解了你正在努力完成的事情。澄清:你想找到所有的Entity1,除非它們也是Entity1Detail <= 1的Entity2? – logan 2011-03-27 03:06:59