Dados Hierárquicos em JPA

7 respostas
guigouz

Fala… to precisando implementar uma tabela com hierarquia no java. Basicamente é uma tabela que referencia ela mesma… tem um artigo muito bom explicando a teoria em http://dev.mysql.com/tech-resources/articles/hierarchical-data.html

A questão é: alguém tem idéia se é possível tal implementação com JPA, ou devo continuar no caminho do JDBC com SQL direto?

Abraço

gui

7 Respostas

henrique.lima

Olá guigous, é possível efetuar este tipo de relacionamento em JPA, abaixo segue um trecho de código:

@Id
    @GeneratedValue
    private Long id;
       
    @ManyToOne
    private Categoria categoriaPai;
    
    @OneToMany(mappedBy = "categoriaPai")
    private List<Categoria> categorias = new ArrayList<Categoria>();

    // metodos setters e getters

    public void addCategoria(Categoria categoria) {
        categoria.setCategoriaPai(this);
        categorias.add(categoria);
    }

É válido lembrar que para que o JPA insira os registros corretamente no banco, o relacionamento deve estar explícito e por isso é recomendável utilizar um método (neste caso o método addCategoria) para efetuar este relacionamento.
Espero que tenha ajudado.

Boa sorte!

guigouz

Funcionou perfeito. Achei que fosse bem mais complexo que isso.
Sabe dizer se essas queries são otimizadas ?

Valeu

gui

henrique.lima

Olá guigous, esse é um tipo de relacionamento muito simples e o JPA por default carrega seus objetos em modo lazy. Isso quer dizer que toda vez que você carregar uma Categoria as suas subcategorias não serão carregadas (o que é muito bom). Quando você precisar das subcategorias o método getCategorias() será chamado e apenas nesta hora a query (where idPai = x) será executada. As subcategorias das categorias contidas na collection obtidas pelo método getCategorias() também não terão suas subcategorias carregadas que é exatamente como eu faria com jdbc puro.
Desta forma, acredito que não haja mais nada que se possa otimizar neste tipo de relacionamento.

Espero ter sido claro na explicação.

Bons estudos! =)

guigouz

Eu to tendo um problema com o LazyLoad... usando Glassfish e Hibernate. Tenho uma entidade que tem vários filhos (nem é hierárquico como o exemplo que você passou, são duas entidades diferentes)... Um TreeBean (stateless EJB) pega a Entidade com o código

public List<Bem> todos() {
        
        List<Bem> l =  em.createQuery("SELECT b FROM Bem b").getResultList();
        
        System.out.println("FOUND " + l.size());
        return l;
    }

Com isso eu monto uma lista de filhos pro cara abrir um e ver os detalhes. O erro que acontece quando vou acessar qualquer um dos filhos é

Exception in thread "AWT-EventQueue-0" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: br.com.prosoma.memoria.entidades.Bem.registrosConservacao, no session or session was closed

Ok... o getRegistrosConservacao(); tá rodando no app-client (local - swing) e a sessão tá no server... Tem alguma dica da lógica disso aí ? Fazer outra query no server talvez ? ou MeuBean.load(Bem) que carregue os campos (no server)

To tentando otimizar ao máximo o processo

valeu

henrique.lima

Olá guigous. Não sei se entendi muito bem o seu problema, pois de uma maneira geral a collection retornada pelo seu método todos() deveria ser carregada normalmente. Me parece que vc deve ter filhos em um relacionamento 1 - * onde vc precise utilizar algo do tipo:

Bem b = suaList.get(0);
List filhos = b.getFilhos(); // aqui pode deve ser onde a encrenca ocorre!

Os filhos não são carregados por causa do FetchType ser lazy e/ou seu ejb ser stateless. Uma maneira de resolver seria usar FetchType.EAGER ou então um stateful EJB. As duas soluções devem ser implementadas com cautela.
Se você puder detalhar melhor o seu problema, prometo me esforçar para tentar ajuda-lo.

Desculpe a demora para responder.

Abraço.

B

henrique.lima:
Olá guigous, esse é um tipo de relacionamento muito simples e o JPA por default carrega seus objetos em modo lazy. Isso quer dizer que toda vez que você carregar uma Categoria as suas subcategorias não serão carregadas (o que é muito bom). Quando você precisar das subcategorias o método getCategorias() será chamado e apenas nesta hora a query (where idPai = x) será executada. As subcategorias das categorias contidas na collection obtidas pelo método getCategorias() também não terão suas subcategorias carregadas que é exatamente como eu faria com jdbc puro.
Desta forma, acredito que não haja mais nada que se possa otimizar neste tipo de relacionamento.

Espero ter sido claro na explicação.

Bons estudos! =)

Estou tendo um problema com o ManyToOne, ele ta carregando em modo eager, mesmo eu declarando: fetch = FetchType.LAZY!

Fiz um topico sobre este erro, deem uma olhada:

http://www.guj.com.br/posts/list/80214.java

Vlw!

guigouz

O meu problema era dentro do container, eu fazia a query no server, e precisava acessar as listas no cliente. Como na sua solução, que só funciona pro toplink, resolvi de um jeito que só funciona no hibernate, com

Coisa c = em.find(Coisa.class, 2L); Hibernate.initialize(c.getFilhos());

Ainda chego em algo melhor, mas por enquanto funciona

Criado 10 de janeiro de 2008
Ultima resposta 24 de jan. de 2008
Respostas 7
Participantes 3