在发布之前我研究了很多,我确实实施了一个我不喜欢的解决方法。我尝试了Hibernate的@Filter和@FilterDef注释并没有取得什么成功,但我不太喜欢这个选项,因为它使我的持久层依赖于提供者框架。我在这里包含一个特定于hibernate的注释,所以我的解决方案并不完全没有特定于框架的代码。
基本上,我有一个Person类,其中包含其他Person的集合。我想归档我的数据,基本上从不删除任何东西,因此应用程序中的所有主要对象都有一个布尔“已删除”值。
我的基本问题是我无法找出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();
这有太多东西......所有未删除的人,但他们的家庭成员可能已被删除,他们仍然会被检索。
当我尝试使用不同的“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 :(得分:0)
那么(可以使用特定于hibernate的)@Where注释可以添加到集合定义中。
另一种选择是进行硬删除并使用Hibernate Envers来维护完整的审核历史记录。因此,您始终可以从审计表中检索已删除的实体,并避免要求所有查询和集合映射使用“where deleted = 0”