En Hibernate StatelessSession Prevenir la filtración de duplicados al tener un EAGER JOIN

Paul Taylor:

Tengo una canción de la clase que contiene una colección de CoverArt s

por ejemplo,

@OneToMany(fetch=FetchType.LAZY, cascade={CascadeType.ALL})
@JoinColumn(name = "recNo")
private List<CoverArt> coverArts;

y estoy usando Hibernate 4.3.11 y la base de datos DB2 y tengo esta consulta para recuperar una lista de canciones de su clave primaria, junto con su coverart.

public static List<Song> getSongsWithCoverArtFromDatabase(Session session, List<Integer> ids)
    {
        try
        {
            Criteria c = session
                    .createCriteria(Song.class)
                    .setFetchMode("coverArts", FetchMode.JOIN)
                    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                    .add(Restrictions.in("recNo", ids));
            List<Song> songs = c.list();
            return songs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed LoadSongToDatabase:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

Tenga en cuenta que nos hemos propuesto ir a buscar a modo de JOIN en la coverArts colección, y que tenemos que establecer s etResultTransformer (Criteria.DISTINCT_ROOT_ENTITY) , de lo contrario si tuviéramos una canción con dos registros Coverart que se pueden conseguir dos objetos Canción devuelto de nuevo. Pero cuando se utiliza Criteria.DISTINCT_ROOT_ENTITY Hibernate sería volver correctamente una canción que contiene dos coverArts.

Sin embargo yo sólo he tratado de hacer lo mismo pero utilizando un StatelessSession. El razonamiento es que estoy tratando de seleccionar los datos para la creación de un informe y quiero velocidad de maximizar y minimizar el consumo de memoria, sin embargo

   public static List<Song> getSongsWithCoverArtFromDatabase(StatelessSession session, List<Integer> ids)
    {
        try
        {
            Criteria c = session
                    .createCriteria(Song.class)
                    .setFetchMode("coverArts", FetchMode.JOIN)
                    .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
                    .add(Restrictions.in("recNo", ids));
            List<Song> songs = c.list();
            return songs;
        }
        catch (Exception e)
        {
            MainWindow.logger.log(Level.SEVERE, "Failed LoadSongToDatabase:" + e.getMessage(), e);
            throw new RuntimeException(e);
        }
    }

esto parece ignorar el .setResultTransformer (Criteria.DISTINCT_ROOT_ENTITY) yS¯ devuelve filas duplicadas.

Es esto un error conocido, la forma en que la intención de comportarse?

df778899:

Parece que este es deficiencia en la forma en StatelessSessionImplse implementa en hibernación, pero una solución podría estar en el camino también ...

Es evidente que con el FetchMode.JOIN, la consulta SQL será (dejó externa) que une a través de las dos tablas, por lo que puede devolver varias filas por canción. Normalmente Hibernate resuelve cada fila devuelta a través de su PersistenceContext.

Si está interesado, se puede ver esto en la fuente de Hibernate para Loader aquí . Entonces, dependiendo del tipo de Session, SessionImpl.getEntityUsingInterceptor () habla con el PersistenceContext, pero StatelessSessionImpl.getEntityUsingInterceptor () simplemente devuelve un valor nulo. Sin embargo hay una cometen más tarde a este método que se ve a hacer lo correcto. La confirmación se parte de HHH-11147 , que dice que las versiones son fijos Hibernate 5.3.11 y 5.4.4 - que no presentan en el repo de Maven en el momento de la escritura.

Mientras tanto, una solución sería la de liar ResultTransformer. Este es un ejemplo bastante 'al punto':

public class DistinctSongResultTransformer implements ResultTransformer {
    private ResultTransformer defaultTransformer = Criteria.DISTINCT_ROOT_ENTITY;

    @Override
    public Object transformTuple(Object[] tuple, String[] aliases) {
        return defaultTransformer.transformTuple(tuple, aliases);
    }

    @SuppressWarnings("rawtypes")
    @Override
    public List transformList(List collection) {
        Map<Integer, Song> distinctSongs = new LinkedHashMap<>();
        for (Object object : collection) {
            Song song = (Song) object;
            distinctSongs.putIfAbsent(song.getId(), song);
        }
        return new ArrayList<>(distinctSongs.values());
    }
}

La diferencia es que el normal, DistinctRootEntityResultTransformerse supone que sólo habrá una instancia única de la entidad en la sesión - se puede ver la compara aquí .

Es evidente que hay espacio para hacer que el ejemplo más reutilizable también, sobre todo para abstraer el getId().

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=209277&siteId=1
Recomendado
Clasificación