2013-10-05 51 views
0

在發佈之前,我研究了很多,我確實實施了一個我不喜歡的解決方法。我嘗試了Hibernate的@Filter和@FilterDef註解,但很少取得成功,因爲它使得我的持久層依賴於提供者框架。不過,我在這裏包含了一個hibernate特定的註釋,所以我的解決方案並不完全沒有框架特定的代碼。JPA Query ::篩選外部連接結果

基本上,我有一個人類與家庭成員,其中包含其他人的集合。我想歸檔我的數據,基本上從不物理刪除任何東西,所以應用程序中的所有主要對象都有一個布爾值「已刪除」值。

我的基本問題是,我找不到一個JPA查詢過濾familyMembers集合,我只能檢索那些尚未從數據庫中刪除的家族成員。

@Entity 
@Table(name="person") 
class Person { 
    private boolean deleted; 
    private List<Person> familyMembers; 

    @ManyToMany(targetEntity=Person.class, cascade={ CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, fetch=FetchType.LAZY) 
    @JoinTable(name = "hp_person_family", 
     joinColumns = { @JoinColumn(name = "person_id") }, inverseJoinColumns = { @JoinColumn(name = "family_person_id") } 
    ) 
    @LazyCollection(LazyCollectionOption.FALSE) 
    public List<Person> getFamilyMembers() { 
     return familyMembers; 
    } 

    @Column(name = "deleted_flag") 
    @Type(type = "yes_no") 
    public boolean isDeleted() { 
     return deleted; 
    } 

} 

在我服務的實現,我有這樣的:

String personQuery = "SELECT DISTINCT(p) FROM Person p " 
     + "LEFT JOIN FETCH p.familyMembers pf " 
     + "WHERE p.deleted = false " 
     + "AND pf.deleted = false"; 
List<Person> people = em.createQuery(personQuery, Person.class) 
     .getResultList(); 

其中僅返回那些人有一個非空familyMembers集合,只有那些沒有被刪除。

接下來,我想:

String personQuery = "SELECT DISTINCT(p) FROM Person p " 
     + "LEFT JOIN FETCH p.familyMembers pf " 
     + "WHERE p.deleted = false"; 
List<Person> people = em.createQuery(personQuery, Person.class) 
     .getResultList(); 

這得到太多的東西......所有未刪除的人,但他們的familyMembers可能已經被刪除,他們仍然會得到恢復。

許多語法錯誤出現了,因爲我嘗試了不同的「ON」語句和其他asinine組合,而我絕望地知道這些組合不會工作,但無論如何嘗試。

最後,我使用了第二個版本,然後使用Predicate對象過濾生成的familyMember集合。我得到了我想要的結果,但是它意味着在檢索後迭代整個集合(在JDK1.8中帶來Lambdas !!!),只是爲了過濾掉壞人。

String personQuery = "SELECT DISTINCT(p) FROM Person p " 
     + "LEFT JOIN FETCH p.familyMembers pf " 
     + "WHERE p.deleted = false"; 
List<Person> people = em.createQuery(personQuery, Person.class) 
     .getResultList(); 

filterPeopleResults(people); 




private void filterPeopleResults(List<Person> people) { 
    if(people == null || people.isEmpty()) return; 
    for(Person person : people) { 
     removeDeletedFamilyMembers(person); 
    } 
} 

private void removeDeletedFamilyMembers(Person person) { 
    if(person.getFamilyMembers().isEmpty() == false) { 
     Predicate predicate = new Predicate() { 
      @Override 
      public boolean evaluate(Object obj) { 
       if(((Person)obj).isDeleted()) return false; 
       return true; 
      } 
     }; 
     CollectionUtils.filter(person.getFamilyMembers(), predicate); 
    } 
} 

我認爲推動這一邏輯到應用層面,因爲我只會過濾那些Person對象其實我想與之合作的可能性。有沒有其他的選擇,我使用JPA或其他一些聰明的JPQL語法,會給我我想要的?

回答

0

那麼你可以添加到集合定義中的@Where註解。

另一種方法是進行硬刪除並使用Hibernate Envers來維護完整的審計歷史記錄。你可以因此始終檢索刪除了審計表實體和避免把所有的查詢和集合映射要求使用「其中刪除= 0」

http://www.jboss.org/envers