【问题标题】:In Hibernate Does StatelessSession prevent filtering out duplicates when have an EAGER JOIN在休眠状态下,当有 EAGER JOIN 时,StatelessSession 是否会阻止过滤掉重复项
【发布时间】:2019-10-24 23:48:05
【问题描述】:

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

例如

@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,并且我们需要设置 setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY) , 否则,如果我们有一首带有两张封面唱片的歌曲,我们将返回两个 Song 对象。但是当使用 Criteria.DISTINCT_ROOT_ENTITY 时,Hibernate 会正确返回包含两个封面的一首歌曲。

但是,我刚刚尝试做同样的事情,但使用了 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) 并返回重复行。

这是一个已知的错误,它的表现如何?

【问题讨论】:

    标签: java hibernate hibernate-4.x


    【解决方案1】:

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

    很明显,使用FetchMode.JOIN,SQL 查询将(左外)连接两个表,因此每首歌曲可能会返回几行。通常 Hibernate 解析通过其PersistenceContext 返回的每一行。

    如果有兴趣,您可以在 Loader here 的 Hibernate 源代码中查看此内容。然后,根据Session 的类型,SessionImpl.getEntityUsingInterceptor()PersistenceContext 对话,但StatelessSessionImpl.getEntityUsingInterceptor() 只返回空值。但是,此方法有一个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()

    【讨论】:

    • 谢谢您的详细回答,我不明白的是,如果我有一个具有一对多关联的对象,那么 HIbernate 旨在隐藏数据库复杂性,为什么它不会默认处理它,我使用 Hibernate 的次数越多,我就越失望。
    • @PaulTaylor - 根据我的经验,大多数框架让简单的事情变得更容易,但偏离支持的功能要困难得多(可能我们期望过高,因为简单的东西很容易)。 Hibernate 就是一个典型的例子。出于同样的原因,我认识的大多数人都对它爱恨交加。 :)
    • 是的,但是我很困惑,如果我在这里做一些奇怪的事情,因为使用 EAGER 检索对象列表及其关联的 1:M 对我来说似乎是一个非常标准的用例?
    猜你喜欢
    • 2019-01-21
    • 1970-01-01
    • 2013-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-30
    • 2011-07-20
    • 2018-11-01
    相关资源
    最近更新 更多