【问题标题】:JPA Query :: Filtering outer join resultsJPA Query :: 过滤外连接结果
【发布时间】:2013-10-12 05:30:43
【问题描述】:

在发布此之前我进行了相当多的研究,并且我确实实施了一个我不喜欢的解决方法。我尝试了 Hibernate 的 @Filter 和 @FilterDef 注释但收效甚微,而且我不太喜欢这个选项,因为它使我的持久层依赖于提供程序框架。不过,我在这里包含了一个特定于休眠的注释,因此我的解决方案并非完全没有特定于框架的代码。

基本上,我有一个包含其他人的集合的家庭成员的 Person 类。我想存档我的数据,并且基本上从不物理删除任何东西,所以应用程序中的所有主要对象都有一个布尔“已删除”值。

我的基本问题是,我无法找出用于过滤 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;
    }

}

在我的服务实现中,我有这个:

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

这得到了太多的东西......所有未删除的人,但他们的家庭成员可能已被删除,他们仍然会被检索。

当我尝试不同的“ON”语句和其他 asinine 组合时,出现了许多语法错误,我非常知道这些是行不通的,但还是尝试了。

最后,我使用了第二个版本,然后使用 Predicate 对象过滤生成的 familyMember 集合。我得到了我想要的结果,但这意味着在检索它之后遍历集合(在 JDK1.8 中引入 Lambda !!!),只是为了过滤掉坏人。

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 语法可以满足我的需求?

【问题讨论】:

    标签: java hibernate jpa jpql


    【解决方案1】:

    嗯,您可以将(特定于休眠的)@Where 注释添加到集合定义中。

    另一种选择是进行硬删除并使用 Hibernate Envers 来维护完整的审计历史记录。因此,您始终可以从审计表中检索已删除的实体,并避免要求所有查询和集合映射使用 'where deleted = 0'

    http://www.jboss.org/envers

    【讨论】:

      猜你喜欢
      • 2011-02-01
      • 2023-03-19
      • 2014-03-21
      • 2014-09-07
      • 2017-11-24
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 2021-05-23
      相关资源
      最近更新 更多