J'ai une classe Order
qui a une liste de OrderTransactions
et je l'ai mappée avec un mappage Hibernate un à plusieurs comme suit:
@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
Ces Order
s ont également un champ orderStatus
, qui est utilisé pour filtrer avec les critères suivants:
public List<Order> getOrderForProduct(OrderFilter orderFilter) {
Criteria criteria = getHibernateSession()
.createCriteria(Order.class)
.add(Restrictions.in("orderStatus", orderFilter.getStatusesToShow()));
return criteria.list();
}
Cela fonctionne et le résultat est comme prévu.
Maintenant voici ma question : Pourquoi, lorsque je mets explicitement le type de recherche à EAGER
, la Order
s apparaît-elle plusieurs fois dans la liste résultante?
@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
Comment devrais-je changer mon code de critères pour atteindre le même résultat avec le nouveau paramètre?
En plus de ce qui est mentionné par Eran, un autre moyen d'obtenir le comportement souhaité est de définir le transformateur de résultat:
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
essayer
@Fetch (FetchMode.SELECT)
par exemple
@OneToMany(targetEntity = OrderTransaction.class, fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@Fetch (FetchMode.SELECT)
public List<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
N'utilisez pas List et ArrayList mais Set et HashSet.
@OneToMany(targetEntity = OrderTransaction.class, cascade = CascadeType.ALL)
public Set<OrderTransaction> getOrderTransactions() {
return orderTransactions;
}
En utilisant Java 8 et Streams, j'ajoute à ma méthode d'utilitaire cette déclaration de retour:
return results.stream().distinct().collect(Collectors.toList());
Les flux suppriment les doublons très rapidement. J'utilise des annotations dans ma classe d'entité comme ceci:
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "STUDENT_COURSES")
private List<Course> courses;
Je pense qu'il est temps dans mon application d'utiliser la session dans la méthode où j'ai besoin de données de base de données. Closse session quand j'ai fini. Ofcourse a défini ma classe Entity pour utiliser le type d'extraction leasy. Je vais au refactor.
J'ai le même problème pour récupérer 2 collections associées: l'utilisateur a 2 rôles (Ensemble) et 2 repas (Liste) et les repas sont dupliqués.
@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 n'aide pas (requête 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);
Enfin, j'ai trouvé 2 solutions:
Solution finale:
@EntityGraph(attributePaths = {"meals"}, type = EntityGraph.EntityGraphType.LOAD)
@Query("SELECT u FROM User u WHERE u.id=?1")
User getWithMeals(int id);
UPDATE: sauf si javadoc pour org.springframework.data.jpa.repository.EntityGraph.EntityGraphType#FETCH
les attributs spécifiés par les nœuds d'attribut du graphique d'entité sont traités comme FetchType.EAGER et les attributs non spécifiés sont traités comme FetchType.LAZY
avec ce type, les rôles sont également récupérés.
Cela ne semble pas être un bon comportement d'appliquer une jointure externe et d'obtenir des résultats dupliqués. La seule solution qui reste est de filtrer nos résultats en utilisant des flux. Merci Java8 donnant un moyen plus facile de filtrer.
return results.stream().distinct().collect(Collectors.toList());