Criteria trazendo resultados duplicados

Olá, tenho uma classe NotaFiscal e uma NotaFiscalItem, os relacionamentos estão definidos da seguinte forma:
Na NotaFiscal:

@OneToMany(mappedBy = "notaFiscal",fetch= FetchType.EAGER)
    List<NotaFiscalItem> item=new ArrayList<>();

Na NotaFiscalItem:

@ManyToOne
    private NotaFiscal notaFiscal;

Quado tento fazer a pesquisa através desse método:

        Session session = ConnectDB.getInstance();
        Criteria criteria = session.createCriteria(NotaFiscal.class);
        List nfs=criteria.list();
        for(int i=0;i<nfs.size();i++){
            System.out.println(((NotaFiscal)nfs.get(i)).getId());
        }

Ele me retorna o seguinte resultado:
11
11
9
9
9
O que é estranho, pois tenho somente 2 notas no banco, a 11 e a 9.
Porém, a nota 11 tem 2 itens e a 9 tem 3, por isso acredito que tem alguma coisa ha ver…
Detalhe: Se eu alterar o fetch type no NotaFiscalItem pra LAZY, funciona certinho… ele me retorna somente 2 objetos.

Tente colocar isso aqui.
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)

Funcionou!
Aproveitando o barco, como eu faço pra botar o javadoc do hibernate no netbeans, pra nao precisar vir aqui pedir sempre que tiver alguma duvida simples como esta rsrs

Uma vez eu achei um jar chamado hibernate-sources.jar e o inclui no classpath, daí isso fazia aparecer o fonte do hibernate.
Qualquer dúvida dá para você o javadoc aqui

http://docs.jboss.org/hibernate/orm/4.1/javadocs/

Mas porque aconteceu isso??

Gostaria de saber tambem por que ele fez essa maracutaia, essa opção DISTINCT_ROOT_ENTITY deveria vir por padrão no Criteria pela lógica… Aliás nao consigo ver logica nenhuma do jeito que ele fez anteriormente rsrs

Aconteceu isso comigo uma vez antigamente.
Daí sabia o porque de ter que colocar o distinct root entity.
Acredito que talvez seja por conta de cada nota fiscal possuir mais de uma notafiscal item e as notafiscal item são eager, o que faria carregar elas junto com a consulta de nota fiscal.

Talvez alguém mais experiente possa dar a resposta.

Tenta 2 testes, 1 é colocar a propriedade como lazy e o outro é mostrar o sql gerado pelo hibernate.

Amigos bom dia!, estou ressuscitando este tópico depois de algum tempo.
Estou com o mesmo problema do mateusviccari , porém não achei a melhor solução no meu caso utilizar a sugestão
do “lele_vader”, antes de tudo gostaria de saber se alguém sabe qual é a origem dos dados duplicados?
Sera que meus mapeamentos estão errados (sou novo no hibernate, hehe)?

Muito Obrigado!!!

[quote=ffoliveira]Amigos bom dia!, estou ressuscitando este tópico depois de algum tempo.
Estou com o mesmo problema do mateusviccari , porém não achei a melhor solução no meu caso utilizar a sugestão
do “lele_vader”, antes de tudo gostaria de saber se alguém sabe qual é a origem dos dados duplicados?
Sera que meus mapeamentos estão errados (sou novo no hibernate, hehe)?

Muito Obrigado!!![/quote]
Por causa do fetch= FetchType.EAGER na lista de Itens da NF o Hibernate fez diretamente Join entre tabelas, é só pegar o SELECT gerado pelo Hibernate e rodar diretamente no banco para entender o que está acontecendo.

Obrigado javaflex, vou analisar e ver se consigo decifrar.
Mas uma pergunta.
Quando uso a anotaçao manytoone de um lado tenho que usar a onetomany do outro lado do relacionamento?

[quote=ffoliveira]Obrigado javaflex, vou analisar e ver se consigo decifrar.
Mas uma pergunta.
Quando uso a anotaçao manytoone de um lado tenho que usar a onetomany do outro lado do relacionamento?
[/quote]
Você faz só se tiver necessidade, modele o básico necessário e depois incrementa com o que precisar. Sobre o caso acima na maioria das vezes o ideal é usar LAZY ou invés de EAGER para listas, e quando quiser fazer fetch join você resolve isso pelo Criteria, sem deixar o modelo pesado.

