これを投稿する前にかなり調査しましたが、気に入らない回避策を実装しました。私は Hibernate の @Filter および @FilterDef アノテーションを試しましたが、ほとんど成功しませんでした。とにかく、永続化レイヤーがプロバイダー フレームワークに依存するようになるため、そのオプションはあまり好きではありませんでした。ただし、ここには hibernate 固有の注釈を 1 つ含めているため、私のソリューションはフレームワーク固有のコードを完全に欠いているわけではありません。
基本的に、他の Person のコレクションを含む家族を持つ 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」ステートメントや、うまくいかないことを必死に知っていた他の無意味な組み合わせを試したときに、多くの構文エラーが発生しましたが、とにかく試しました。
最後に、2 番目のバージョンを使用し、Predicate オブジェクトを使用して結果の familyMember コレクションをフィルター処理しました。必要な結果が得られますが、それはコレクションを取得した後、コレクションを反復処理することを意味します (JDK1.8 でラムダを使用します!!!)。
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構文はありますか?