Tentando entender auto relacionamento com hibernate

Olá pessoal venho por meio deste pedir ajuda para um entendimento, a situação é a seguinte tendo uma tree do primeFaces, simples em primeira vista tendo duas categorias pais, que não podem ser alteradas e os filhos, até ai tudo bem, o que não consigo entender é o relacionamento com o Hibernate, pensei em uma lógica sem o Hibernate e consegui concretizar o pensamento de forma com que eu entendesse mas essa entidade do livro está realmente complicado.

package financeiro.categoria;

import java.io.Serializable;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;

import org.hibernate.annotations.OnDelete;
import org.hibernate.annotations.OnDeleteAction;

import financeiro.usuario.Usuario;

@Entity
public class Categoria implements Serializable {

	@Id
	@GeneratedValue
	private Integer	      codigo;

	@ManyToOne
	@JoinColumn(name = "categoria_pai", nullable = true)
	@org.hibernate.annotations.ForeignKey(name = "fk_categoria_categoria")
	private Categoria	      pai;

	@ManyToOne
	@OnDelete(action=OnDeleteAction.CASCADE)
	@JoinColumn(name = "usuario")
	@org.hibernate.annotations.ForeignKey(name = "fk_categoria_usuario")
	private Usuario	      usuario;

	private String	         descricao;

	private int	            fator;

	@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
	@JoinColumn(name = "categoria_pai", updatable = false)
	@org.hibernate.annotations.OrderBy(clause = "descricao asc")
	private List<Categoria>	filhos;

	public Categoria() {
	}

	public Categoria(Categoria pai, Usuario usuario, String descricao, int fator) {
		this.pai = pai;
		this.usuario = usuario;
		this.descricao = descricao;
		this.fator = fator;
	}

	public Integer getCodigo() {
		return codigo;
	}

	public void setCodigo(Integer codigo) {
		this.codigo = codigo;
	}

	public String getDescricao() {
		return descricao;
	}

	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}

	public Categoria getPai() {
		return pai;
	}

	public void setPai(Categoria pai) {
		this.pai = pai;
	}

	public Usuario getUsuario() {
		return usuario;
	}

	public void setUsuario(Usuario usuario) {
		this.usuario = usuario;
	}

	public List<Categoria> getFilhos() {
		return filhos;
	}

	public void setFilhos(List<Categoria> filhos) {
		this.filhos = filhos;
	}

	public int getFator() {
		return fator;
	}

	public void setFator(int fator) {
		this.fator = fator;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((codigo == null) ? 0 : codigo.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Categoria other = (Categoria) obj;
		if (codigo == null) {
			if (other.codigo != null)
				return false;
		} else if (!codigo.equals(other.codigo))
			return false;
		return true;
	}
}

Alguém pode me explicar como funciona aquelas chaves externas, sei que existe o relacionamento dos filhos com os pais o que ajuda na hora da exclusão de uma categoria seus filhos sejam excluidos também, mas a existencia das outras chaves está dificil de entender. Se alguém se dispuser a explicar como elas se formam e onde são usadas.

Olá Mathe, tenho esse livro e fiz também esse projeto, então é assim mesmo. Nesse caso a classe categoria tem relacionamento com ela mesmo (auto-relação), isso porque uma categoria pode ter subcategorias relacionada a ela. Na primeira vez que a categoria for criada ela não terá pai, porém pode ter categorias associadas a ela.
A explicação que vc quer está na classe de regra de negocio onde mostra a relação do objeto Categoria pai.

Hmm, poisé essa parte ai eu entendi mas não fico claro o fato de por exemplo a foreign key do usuario aonde ela é usada e como?

[quote=satangoss]Olá Mathe, tenho esse livro e fiz também esse projeto, então é assim mesmo. Nesse caso a classe categoria tem relacionamento com ela mesmo (auto-relação), isso porque uma categoria pode ter subcategorias relacionada a ela. Na primeira vez que a categoria for criada ela não terá pai, porém pode ter categorias associadas a ela.
A explicação que vc quer está na classe de regra de negocio onde mostra a relação do objeto Categoria pai. [/quote]

Então a foreign key está no relacionamento:

@ManyToOne  
    @JoinColumn(name = "categoria_pai", nullable = true)  
    @org.hibernate.annotations.ForeignKey(name = "fk_categoria_categoria")  
    private Categoria         pai;  

pode ver que na tabela categoria tem um campo fk_categoria_categoria que referencia outra Categoria, que pode ser nula caso a categoria for a categoria do topo da hierarquia.

