web-dev-qa-db-fra.com

Hibernate CollectionOfElements EAGER récupère des éléments en double

J'ai une classe appelée SynonymMapping qui a une collection de valeurs mappées en tant que CollectionOfElements

@Entity(name = "synonymmapping")
public class SynonymMapping {

    @Id private String keyId;

    //@CollectionOfElements(fetch = FetchType.EAGER)
    @CollectionOfElements
    @JoinTable(name="synonymmappingvalues", joinColumns={@JoinColumn(name="keyId")})
    @Column(name="value", nullable=false)
    @Sort(type=SortType.NATURAL)
    private SortedSet<String> values;

    public SynonymMapping() {
        values = new TreeSet<String>();
    }

    public SynonymMapping(String key, SortedSet<String> values) {
        this();
        this.keyId = key;
        this.values = values;
    }

    public String getKeyId() {
        return keyId;
    }

    public Set<String> getValues() {
        return values;
    }
}

J'ai un test dans lequel je stocke deux objets SynonymMapping dans la base de données, puis je demande à la base de données de renvoyer tous les objets SynonymMapping enregistrés, en espérant recevoir les deux objets que j'ai stockés.

Lorsque je modifie le mappage des valeurs pour le rendre désirable (comme indiqué dans le code par la ligne commentée) et que je relance le test, je reçois quatre correspondances. 

J'ai nettoyé la base de données entre les exécutions et je peux dupliquer ce problème en échangeant entre passionné et paresseux.

Je pense que cela a à voir avec les jointures créées par hibernate mais je ne trouve pas de réponse définitive en ligne.

Quelqu'un peut-il me dire pourquoi une extraction rapide duplique les objets?

Merci.

37
Rachel

Ce n'est généralement pas une bonne idée d'imposer une extraction rapide dans le mappage - il est préférable de spécifier des jointures enthousiastes dans les requêtes appropriées (sauf si vous êtes sûr à 100% que dans toutes les circonstances votre objet n'aura pas de sens/sera valide sans cette collection. être peuplé). 

Vous obtenez des doublons car Hibernate joint en interne vos tables racine et de collecte. Notez qu’il s’agit vraiment de doublons, par exemple pour 2 SynonymMappings avec 3 éléments de collection chacun, vous obtiendrez 6 résultats (2x3), 3 copies de chaque entité SynonymMapping. La solution de contournement la plus simple consiste donc à intégrer les résultats dans un ensemble, garantissant ainsi qu'ils sont uniques.

29
ChssPly76

Je suis tombé dans le même problème: lorsque vous définissez FetchType.EAGER pour un @CollectionOfElements, Hibernate essaie de tout obtenir en un seul coup, c’est-à-dire en utilisant une seule requête pour chaque entrée d’élément lié à un objet "maître". Ce problème peut être résolu avec un coût de requête N + 1, si vous ajoutez l'annotation @Fetch (FetchMode.SELECT) à votre collection. Dans mon cas, je souhaitais avoir une entité MediaObject avec une collection de métadonnées (codec vidéo, codec audio, tailles, etc.). Le mappage pour une collection metadataItems se présente comme suit:

 
 @ CollectionOfElements (targetElement = String.class, fetch = FetchType.EAGER) 
 @ JoinTable (name = "mo_metadata_item", joinColumns = @JoinColumn (name = "media_object_id") ) 
 @ MapKey (columns = @Column (name = "name")) 
 @ Column (name = "value") 
 @ Fetch (FetchMode.SELECT) 
 carte privée <String, String> metadataItems = new HashMap <String, String> (); 
63
user176668

J'ai fait face à ce problème et je l'ai résolu en utilisant 

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY); 

Cela efface les doublons causés par la jointure faite aux tables enfants.

6
Raja Anbazhagan

Vous pouvez utiliser une clause SELECT DISTINCT (Hibernate Query Language) comme suit

SELECT DISTINCT synonym FROM SynonymMapping synonym LEFT JOIN FETCH synonym.values

La clause DISTINCT supprime les références en double dans Hibernate.

Bien que le cycle de vie de la collection de composants et de types de valeur soit lié à la classe d'entités propriétaire, vous devez les déclarer dans la clause select afin de les récupérer. (LEFT JOIN FETCH synonym.values)

La réponse de ChssPly76 est une autre approche, mais n'oublie pas les méthodes de substitution égales et hashcode selon Set sémantique.

cordialement,

2
Arthur Ronald

Au lieu de FetchMode.SELECT avec N + 1 requêtes, il vaut mieux utiliser BatchSize e.q. @BatchSize(size = 200).

DISTINCT et Criteria.DISTINCT_ROOT_ENTITY n'aident pas, si vous devez extraire plus d'une association. Pour ce cas, voir d'autres solutions: https://stackoverflow.com/a/46013654/548473

0
GKislin

Je l'ai réalisé via simplement ajouter

session.createCriteria(ModelClass.class).setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Cette aide pour supprimer les doublons. 

0
Rahul Gangahar