Ótimo! Esses relacionamentos causam alguns problemas de inicio.
Valeu pela dica!

Legal, consegui verificar o ponto onde a SQL esta replicando as informações (join com tabela desnecessária) .
como que eu faço para retirar esse join? (estou usando criteria)

[quote=ffoliveira]Legal, consegui verificar o ponto onde a SQL esta replicando as informações (join com tabela desnecessária) .
como que eu faço para retirar esse join? (estou usando criteria)[/quote]
Mostre as entidades e mapeamentos envolvidos e sua Criteria. O SQL gerado também.

Entidade Pessoa Juridica

@Entity
@PrimaryKeyJoinColumn(name = "CD_PESSOA")
@Table(name="JURIDICA")
public class PessoaJuridica extends Pessoa implements Serializable{
        @Column(name="CNPJ",insertable=true,length=20,nullable=false,unique=true)
        private String cnpj;
        @Column(name="RAZAOSOCIAL",insertable=true,length=90,nullable=false)
        private String razaosocial;
        @Column(name ="DT_FUNDACAO",insertable=true)
        @Temporal(javax.persistence.TemporalType.DATE)
        private Date datadafundacao;
        @Column(name="IE",insertable=true,length=90,nullable=false)
        private String ie;
        @ManyToMany(fetch=FetchType.EAGER)
        @JoinTable(name="SOCIOS", joinColumns={@JoinColumn(name="CD_PESSOA_FISICA")},inverseJoinColumns={@JoinColumn(name="CD_PESSOA_JURIDICA")})
        private Set<PessoaFisica> fisica = new HashSet<>();

Entidade Telefones

@Entity
@Table(name="TELEFONE")
public class Telefone implements Serializable{
        @Id  
        @GeneratedValue (strategy = GenerationType.IDENTITY)  
        @Column(name="CD_TELEFONE",insertable=true,nullable=false,updatable=false)
        private Integer codigo;
        @ManyToOne
        @JoinColumn (name="CD_PESSOA")
        private Pessoa pessoa;
        @Column(name = "NUMERO",insertable=true,length=16,nullable=false)
        private String numero;
        @Column(name="TIPO",insertable=true,nullable=false)
        private int tipo;

Método Telefone que lista todos os telefones de uma determinada pessoa…

@Override
    public List<Telefone> listar(Integer codigoPessoa) {
       try{
         this.session = HibernateUtil.getSessionFactory().getCurrentSession();
         ts = session.beginTransaction();
         Criteria c =  this.session.createCriteria(Telefone.class).createAlias("pessoa", "p");
         
         c.add(Restrictions.eq("p.codigo", codigoPessoa));
         
         return c.list();
         
       }catch (HibernateException erro){
           System.out.println(erro.getMessage());
             ts.rollback();
             return null;
        }finally{
            try{
                if(this.session.isOpen()){
                    this.session.close();
                }
            }catch(Throwable e){
             
            }
            
        }
       
        
    }

SQL
select
this_.CD_TELEFONE as CD1_7_2_,
this_.NUMERO as NUMERO7_2_,
this_.CD_PESSOA as CD4_7_2_,
this_.TIPO as TIPO7_2_,
p1_.CD_PESSOA as CD1_5_0_,
p1_.CEP as CEP5_0_,
p1_.AGENCIA as AGENCIA5_0_,
p1_.BAIRRO as BAIRRO5_0_,
p1_.BANCO as BANCO5_0_,
p1_.CIDADE as CIDADE5_0_,
p1_.COMPLEMENTO as COMPLEME7_5_0_,
p1_.CONTA as CONTA5_0_,
p1_.ENDERECO as ENDERECO5_0_,
p1_.ESTADO as ESTADO5_0_,
p1_.NUMERO as NUMERO5_0_,
p1_.TP as TP5_0_,
p1_.UF as UF5_0_,
p1_1_.CPF as CPF8_0_,
p1_1_.DH_CADASTRO as DH2_8_0_,
p1_1_.ESTADO_CIVIL as ESTADO3_8_0_,
p1_1_.NACIONALIDADE as NACIONAL4_8_0_,
p1_1_.NOME as NOME8_0_,
p1_1_.PROFISSAO as PROFISSAO8_0_,
p1_1_.RG as RG8_0_,
p1_1_.TRATAMENTO as TRATAMENTO8_0_,
p1_2_.CNPJ as CNPJ9_0_,
p1_2_.DT_FUNDACAO as DT2_9_0_,
p1_2_.IE as IE9_0_,
p1_2_.RAZAOSOCIAL as RAZAOSOC4_9_0_,
case
when p1_1_.CD_PESSOA is not null then 1
when p1_2_.CD_PESSOA is not null then 2
when p1_.CD_PESSOA is not null then 0
end as clazz_0_,
fisica4_.CD_PESSOA_FISICA as CD1_5_4_,
pessoafisi5_.CD_PESSOA as CD2_4_,
pessoafisi5_.CD_PESSOA as CD1_5_1_,
pessoafisi5_1_.CEP as CEP5_1_,
pessoafisi5_1_.AGENCIA as AGENCIA5_1_,
pessoafisi5_1_.BAIRRO as BAIRRO5_1_,
pessoafisi5_1_.BANCO as BANCO5_1_,
pessoafisi5_1_.CIDADE as CIDADE5_1_,
pessoafisi5_1_.COMPLEMENTO as COMPLEME7_5_1_,
pessoafisi5_1_.CONTA as CONTA5_1_,
pessoafisi5_1_.ENDERECO as ENDERECO5_1_,
pessoafisi5_1_.ESTADO as ESTADO5_1_,
pessoafisi5_1_.NUMERO as NUMERO5_1_,
pessoafisi5_1_.TP as TP5_1_,
pessoafisi5_1_.UF as UF5_1_,
pessoafisi5_.CPF as CPF8_1_,
pessoafisi5_.DH_CADASTRO as DH2_8_1_,
pessoafisi5_.ESTADO_CIVIL as ESTADO3_8_1_,
pessoafisi5_.NACIONALIDADE as NACIONAL4_8_1_,
pessoafisi5_.NOME as NOME8_1_,
pessoafisi5_.PROFISSAO as PROFISSAO8_1_,
pessoafisi5_.RG as RG8_1_,
pessoafisi5_.TRATAMENTO as TRATAMENTO8_1_
from
TELEFONE this_
inner join
PESSOA p1_
on this_.CD_PESSOA=p1_.CD_PESSOA
left outer join
FISICA p1_1_
on p1_.CD_PESSOA=p1_1_.CD_PESSOA
left outer join
JURIDICA p1_2_
on p1_.CD_PESSOA=p1_2_.CD_PESSOA
left outer join
SOCIOS fisica4_
on p1_.CD_PESSOA=fisica4_.CD_PESSOA_FISICA
left outer join
FISICA pessoafisi5_
on fisica4_.CD_PESSOA_JURIDICA=pessoafisi5_.CD_PESSOA
left outer join
PESSOA pessoafisi5_1_
on pessoafisi5_.CD_PESSOA=pessoafisi5_1_.CD_PESSOA
where
p1_.CD_PESSOA=1

Você viu como necessário FetchType EAGER? Senão muda de EAGER para LAZY principalmente onde for lista.

javaflex, eu analisei e preciso do FetchType EAGER, pois faço uso de mais objetos do mapeamento

Ideal é deixar as listas no mapeamento como Lazy e em consultas você usa setFetchMode para setar o tipo de fetch que quiser dinamicamente de acordo com a necessidade para montar o SQL único. Neste seu caso então será igual ao do outro colega, em que terá que usar o DISTINCT_ROOT_ENTITY para o Hibernate popular a lista e não deixar duplicar a objeto raiz.

javaflex, muito obrigado pela força!
Vou rever os mapeamentos, e deixar da forma como vc disse.

Novamente no tópico…
Ajustei as classes e os mapeamentos para usar o fetch = FetchType.LAZY
porem o hibernate esta retornando uma exception,

failed to lazily initialize a collection of role

Do que se trata esta erro, e como faço para resolve-lo?