Order
のリストを持つOrderTransactions
クラスがあり、次のような1対多のHibernateマッピングでマッピングしました。
@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
これらのOrder
sには、フィールドorderStatus
もあります。これは、次の基準でフィルタリングするために使用されます。
public List<Order> getOrderForProduct(OrderFilter orderFilter) {
Criteria criteria = getHibernateSession()
.createCriteria(Order.class)
.add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));
return criteria.list();
}
これは機能し、結果は期待どおりです。
さあ私の質問です:フェッチタイプを明示的にEAGER
に設定すると、Order
sが結果リストに複数回表示されるのはなぜですか?
@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
新しい設定で同じ結果を得るには、どのように基準コードを変更する必要がありますか?
Eranが述べていることに加えて、あなたが望む振る舞いを得る別の方法は、結果トランスフォーマーを設定することです:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
試してみる
@Fetch (FetchMode.SELECT)
例えば
@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch (FetchMode.SELECT)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
ListとArrayListを使用せず、SetとHashSetを使用してください。
@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public Set<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
Java 8とStreamsをユーティリティメソッドに追加すると、この戻り値が返されます。
return results.stream().distinct().collect(Collectors.toList());
ストリームは重複を非常に高速に削除します。次のように、Entityクラスで注釈を使用します。
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;
データベースのデータが必要なメソッドでセッションを使用することは、私のアプリではさらに進んでいると思います。完了したらセッションを終了します。もちろんEntityクラスは、遅延フェッチタイプを使用するように設定します。リファクタリングに行きます。
2つの関連コレクションを取得するのと同じ問題があります。ユーザーには2つのロール(セット)と2つの食事(リスト)があり、食事が重複しています。
@Table(name = "users")
public class User extends AbstractNamedEntity {
@CollectionTable(name = "user_roles", joinColumns = @JoinColumn(name = "user_id"))
@Column(name = "role")
@ElementCollection(fetch = FetchType.EAGER)
@BatchSize(size = 200)
private Set<Role> roles;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
@OrderBy("dateTime DESC")
protected List<Meal> meals;
...
}
DISTINCTは役に立ちません(DATA-JPAクエリ):
@EntityGraph(attributePaths={"meals", "roles"})
@QueryHints({@QueryHint(name= org.hibernate.jpa.QueryHints.HINT_PASS_DISTINCT_THROUGH, value = "false")}) // remove unnecessary distinct from select
@Query("SELECT DISTINCT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);
ようやく2つのソリューションが見つかりました。
最終的解決:
@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);
更新:org.springframework.data.jpa.repository.EntityGraph.EntityGraphType#FETCH
のjavadocを除く
エンティティグラフの属性ノードで指定された属性はFetchType.EAGERとして扱われ、指定されていない属性はFetchType.LAZYとして扱われます
このタイプのロールでもフェッチされます。
外部結合を適用し、重複した結果をもたらす素晴らしい動作ではないようです。残っている唯一の解決策は、ストリームを使用して結果をフィルタリングすることです。簡単なフィルタリング方法を提供してくれたJava8に感謝します。
return results.stream().distinct().collect(Collectors.toList());