java-在Hibernate中,当有EAGER JOIN时,StatelessSession是否防止过滤出重复项

我有一个Song类,其中包含CoverArts的集合

例如

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

并且正在使用Hibernate 4.3.11和DB2数据库,并且我有这个查询,用于按其主键及其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);
        }
    }

请注意,我们已经在coverArts集合上将获取模式设置为JOIN,并且需要设置setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY),否则,如果我们有一首带有两个Coverart记录的歌曲,我们将返回两个Song对象.但是,当使用Criteria.DISTINCT_ROOT_ENTITY时,Hibernate将正确返回一首包含两个CoverArt的乐曲.

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

   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)并返回重复行.

这是一个已知的错误,它意味着什么?

最佳答案

看起来这在Hibernate中实现StatelessSessionImpl的方式是有缺陷的,但是也可能正在解决此问题.

显然,使用FetchMode.JOIN,SQL查询将在两个表之间(左外侧)联接,因此每首歌曲可能返回几行.通常,Hibernate解析通过其PersistenceContext返回的每一行.

如果感兴趣的话,您可以在Loader here的Hibernate源代码中看到它.然后,根据Session的类型,SessionImpl.getEntityUsingInterceptor()与PersistenceContext进行通信,但是StatelessSessionImpl.getEntityUsingInterceptor()只是返回null.但是,此方法有一个later commit看起来可以做正确的事.提交是HHH-11147的一部分,其中说修订版本是Hibernate 5.3.11和5.4.4-在撰写本文时未显示在Maven repo中.

同时,一种解决方法是投放自己的ResultTransformer.这是一个相当“关键”的例子:

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()的空间.

发布了539 篇原创文章 · 获赞 0 · 访问量 1847

猜你喜欢

转载自blog.csdn.net/weixin_44109689/article/details/103934331