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. 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. 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.
@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.
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.
Many syntax errors arose as I tried different "ON" statements and other asinine combinations that I desperately knew wouldn't work, but tried anyway.
Finally, I used the second version, and then used a Predicate object to filter the resulting familyMember collections. 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.
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. Are there any other options I have using JPA, or some other clever JPQL syntax that would give me what I want?
Well there's the (hibernate specific) @Where annotation you can add to the collection definition.
Another option is to do a hard delete and use Hibernate Envers to maintain a full audit history. 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'
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.