Este é realmente o comportamento esperado se eu entendi sua configuração corretamente.
O Hibernate não retorna resultados distintos para uma consulta com a busca de junção externa habilitada para uma coleção (mesmo se eu usar a palavra-chave distinta)? Primeiro, você precisa entender o SQL e como OUTER JOINs funcionam no SQL. Se você não entende e compreende totalmente as junções externas em SQL, não continue lendo este item da FAQ, mas consulte um manual ou tutorial de SQL. Caso contrário, você não entenderá a explicação a seguir e reclamará sobre esse comportamento no fórum do Hibernate.
Exemplos típicos que podem retornar referências duplicadas do mesmo objeto Order:
List result = session.createCriteria(Order.class)
.setFetchMode("lineItems", FetchMode.JOIN)
.list();
<class name="Order">
...
<set name="lineItems" fetch="join">
List result = session.createCriteria(Order.class)
.list();
List result = session.createQuery("select o from Order o left join fetch o.lineItems").list();
Todos esses exemplos produzem a mesma instrução SQL:
SELECT o.*, l.* from ORDER o LEFT OUTER JOIN LINE_ITEMS l ON o.ID = l.ORDER_ID
Quer saber por que as duplicatas estão lá? Observe o conjunto de resultados SQL, o Hibernate não esconde essas duplicatas no lado esquerdo do resultado da junção externa, mas retorna todas as duplicatas da tabela de controle. Se você tiver 5 pedidos no banco de dados e cada pedido tiver 3 itens de linha, o conjunto de resultados terá 15 linhas. A lista de resultados Java dessas consultas terá 15 elementos, todos do tipo Order. Apenas 5 instâncias de Order serão criadas pelo Hibernate, mas duplicatas do conjunto de resultados SQL são preservadas como referências duplicadas a essas 5 instâncias. Se você não entende esta última frase, você precisa ler sobre Java e a diferença entre uma instância no heap Java e uma referência a tal instância.
(Por que uma junção externa à esquerda? Se você tivesse um pedido adicional sem itens de linha, o conjunto de resultados seria de 16 linhas com NULL preenchendo o lado direito, onde os dados do item de linha são para outro pedido. Você deseja pedidos mesmo que eles não têm itens de linha, certo? Caso contrário, use uma busca de junção interna em seu HQL).
O Hibernate não filtra essas referências duplicadas por padrão. Algumas pessoas (não você) realmente querem isso. Como você pode filtrá-los?
Como isso:
Collection result = new LinkedHashSet( session.create*(...).list() );