简体   繁体   English

JPA查询::过滤外部联接结果

[英]JPA Query :: Filtering outer join results

I researched quite a bit before posting this, and I did implement a workaround that I don't like. 在发布此内容之前,我进行了很多研究,并且确实实现了我不喜欢的解决方法。 I tried Hibernate's @Filter and @FilterDef annotations with little success, and I didn't like that option much anyway because it made my persistence layer dependent upon the provider framework. 我尝试了Hibernate的@Filter和@FilterDef注释,但收效甚微,而且我还是不太喜欢该选项,因为它使我的持久层依赖于提供程序框架。 I am including one hibernate specific annotation in here, though, so my solution is not completely devoid of framework-specific code. 不过,我在此处包括一个特定于休眠的注释,因此我的解决方案并非完全没有特定于框架的代码。

Basically, I have a Person class with family members that contains a collection of other Person's. 基本上,我有一个带有家庭成员的Person类,其中包含其他Person的集合。 I want to archive my data, and basically never physically delete anything, so all major objects in the app have a boolean 'deleted' value. 我想存档数据,并且基本上从不物理删除任何内容,因此应用程序中的所有主要对象都具有布尔“已删除”值。

My basic problem was that I could not figure out a JPA query for filtering the familyMembers collection where I would only retrieve those family members that have not been deleted from the database. 我的基本问题是,我无法找出用于过滤familyMembers集合的JPA查询,在该查询中,我只能检索那些尚未从数据库中删除的家庭成员。

@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;
    }

}

In my service implementation, I had this: 在我的服务实现中,我有以下几点:

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(); 

Which only returned those people that had a non-empty familyMembers collection, and only those that had not been deleted. 仅返回那些拥有非空的familyMembers集合的人,而仅返回那些未被删除的人。

Next, I tried: 接下来,我尝试了:

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(); 

This gets too many things ... all non-deleted people, but their familyMembers could have been deleted and they would still get retrieved. 这带来了太多的事情...所有未删除的人,但他们的familyMembers可能已被删除,他们仍然会被取回。

Many syntax errors arose as I tried different "ON" statements and other asinine combinations that I desperately knew wouldn't work, but tried anyway. 当我尝试不同的“ ON”语句和我拼命知道不会起作用但无论如何尝试的其他asinine组合时,出现了许多语法错误。

Finally, I used the second version, and then used a Predicate object to filter the resulting familyMember collections. 最后,我使用了第二个版本,然后使用了Predicate对象来过滤生成的familyMember集合。 I get the results I want, but it means iterating through the collection (bring on the Lambdas in JDK1.8!!!) after retrieving it, just to filter out the baddies. 我得到了想要的结果,但是这意味着在检索到该集合后(在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);
    }
}

I've considered the possibility of pushing this logic up to the application level since I would only filter those Person objects I actually want to work with. 我考虑过将这种逻辑推到应用程序级别的可能性,因为我只会过滤那些我实际上想使用的Person对象。 Are there any other options I have using JPA, or some other clever JPQL syntax that would give me what I want? 我在使用JPA时是否还有其他选择,或者其他一些巧妙的JPQL语法可以满足我的需求?

Well there's the (hibernate specific) @Where annotation you can add to the collection definition. 好了,您可以在集合定义中添加@Where注释(特定于休眠)。

Another option is to do a hard delete and use Hibernate Envers to maintain a full audit history. 另一个选择是进行硬删除并使用Hibernate Envers维护完整的审核历史记录。 You can therefore always retrieve deleted entities from the audit tables and avoids the requirement to have all queries and collection mappings use 'where deleted = 0' 因此,您始终可以从审核表中检索已删除的实体,并避免要求所有查询和集合映射使用“ where Deleted = 0”的要求

http://www.jboss.org/envers http://www.jboss.org/envers

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM