在 Hibernate 中,StatelessSession 在有 EAGER JOIN 时防止过滤掉重复项

In Hibernate Does StatelessSession prevent filtering out duplicates when have an EAGER JOIN

我有一首 歌曲 class 包含 CoverArt 的集合

例如

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

我正在使用 Hibernate 4.3.11 和 DB2 数据库,我有这个查询来通过主键和封面艺术检索歌曲列表。

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

请注意,我们在 coverArts 集合上将获取模式设置为 JOIN,并且我们需要设置 s etResultTransformer(Criteria.DISTINCT_ROOT_ENTITY),否则如果我们有一首包含两个翻唱唱片的歌曲,我们将得到两个 Song 对象 return 返回。但是当使用 Criteria.DISTINCT_ROOT_ENTITY Hibernate 时会正确 return 一首包含两个封面的歌曲。

但是我刚刚尝试做同样的事情,但使用的是 StatelessSession。原因是我只是想 select 数据来创建报告,我想最大化速度并最小化内存消耗,但是

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

这似乎忽略了 .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) 和 returns 重复行。

这是已知错误吗?它的行为方式是什么?

看起来这是 StatelessSessionImpl 在 Hibernate 中实现的方式的缺点,但修复可能也在路上...

很明显,对于 FetchMode.JOIN,SQL 查询将(左外)连接两个表,因此每首歌曲可能 return 多行。通常 Hibernate 通过其 PersistenceContext 解析每一行 returned。

如果有兴趣,您可以在撰写本文时在 Loader here. Then, depending on the type of Session, SessionImpl.getEntityUsingInterceptor() talks to the PersistenceContext, but StatelessSessionImpl.getEntityUsingInterceptor() just returns null. However there is a later commit to this method that looks to do the right thing. The commit is part of HHH-11147, which says the fix versions are Hibernate 5.3.11 and 5.4.4 - not showing in the Maven repo 的 Hibernate 源代码中查看。

与此同时,一种解决方法是推出您自己的 ResultTransformer。这是一个相当 'to the point' 的例子:

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());
    }
}

不同之处在于正常的 DistinctRootEntityResultTransformer 假定会话中只有一个实体的唯一实例 - 您可以看到比较 here.

显然,还有使该示例更具可重用性的空间,特别是抽象 getId()