Hmm, isso é o que as manteria unida para no caso de exclusão o efeito cascade ser utilizado?

E nos outros casos como o da foreign key do usuario?

[quote=Mathe] Hmm, isso é o que as manteria unida para no caso de exclusão o efeito cascade ser utilizado?

E nos outros casos como o da foreign key do usuario?[/quote]
caso o usuário for excluido, serão excluidas as categorias relacionadas a ele como mostra aqui:

@ManyToOne @OnDelete(action=OnDeleteAction.CASCADE) @JoinColumn(name = "usuario") @org.hibernate.annotations.ForeignKey(name = "fk_categoria_usuario") private Usuario usuario;

Hmm, mas o que indica isso é o nome da foreign key por ser categoria_usuario?

Não, o que indica é que a classe categoria tem um objeto do tipo Usuario, ou muitas categorias para um usuario (ManyToOne) por isso toda vez que um usuário for excluido o hibernate verifica se o mesmo tem alguma dependencia de Categoria, justamente por causa da anotação @OnDelete(action=OnDeleteAction.CASCADE) no Objeto :
private Usuario usuario;

Hmm, agora ficou claro mas então para onde vão os filhos? na tabela?

nao vai ter nenhuma menção a filho na tabela Categoria e sim a coluna categoria_pai, ou seja toda categoria que tiver categoria_pai != null será categoria filha, as categorias que nao tiverem categoria_pai serão as categorias superiores no caso DESPESA e RECEITA.

Nossa velho talvez eu tenha dificultado mais do que o necessario, vc pode me dizer se o meu pensamento agora está correto, quando utilizado o atributo List filhos; está se utilizando uma nova instancia de categoria que tera esse ligado a um pai na categoria_pai e quando esse pai for chamado chamara todos os filhos, se não for isso vou queimar o livro -

De uma olhada no metodo montaDadosTree da classe CategoriaBean, lá ele monta a arvore por recursão.

Sim, realmente entendi o que acontece só foi estranho pra min o fato dos filhos ficarem somente ligados a um pai e quando esse pai for chamado aparecerem, e não ficarem “fisicamente” armazenados na tabela.

Na verdade é uma abordagem diferente, é só olhar na tabela categoria, a categoria que nao tiver categoria pai é filha.

pra ficar mais claro olhe o exemplo:

+--------+----------------------+-------+---------------+---------+
| codigo | descricao            | fator | categoria_pai | usuario |
+--------+----------------------+-------+---------------+---------+
|      1 | DESPESAS             |    -1 |          NULL |      10 |
|      2 | Moradia              |    -1 |             1 |      10 |
|      3 | Alimentação          |    -1 |             1 |      10 |
|      4 | Vestuario            |    -1 |             1 |      10 |
|      5 | Deslocamento         |    -1 |             1 |      10 |
|      6 | Cuidados Pessoais    |    -1 |             1 |      10 |
|      7 | Educação             |    -1 |             1 |      10 |
|      8 | Saúde                |    -1 |             1 |      10 |
|      9 | Lazer                |    -1 |             1 |      10 |
|     10 | Despesas Financeiras |    -1 |             1 |      10 |
|     11 | RECEITAS             |     1 |          NULL |      10 |
|     12 | Salário              |     1 |            11 |      10 |
|     13 | Restituições         |    -1 |            11 |      10 |
|     14 | Rendimento           |    -1 |            11 |      10 |
|     15 | Internet             |    -1 |             1 |      10 |
|     16 | Restaurante          |    -1 |             3 |      10 |
|     17 | Mercado              |    -1 |             3 |      10 |
|     33 | mensalidade          |    -1 |            15 |      10 |
+--------+----------------------+-------+---------------+---------+

Repare que RECEITAS E DESPESAS, nao tem pai, portanto são categorias de nó Rais (Topo da hierarquia),
Alimentação é categoria filha de DESPESAS e ao mesmo tempo pai da Categoria Restaurante e Mercado.
A categoria mensalidade é categoria do tipo folha da arvore pq é filha de Internet que é fila de DESPESAS mas não tem nenhuma categoria com ela como pai.

AA velho agora explico tudo um usuario usa mais de uma linha na tabela eu achei q cada usuario só teria direito a usar uma linha o que complicava tudo

Sim pelo próprio mapeamento ManyToOne de Categoria com Usuario;

@ManyToOne @OnDelete(action=OnDeleteAction.CASCADE) @JoinColumn(name = "usuario") @org.hibernate.annotations.ForeignKey(name = "fk_categoria_usuario") private Usuario usuario;