2009-07-07 1131 views
35

tengo una clase llamada SynonymMapping que tiene una colección de valores asignada como CollectionOfElementsHibernate CollectionOfElements EAGER fetch elementos duplicados

@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; 
    } 
} 

tengo una prueba donde almaceno dos objetos SynonymMapping a la base de datos y luego pedir a la base de datos para devolver todos los objetos SavedMessageing, esperando recibir los dos objetos que almacené.

Cuando cambio el mapeo de valores para estar ansioso (como se muestra en el código por la línea comentada) y vuelvo a ejecutar la prueba, recibo cuatro coincidencias.

He limpiado la base de datos entre ejecuciones y puedo duplicar este problema intercambiando entre impaciente y perezoso.

Creo que tiene que ver con las uniones que hibernate crea debajo pero no puedo encontrar una respuesta definitiva en línea.

¿Alguien puede decirme por qué una búsqueda ansiosa está duplicando los objetos?

Gracias.

+0

Todos los que tengan la excepción "Se encontró más de una fila con el identificador dado" deben tenerlo en cuenta. Realmente ahorra muchas horas sin saber qué demonios está yendo mal. Ver respuesta @ user176668 !! –

Respuesta

27

En general, no es una buena idea imponer la búsqueda ansiosa en el mapeo: es mejor especificar uniones entusiastas en las consultas apropiadas (a menos que esté 100% seguro de que bajo ninguna circunstancia su objeto tendrá sentido/será válido sin que esa colección sea poblada).

La razón por la que obtiene duplicados es porque Hibernate se une internamente a sus tablas raíz y de colección. Tenga en cuenta que realmente son duplicados, p. para 2 SynonymMappings con 3 elementos de colección, cada uno obtendría 6 resultados (2x3), 3 copias de cada entidad SynonymMapping. Por lo tanto, la solución más fácil es ajustar los resultados en un conjunto para garantizar que sean únicos.

+34

Pero, ¿por qué Hibernate no puede filtrarlos, no puedo entender por qué querría que esto ocurriera así? –

+0

Puedo confirmar que funciona. Estaba usando una Colección solo en general, y eso fue un error. –

2

Se podría utilizar una cláusula SELECT DISTINCT (Hibernate Query Language) como sigue

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

cláusula DISTINCT elimina las referencias duplicadas en hibernación.

Aunque tanto el componente como la colección de valores tiene su ciclo de vida vinculado a la clase de entidad propietaria, debe declararlos en la cláusula de selección para recuperarlos. (LEFT JOIN FETCH synonym.values)

respuesta de ChssPly76 es otro enfoque, pero no se olvide de anulación es igual y el método de acuerdo con código hash Establecer semánticas

respecto,

+3

Me preguntaba más acerca de por qué sucede esto y por qué se tomó la decisión de diseño para que hibernate responda así más que las diferentes formas de evitarlo. – Rachel

62

entré en el mismo problema - cuando establece FetchType.EAGER para @CollectionOfElements, Hibernate intenta obtener todo de una vez, es decir, utilizando una única consulta para cada entrada de elemento vinculado a un objeto "maestro". Este problema se puede resolver con éxito a un costo de consulta N + 1, si agrega la anotación @Fetch (FetchMode.SELECT) a su colección. En mi caso, quería tener una entidad MediaObject con una colección de sus elementos de metadatos (códec de video, códec de audio, tamaños, etc.).El mapeo para una colección metadataItems se ve de la siguiente manera:

 

@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) 
private Map<String, String> metadataItems = new HashMap<String, String>(); 
+2

Muchas gracias por esto. – AHungerArtist

+1

Me gustó la solución, porque no necesitamos envolver el resultado con un conjunto para obtener la singularidad. – sanbhat

+0

¡Muchas gracias SOOOOO por esto! Guardado mi día :) – pasql

5

he enfrentado a este problema y lo resolvió mediante

criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

Esto limpia los duplicados que son causadas por la unión hecha a la mesas para niños

0

En lugar de FetchMode.SELECT con N + 1 consultas, es mejor usar BatchSize e.q. @BatchSize(size = 200).

DISTINCT y Criteria.DISTINCT_ROOT_ENTITY no ayuda, si tiene que buscar más de 1 asociación. Para este caso, vea otras soluciones: https://stackoverflow.com/a/46013654/548473

Cuestiones relacionadas