Hibernate: Diferenca no tratamento de FetchType.EAGER e FetchType.LAZY?

0 respostas
kuchma

Pessoal,

Estou com um problema esquisito aqui. Tenho dois relacionamentos 1-N numa classe. Se utilizo a estrategia de fetch FetchType.LAZY, a coisa funciona legal, se utilizo FetchType.EAGER parece que o Hibernate "se confunde". Provavelmente eu que confundi alguma coisa - mas nao consigo entender esse comportamento.

Em poucas palavras:

- Trazendo tudo com Query (FetchType.LAZY): OK
- Trazendo tudo com Query (FetchType.EAGER): OK
- Trazendo um objeto com Session.get (FetchType.LAZY): OK
- Trazendo um objeto com Session.get (FetchType.EAGER): Problema

Abaixo segue codigo das classes, tabelas geradas e a saida do teste:

Um cliente pode ter N carros e N imoveis:
package teste;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "cliente")
public class Cliente {

    private int id;
    private String nome;
    private List<Carro> carroList = new ArrayList<Carro>();
    private List<Imovel> imovelList = new ArrayList<Imovel>();

    @Id(generate = GeneratorType.AUTO)
    public int getId() { return this.id; }
    public void setId(int id) { this.id = id; }

    @Column(name = "nome", nullable = false)
    public String getNome() { return this.nome; }
    public void setNome(String nome) { this.nome = nome; }

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "id_cliente")
    public List<Carro> getCarroList() { return this.carroList; }
    public void setCarroList(List<Carro> carroList) { this.carroList = carroList; }

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "id_cliente")
    public List<Imovel> getImovelList() { return this.imovelList; }
    public void setImovelList(List<Imovel> imovelList) { this.imovelList = imovelList; }

    public boolean equals(Object o) {
        return ((o instanceof Cliente) && (((Cliente)o).getId() == getId()));
    }
    public int hashCode() {
        return new Integer(this.getId()).hashCode();
    }

}
Carro:
package teste;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "carro")
public class Carro {

    private int id;
    private String modelo;

    @Id(generate = GeneratorType.AUTO)
    public int getId() { return this.id; }
    public void setId(int id) { this.id = id; }

    @Column(name = "modelo", nullable = false)
    public String getModelo() { return this.modelo; }
    public void setModelo(String modelo) { this.modelo = modelo; }

    public boolean equals(Object o) {
        return ((o instanceof Carro) && (((Carro)o).getId() == getId()));
    }
    public int hashCode() {
        return new Integer(this.getId()).hashCode();
    }

}
Imovel:
package teste;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratorType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "imovel")
public class Imovel {

    private int id;
    private String endereco;

    @Id(generate = GeneratorType.AUTO)
    public int getId() { return this.id; }
    public void setId(int id) { this.id = id; }

    @Column(name = "endereco", nullable = false)
    public String getEndereco() { return this.endereco; }
    public void setEndereco(String endereco) { this.endereco = endereco; }

    public boolean equals(Object o) {
        return ((o instanceof Imovel) && (((Imovel)o).getId() == getId()));
    }
    public int hashCode() {
        return new Integer(this.getId()).hashCode();
    }

}
Programinha pra testar esse cenario:
package teste;

import java.util.Arrays;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.AnnotationConfiguration;

public class Programa {

    private static final SessionFactory sessionFactory = 
        new AnnotationConfiguration().configure("/resource/hibernate.cfg.xml")
        .buildSessionFactory();

    private void insert() throws Exception {

        Cliente cliente1 = new Cliente(); cliente1.setNome("Cliente 1");
        Cliente cliente2 = new Cliente(); cliente2.setNome("Cliente 2");

        Carro c1 = new Carro(); c1.setModelo("Carro 1");
        Carro c2 = new Carro(); c2.setModelo("Carro 2");
        Carro c3 = new Carro(); c3.setModelo("Carro 3");
        Carro c4 = new Carro(); c4.setModelo("Carro 4");

        Imovel i1 = new Imovel(); i1.setEndereco("Imovel 1");
        Imovel i2 = new Imovel(); i2.setEndereco("Imovel 2");
        Imovel i3 = new Imovel(); i3.setEndereco("Imovel 3");
        Imovel i4 = new Imovel(); i4.setEndereco("Imovel 4");

        cliente1.setCarroList(Arrays.asList(new Carro[]{ c1, c2 }));
        cliente1.setImovelList(Arrays.asList(new Imovel[]{ i1, i2 }));
        cliente2.setCarroList(Arrays.asList(new Carro[]{ c3, c4 }));
        cliente2.setImovelList(Arrays.asList(new Imovel[]{ i3, i4 }));

        Session session = sessionFactory.openSession();
        Transaction tx = session.beginTransaction();
        session.saveOrUpdate(cliente1);
        session.saveOrUpdate(cliente2);
        tx.commit();
        session.close();

    }

