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()的空间.
标签:hibernate,hibernate-4-x,java 来源: https://codeday.me/bug/20191210/2104335.html