    private void search() throws Exception {

        Session session = null;
        
        session = sessionFactory.openSession();
        Query query = session.createQuery("from Cliente c order by c.nome");
        List list = query.list();
        //session.close();

        for (Object object : list) {
            Cliente cliente = (Cliente)object;
            System.err.println("-------------");
            System.err.printf("%s (Carros: %d, Imoveis: %d)\n", cliente.getNome(), 
                    cliente.getCarroList().size(), cliente.getImovelList().size());
            for (Carro c : cliente.getCarroList()) {
                System.err.printf("  Carro %d - %s\n", c.getId(), c.getModelo());
            }
            for (Imovel i : cliente.getImovelList()) {
                System.err.printf("  Imovel %d - %s\n", i.getId(), i.getEndereco());
            }
            System.err.println("-------------");
        }

        session.close();

        session = sessionFactory.openSession();
        Cliente cliente = (Cliente)session.get(Cliente.class, new Integer(1));
        //session.close();

        System.err.println("-------------");
        System.err.printf("%s (Carros: %d, Imoveis: %d)\n", cliente.getNome(), 
                cliente.getCarroList().size(), cliente.getImovelList().size());
        for (Carro c : cliente.getCarroList()) {
            System.err.printf("  Carro %d - %s\n", c.getId(), c.getModelo());
        }
        for (Imovel i : cliente.getImovelList()) {
            System.err.printf("  Imovel %d - %s\n", i.getId(), i.getEndereco());
        }
        System.err.println("-------------");

        session.close();

    }
    
    public static void main(String[] args) throws Exception {

        Programa programa = new Programa();
        programa.insert();
        programa.search();

    }

}
Tabelas e dados apos rodar o teste (tudo OK):
mysql> select * from cliente; select * from carro; select * from imovel;
+----+-----------+
| id | nome      |
+----+-----------+
|  1 | Cliente 1 |
|  2 | Cliente 2 |
+----+-----------+
2 rows in set (0.00 sec)

+----+---------+------------+
| id | modelo  | id_cliente |
+----+---------+------------+
|  1 | Carro 1 |          1 |
|  2 | Carro 2 |          1 |
|  3 | Carro 3 |          2 |
|  4 | Carro 4 |          2 |
+----+---------+------------+
4 rows in set (0.00 sec)

+----+----------+------------+
| id | endereco | id_cliente |
+----+----------+------------+
|  1 | Imovel 1 |          1 |
|  2 | Imovel 2 |          1 |
|  3 | Imovel 3 |          2 |
|  4 | Imovel 4 |          2 |
+----+----------+------------+
4 rows in set (0.00 sec)

Saida do programa utilizando a estrategia de fetch FetchType.LAZY (OK):

-------------
Cliente 1 (Carros: 2, Imoveis: 2)
  Carro 1 - Carro 1
  Carro 2 - Carro 2
  Imovel 1 - Imovel 1
  Imovel 2 - Imovel 2
-------------
-------------
Cliente 2 (Carros: 2, Imoveis: 2)
  Carro 3 - Carro 3
  Carro 4 - Carro 4
  Imovel 3 - Imovel 3
  Imovel 4 - Imovel 4
-------------
-------------
Cliente 1 (Carros: 2, Imoveis: 2)
  Carro 1 - Carro 1
  Carro 2 - Carro 2
  Imovel 1 - Imovel 1
  Imovel 2 - Imovel 2
-------------

Saida do programa (apagando as tabelas), mas mudando o fetch para FetchType.EAGER (problema):

-------------
Cliente 1 (Carros: 2, Imoveis: 2)
  Carro 1 - Carro 1
  Carro 2 - Carro 2
  Imovel 1 - Imovel 1
  Imovel 2 - Imovel 2
-------------
-------------
Cliente 2 (Carros: 2, Imoveis: 2)
  Carro 3 - Carro 3
  Carro 4 - Carro 4
  Imovel 3 - Imovel 3
  Imovel 4 - Imovel 4
-------------
-------------
Cliente 1 (Carros: 4, Imoveis: 4)
  Carro 1 - Carro 1
  Carro 1 - Carro 1
  Carro 2 - Carro 2
  Carro 2 - Carro 2
  Imovel 1 - Imovel 1
  Imovel 2 - Imovel 2
  Imovel 1 - Imovel 1
  Imovel 2 - Imovel 2
-------------

Se leu ate aqui, obrigado pela paciencia. :)

Onde esta o erro? Algum problema conceitual? O manual do Hibernate Annotations diz que eh incomum e nao recomenda mapear 1-N colocando a FK na mesma tabela do lado N, mas nao consegui entender o porque - parece algo tao normal :).

Marcio Kuchma

Criado 18 de abril de 2006
Respostas 0
Participantes 1