Conexão Java 2006 Mini-curso Hibernate Sérgio Luís Lopes Júnior Caelum – www.caelum.com.br Neste tutorial, você irá aprender a: - usar a ferramenta de ORM Hibernate - gerar as tabelas em um banco de dados qualquer a partir de suas classes de modelo - automatizar o sistema de adicionar, listar, remover e procurar objetos no banco - utilizar anotações para facilitar o mapeamento de classes para tabelas - criar classes de dao bem simples utilizando o hibernate - utilizar relacionamentos entre tabelas Introdução A utilização de código SQL dentro de uma aplicação agrava o problema da independência de plataforma de banco de dados e complica, em muito, o trabalho de mapeamento entre classes e banco de dados relacional. HIBERNATE O Hibernate abstrai o código SQL da nossa aplicação e permite escolher o tipo de banco de dados enquanto o programa está rodando, permitindo mudar sua base sem alterar nada no seu código Java. Além disso, ele permite criar suas tabelas do banco de dados de um jeito bem simples, não se fazendo necessário todo um design de tabelas antes de desenvolver seu projeto que pode ser muito bem utilizado em projetos pequenos. Já projetos grandes onde o plano de ação padrão tomado pelo Hibernate não satisfaz as necessidades da empresa (como o uso de select *, joins etc), ele possui dezenas de otimizações que podem ser feitas para atingir tal objetivo. O Hibernate é o framework de mapeamento objeto relacional mais utilizado em projetos Java. Seu criador Gavin King se juntou a Marc Flery do grupo JBoss, e agora o Hibernate faz parte do JBoss group, que por sua vez foi vendido para a corporação Red Hat. Anotações, EJB3 e Persistence API Fazer o mapeamento objeto relacional com o Hibernate era muito trabalhoso até a chegada das anotações do java5: você precisava criar arquivos XML de mapeamento, especificando as relações entre as entidades, nome de colunas, tabelas e muitos outros. Para não fazer isso tudo na mão o XDoclet ajudava muito, porém abria espaço para erros de digitação. Muitos plugins para as IDEs foram lançados, mas faltava algo para criar uma real facilidade. A JSR 220, que especifica o EJB3, separou a parte de Entity Beans na Persistence API (javax.persistence), onde define um EntityManager que faz o papel das antiga Homes, assim como uma EJB query language renovada e um conjunto de anotações para fazer o mapeamento objeto relacional. Essa API é desacoplada da API dos EJBs, fazendo com que a API não precise de um container Java EE para ser utilizado. Gavin King, como um dos líderes dessa JSR, foi implementando a Persistence API em cima do Hibernate enquanto a JSR ia sendo desenvolvida. Dia 1o de maio de 2006, com o lançamento da JSR, o Hibernate já possui implementado a grande maioria das necessidades especificadas na JSR. Por esse motivo uma grande parte de imports que vamos usar serão javax.persistence e não apenas org.hibernate. Ambiente usado Usaremos neste projeto a IDE livre Eclipse (www.eclipse.org) e Java 5. A versão do Hibernate que usamos é a 3.2.1. Como Banco de Dados, usaremos o Apache Derby, um banco escrito totalmente em Java e que pode ser distribuído junto com sua aplicação. Criando seu projeto Para criar seu projeto, é necessário baixar os arquivos .jar necessários para rodar o Hibernate e colocá-los no classpath do mesmo. O site oficial do hibernate é o www.hibernate.org e lá você pode baixar a última versão estável do mesmo na seção Download. Após descompactar esse arquivo, basta copiar todos os jars para o nosso projeto. HIBERNATE ANNOTATIONS Ainda falta baixar as classes correspondentes ao HibernateAnnotations, que iremos utilizar para gerar o mapeamento entre as classes Java e o banco de dados. Eles são encontrados também no site do hibernate e contem outros jars que devemos colocar no nosso projeto. Exercício 1) Abra o eclipse e selecione o workspace padrão. 2) Crie um novo projeto Java: File -> New -> Project -> Java Project Clique em Next e na próxima tela marque: Project name: hibernate Create separate source and output folders Clique em Finish. 3) Crie uma pasta lib dentro do seu projeto: Clique com o botão direito no nome do projeto e vá em New -> Folder e crie uma pasta chamada lib 4) Copie os jars do hibernate, hibernate-annotations e do derby para o seu diretório lib: - Clique com o botão direito na pasta lib e clique em Import - Selecione Archive File (na aba General), clique em Next - Selecione o arquivo hibernate-jar.zip - Clique em Select All e depois em Finish 5) Precisamos adicionar os jars no class path. Expanda a pasta lib e selecione todos os jars. Clique com o botão direito e vá em: Build Path -> Add to build path... Propriedades do banco O arquivo hibernate.properties é um arquivo de propriedades para configurarmos as opções do hibernate. Há centenas de configurações possíveis, como avançados controles de cache, transações e outros. Para nosso sistema, precisamos de quatro linhas básicas, que configuram o banco, o driver, o usuário e senha, que já conhecemos, e uma linha adicional, que diz para o hibernate qual dialeto de SQL ele deve “falar”: o dialeto do hsqldb. Além disso, vamos colocar uma opção para que o hibernate exiba os comandos SQL que ele executar. O arquivo final fica da seguinte maneira: hibernate.dialect org.hibernate.dialect.DerbyDialect hibernate.connection.driver_class org.apache.derby.jdbc.EmbeddedDriver hibernate.connection.url jdbc:derby:bancodedados;create=true hibernate.connection.username hibernate.connection.password hibernate.show_sql true hibernate.format_sql true Ele deve ser colocado no classpath de sua aplicação. No nosso caso, basta colocálo no diretório src. Além disso, vamos copiar também um arquivo chamado log4j.properties para configurar o log do hibernate; usamos o arquivo padrão que vem junto com o hibernate. Exercícios 1-) Para economizarmos tempo, copie o arquivo properties citado acima para dentro do diretório src de nosso projeto: - Clique com o botão direito na pasta src e clique em Import Selecione Archive File (na aba General), clique em Next Selecione o arquivo hibernate-properties.zip Clique em Select All e depois em Finish 2) Abra o arquivo hibernate.properties copiado e veja seu conteúdo. Em casa: Baixando os arquivos necessários Vá em www.hibernate.org e baixe o hibernate e o hibernate-annotations. Descompacte os dois arquivos baixados. Os jars necessários estão na raiz de cada pasta criada e dentro de suas respectivas pastas lib. Os arquivos hibernate.properties e o log4j.properties estão na pasta src dentro da pasta onde você descompactou o hibernate. O Derby você baixa em derby.apache.org. Copie o arquivo derby.jar de dentro da pasta lib dele. Modelo Como exemplo, vamos criar um sistema de controle de Produtos para uma loja. Iniciamos nosso trabalho modelando uma classe para representar essa entidade Produto do sistema. A classe produto será uma classe JavaBean simples, como atributos privados e métodos de acesso get e set. package br.com.caelum.hibernate; public class Produto { private private private private Long id; String nome; String descricao; double preco; // gets e sets aqui } Além de termos a classe modelando a entidade Produto no nosso programa Java, precisamos de alguma forma de persistí-lo no banco de dados. Normalmente, criaríamos a tabela em SQL (create table ...) para guardar os produtos. Vamos usar o hibernate para isso. Mas antes precisamos configurar a classe para o hibernate saber usá-la. Configurando a classe/tabela Produto Antigamente (até a versão 2 do hibernate) a configuração era feito somente através de arquivos xml, o que era bem chato, e utilizávamos de uma ferramenta chamada Xdoclet que criava tais xmls através de comentários especiais. Hoje em dia o Xdoclet foi substituido pelas anotações do Java 5. A grande diferença entre os dois – anotações e comentários – é que as anotações são bem estruturadas, seguem um padrão e são mantidas em tempo de execução, enquanto os comentários são perdidos em tempo de compilação, que impossibilita descobrir em tempo de execução o que havia sido comentado. O código a seguir coloca nossa classe na tabela "Produto" e seta algumas propriedades e o id (chave primária). Atenção: toda classe que vai trabalhar com o Hibernate precisa de um (ou mais) campo(s) que será(ão) a chave primária (composta ou não). package br.com.caelum.hibernate; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Produto { @Id @GeneratedValue private Long id; private String nome; private String descricao; private double preco; // gets e sets aqui } A especificação do EJB3 define tais anotações e possui diversas opções que podemos utilizar em nosso projeto. Sempre que possuir alguma dúvida em relação as anotações lembre-se de ler a tal especificação. O API comum das anotações usadas pelo Hibernate e o EJB3 é o Java Persistence API, no pacote javax.persistence. Exercícios 1-) Crie uma classe chamada Produto no pacote br.com.caelum.hibernate: File -> New -> Class Coloque o nome do pacote como br.com.caelum.hibernate Coloque o nome da classe como Produto 2) Escreva a classe como vimos acima: package br.com.caelum.hibernate; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class Produto { @Id @GeneratedValue private Long id; private String nome; private String descricao; private double preco; } 3-) Gere os getters e setters usando o eclipse: - Selecione os atributos da classe e vá em: - Source -> Generate Getters and Setters - Clique em Select All e depois em Finish. Configurando Qualquer programa que use o hibernate precisa, antes de qualquer coisa, cofigurá-lo. Fazemos isso através da classe AnnotationConfiguration. // cria a configuração do hibernate AnnotationConfiguration conf = new AnnotationConfiguration(); A partir daí podemos adicionar quantas classes desejarmos a nossa configuração. // adiciona a classe Produto conf.addAnnotatedClass(Produto.class); O Hibernate requer que descrevamos como a classe se relaciona com as tabelas no banco de dados e fizemos isso através das anotações do Hibernate. Ao adicionarmos a classe Produto na nossa configuração, o Hibernate lê essas configurações. XML Essa configuração poderia ser feita através de um arquivo xml chamado hibernate.cfg.xml, e em vez de utilizarmos as chamadas ao método addAnnotatedClass iríamos executar o método configure() . Criando as tabelas SCHEMA EXPORT Vamos criar um programa que gera as tabelas do banco. Dada uma configuração, a classe SchemaExport é capaz de gerar o código DDL de criação de tabelas em determinado banco (no nosso caso, o hsqldb). Para exportar tais tabelas, fazemos uso do método create que recebe dois argumentos booleanos. O primeiro diz se desejamos ver o código DDL e o segundo se desejamos executá-lo realmente. // gera a tabela no banco new SchemaExport(conf).create(true, true); O código final da classe GeraTabelas, incluindo a configuração, você vê no exercício abaixo: Exercícios 1-) Crie a classe GeraTabelas: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como GeraTabelas 2) Escreva o método main e, dentro dele, faça a configuração do hibernate e depois gere a tabela, da seguinte forma: package br.com.caelum.hibernate; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.tool.hbm2ddl.SchemaExport; public class GeraTabelas { public static void main(String[] args) { // cria a configuração do hibernate AnnotationConfiguration conf = new AnnotationConfiguration(); // adiciona a classe Produto conf.addAnnotatedClass(Produto.class); } // gera a tabela no banco new SchemaExport(conf).create(true, true); } 3-) Crie suas tabelas executando o código anterior. Clique com o botão direito no nome da classe, vá em: Run As... -> Java Application O Hibernate deve reclamar que não configuramos nenhum arquivo de log para ele (dois warnings, ignore-os) e mostrar o código SQL que ele executou no banco. Sessões O hibernate nãp te dá acesso direto às conexões com o banco de dados. Ele trabalha com a idéia de sessões de uso do banco. Para efetuar alguma operação no banco (INSERT, SELECT, ...) você precisa, antes, obter uma sessão do hibernate. O Hibernate provê uma fábrica de sessões, onde você pode obter uma sessão quando quiser. Na configuração do hibernate, chamamos o método buildSessionFactory() que nos devolve uma SessionFactory. SessionFactory factory = conf.buildSessionFactory(); factory.close(); O Hibernate gera sessões através dessa factory. Essas sessões são responsáveis por se conectar ao banco de dados e persistir e buscar objetos no mesmo. A maneira mais simples de abrir uma nova sessão e fechar a mesma é: Session session = factory.openSession(); session.close(); Hibernate Session Factory Sempre que formos trabalhar com o Hibernate, teremos que configurá-lo, obter a fábrica de sessões e depois obter uma sessão. Ao invés de espalhar esse código por todo nosso sistema, vamos encapsulá-lo em algum lugar, em alguma classe. Vamos criar uma classe HibernateFactory que cuidará de: - instanciar a SessionFactory do Hibernate; - nos dar Sessions do hibernate quando precisarmos. package br.com.caelum.hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public class HibernateFactory { private static SessionFactory factory; static { AnnotationConfiguration cfg = new AnnotationConfiguration(); cfg.addAnnotatedClass(Produto.class); factory = cfg.buildSessionFactory(); } public Session getSession() { return factory.openSession(); } } O bloco estático das linhas 4 a 8 cuidará de configurar o Hibernate e pegar uma SessionFactory. Lembre-se que o bloco estático é executado automaticamente quando a classe é carregada pelo Class Loader e só neste momento; ele não será executado outras vezes, como quando você der new HibernateFactory(). Ou seja, a configuração do Hibernate será feita uma única vez em todo seu programa. O método getSession SessionFactory do Hibernate. devolverá uma Session, conseguida através do Exercícios 1-) Crie a classe HibernateFactory: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como HibernateFactory 2-) Implemente a classe HibernateFactory como está na seção anterior. No momento de importar Session lembre-se que não é a classic! package br.com.caelum.hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public class HibernateFactory { private static SessionFactory factory; static { AnnotationConfiguration cfg = new AnnotationConfiguration(); cfg.addAnnotatedClass(Produto.class); factory = cfg.buildSessionFactory(); } public Session getSession() { return factory.openSession(); } } Erros comuns 1-) O erro mais comum ao criar a classe HibernateFactory está em importar org.hibernate.classic.Session ao invés de org.hibernate.Session. Uma vez que o método openSession devolve uma Session que não é do tipo classic, o Eclipse pede para fazer um casting. Não faça o casting! Remova o seu import e adicione o import correto. Salvando novos objetos Depois de todos esses passos de configuração e etc, vamos agora efetivamente trabalhar com o banco de dados, inserindo um novo Produto. Através de um objeto do tipo Session é possível gravar novos objetos do tipo Produto no banco. Para tanto basta criar o objeto e depois utilizar o método save. Mas como usamos um banco transacional, precisamos usar Transações. Isso é feito com o beginTransaction() na session e depois com o commit() para comitar. // cria um produto p e o popula Produto p = new Produto(); p.setNome("Nome aqui"); p.setDescricao("Descrição aqui"); p.setPreco(100.50); // obtém uma sessão Session session = new HibernateFactory().getSession(); // Inicia uma transação Transaction transaction = session.beginTransaction(); // salva o produto session.save(p); // Comita a transação transaction.commit(); // veja o id gerado System.out.println("ID do produto: " + p.getId()); session.close(); Exercícios 1-) Crie a classe AdicionaProduto: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como AdicionaProduto 2) Implemente o método main para adicionar um produto no banco de dados, conforme vimos acima: package br.com.caelum.hibernate; import org.hibernate.Session; public class AdicionaProduto { public static void main(String[] args) { // cria um produto p e o popula Produto p = new Produto(); p.setNome("Nome aqui"); p.setDescricao("Descrição aqui"); p.setPreco(100.50); // obtém uma sessão Session session = new HibernateFactory().getSession(); // Inicia uma transação Transaction transaction = session.beginTransaction(); // salva o produto session.save(p); // Comita a transação transaction.commit(); // veja o id gerado System.out.println("ID do produto: " + p.getId()); session.close(); } } 3-) Adicione um produto executando o código anterior. Clique com o botão direito no nome da classe, vá em: Run As... -> Java Application O Hibernate deve mostrar o código SQL que ele executou no banco. 4-) (opcional) Adicione outros produtos no banco (altere os dados no programa e rode-o outras vezes). Buscando pelo id Para buscar um objeto pela chave primária, no caso o seu id, utilizamos o método load, conforme o exemplo a seguir: // procura o produto de id 2 Produto p = (Produto) session.load(Produto.class, 1L); // imprime o nome do Produto System.out.println(p.getNome()); Exercício 1) Crie uma classe BuscaProduto: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como BuscaProduto 2) Crie o método main e busque um Produto pelo id: package br.com.caelum.hibernate; import org.hibernate.Session; public class BuscaProduto { public static void main(String[] args) { // obtém uma sessão Session session = new HibernateFactory().getSession(); // procura o produto de id 2 Produto p = (Produto) session.load(Produto.class, 1L); // imprime o nome do Produto System.out.println(p.getNome()); } session.close(); } 3) Rode a classe e observe o SQL gerado pelo Hibernate: - Clique com o botão direito na classe e vá em: Run as -> Java Application Listando todos os produtos Vamos listar todos os produtos existentes no banco de dados. Para isso, vamos usar o método createCriteria de Session que cria um Criteria. Através de um Criteria, temos acesso a diversas operações no banco de dados; uma delas é listar tudo com o método list(). Dada uma Session, para listarmos todos os produtos fazemos: // obtém lista de todos os produtos List<Produto> produtos = session.createCriteria(Produto.class).list(); Depois, basta percorrer essa lista e imprimir todos os nomes, por exemplo: // percorre os produtos e imprime o nome de cada um for (Produto p : produtos) { System.out.println(p.getNome()); } Exercício 1) Crie uma classe ListaProdutos: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como ListaProdutos 2) Crie o método main e busque todos os produtos do banco: package br.com.caelum.hibernate; import java.util.List; import org.hibernate.Session; public class ListaProdutos { public static void main(String[] args) { Session session = new HibernateFactory().getSession(); // obtém lista de todos os produtos List<Produto> produtos = session.createCriteria(Produto.class).list(); // percorre os produtos e imprime o nome de cada um for (Produto p : produtos) { System.out.println(p.getNome()); } } session.close(); } 3) Rode a classe e observe o SQL gerado pelo Hibernate: - Clique com o botão direito na classe e vá em: Run as -> Java Application Buscando com WHERE em HQL No exemplo anterior, usamos a chamada Criteria API, uma API do hibernate que permite que busquemos no banco através da chamada de métodos, sem escrever SQL. Embora a Criteria API seja muito poderosa e seja virtualmente possível fazer tudo com ela, há ainda uma segunda possibilidade que eventualmente pode ser mais simples e direta: o uso de uma linguagem de queries como o SQL. O problema de usarmos SQL diretamente, porém, é que, como vimos, ele é dependente de banco de dados. Para solucionar isso, o hibernate criou uma linguagem de queries própria, a HQL, ou Hibernate Query Language. A grande vantagem é que ela é extremamente semelhante ao SQL que estamos acostumados mas é portável. Ou seja, na hora de executar realmente no banco, o hibernate traduz o comando HQL para o SQL específico do banco em questão. Para buscarmos os produtos usando alguma cláusula WHERE, por exemplo buscar apenas os produtos de id maior que 1, podemos fazer: // obtém lista dos produtos com id maior que 1 List<Produto> produtos = session.createQuery("from Produto where id > 1").list(); Exercício 1) Crie uma classe ListaProdutosHQL: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como ListaProdutosHQL 2) Crie o método main e busque no banco os produtos com id maior que 1 usando HQL: package br.com.caelum.hibernate; import java.util.List; import org.hibernate.Session; public class ListaProdutosHQL { public static void main(String[] args) { Session session = new HibernateFactory().getSession(); // obtém lista dos produtos com id maior que 1 List<Produto> produtos = session.createQuery("from Produto where id > 1").list(); // imprime nome e id de cada produto for (Produto p : produtos) { System.out.println(p.getId() + " - " + p.getNome()); } session.close(); } } 3) Rode a classe e observe o SQL gerado pelo Hibernate: - Clique com o botão direito na classe e vá em: Run as -> Java Application Remoção (DELETE) Assim como inserir usando o Hibernate é muito simples, remover também é bastante simples. Basta chamarmos o método delete() na session passando o objeto que queremos remover. No nosso caso, passamos o Produto a ser removido. Cuidado que, assim como em um DELETE normal, sempre que vamos remover alguém precisamos dizer qual é o id dele. Então nosso Produto precisa estar com o id setado. Não se esqueça também que precisamos de uma Transaction para executar algo no banco. O código final fica: // cria um Produto e seta seu id Produto p = new Produto(); p.setId(0L); // lembre que id é um Long! // obtém sessão Session session = new HibernateFactory().getSession(); // abre transação Transaction transaction = session.beginTransaction(); // apaga o produto session.delete(p); // comita a transação transaction.commit(); session.close(); Exercício 1) Crie uma classe RemoveProduto: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como RemoveProduto 2) Crie o método main e remova o produto de id 0: package br.com.caelum.hibernate; import org.hibernate.Session; import org.hibernate.Transaction; public class RemoveProduto { public static void main(String[] args) { // cria um Produto e seta seu id Produto p = new Produto(); p.setId(0L); // lembre que id é um Long! // obtém sessão Session session = new HibernateFactory().getSession(); // abre transação Transaction transaction = session.beginTransaction(); // apaga o produto session.delete(p); // comita a transação transaction.commit(); session.close(); } } 3) Rode a classe e observe o SQL gerado pelo Hibernate: - Clique com o botão direito na classe e vá em: Run as -> Java Application Uma categoria E se cada Produto na nossa loja tivesse uma Categoria? Ou seja, um relacionamento muitos para um? Com o hibernate, é muito fácil criar relacionamentos entre entidades. Começamos criando uma classe para representar uma Categoria com, por exemplo, id e nome. Como ela é uma classe a ser gerenciada pelo Hibernate, marcamos as anotações apropriadas. A classe final ficará assim: package br.com.caelum.hibernate; import import import import javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; @Entity public class Categoria { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String nome; } // gets e sets Repare que ela é muito parecida com a classe Produto. Exercício 1-) Crie uma classe chamada Categoria no pacote br.com.caelum.hibernate: File -> New -> Class Coloque o nome do pacote como br.com.caelum.hibernate Coloque o nome da classe como Categoria 2) Escreva a classe como vimos acima: package br.com.caelum.hibernate; import import import import javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; @Entity public class Categoria { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; } private String nome; 3-) Gere os getters e setters usando o eclipse: - Selecione os atributos da classe e vá em: - Source -> Generate Getters and Setters - Clique em Select All e depois em Finish. Produto tem uma Categoria No nosso sistema, cada Produto possui uma Categoria. Em Java, representamos isso com um atributo na classe Produto. Ou seja, colocamos um atributo do tipo Categoria dentro de Produto (e geramos get e set para ele). Mas precisamos, além disso, instruir o hibernate de que estamos fazendo na verdade um relacionamento Muitos para Um no banco de dados também. Fazemos isso com uma anotação simples, a @ManyToOne no atributo categoria. A classe Produto, com as modificações fica: package br.com.caelum.hibernate; import import import import import javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; javax.persistence.ManyToOne; @Entity public class Produto { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String nome; private String descricao; private double preco; @ManyToOne private Categoria categoria; } // gets e sets aqui Exercício 1) Abra sua classe Produto. Acrescente um atributo do tipo Categoria chamado categoria. Marque esse atributo com a anotação @ManyToOne: @ManyToOne private Categoria categoria; 2) Gere get e set para este novo atributo. Selecione o atributo que você acabou de criar e vá em: - Source -> Generate Getters And Setters - Cliquem em Select All e depois em Finish 3) O código final de sua classe Produto deve estar assim: package br.com.caelum.hibernate; import import import import import javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; javax.persistence.ManyToOne; @Entity public class Produto { @Id @GeneratedValue(strategy=GenerationType.IDENTITY) private Long id; private String nome; private String descricao; private double preco; @ManyToOne private Categoria categoria; } // gets e sets aqui Recriando as tabelas Agora que adicionamos um campo novo na tabela Produto e criamos uma nova classe a ser persistida pelo hibernate, precisamos recriar as tabelas. Vamos usar a classe GeraTabelas que fizemos anteriormente. Precisamos apenas indicar, na nossa configuração, que agora temos, além de Produto, também Categoria: // cria a configuração do hibernate AnnotationConfiguration conf = new AnnotationConfiguration(); // adiciona as entidades conf.addAnnotatedClass(Produto.class); conf.addAnnotatedClass(Categoria.class); // gera o esquema do banco new SchemaExport(conf).create(true, true); Exercício 1) Abra sua classe GeraTabelas. Logo depois de adicionar o Produto à configuração, adicione a Categoria também. O código final de sua classe deve ser: package br.com.caelum.hibernate; import org.hibernate.cfg.AnnotationConfiguration; import org.hibernate.tool.hbm2ddl.SchemaExport; public class GeraTabelas { public static void main(String[] args) { // cria a configuração do hibernate AnnotationConfiguration conf = new AnnotationConfiguration(); // adiciona as entidades conf.addAnnotatedClass(Produto.class); conf.addAnnotatedClass(Categoria.class); // gera o esquema do banco new SchemaExport(conf).create(true, true); } } 2) Rode a classe e observe o SQL gerado pelo Hibernate: - Clique com o botão direito na classe e vá em: Run as -> Java Application Adicionar um produto com categoria Que tal adicionar um Produto que possua uma Categoria? Podemos fazer isso facilmente com o Hibernate, usando o método save na session(). Mas antes, lembre de alterar a nossa classe HibernateFactory para também incluir a configuração da classe Categoria, assim como fizemos no GeraTabelas. A classe HibernateFactory no final ficará assim: package br.com.caelum.hibernate; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.cfg.AnnotationConfiguration; public class HibernateFactory { private static SessionFactory factory; static { AnnotationConfiguration cfg = new AnnotationConfiguration(); cfg.addAnnotatedClass(Produto.class); cfg.addAnnotatedClass(Categoria.class); factory = cfg.buildSessionFactory(); } public Session getSession() { return factory.openSession(); } } Para adicionarmos um Produto com alguma Categoria, primeiro criamos uma Categoria: Categoria c = new Categoria(); c.setNome("Uma categoria"); E criamos um Produto: Produto p = new Produto(); p.setNome("Nome do produto"); p.setDescricao("Sua descricao"); p.setPreco(123.00); Como dizer agora que o Produto p é da Catgoria c? Basta chamar o setCategoria! p.setCategoria(c); Apenas isso! Agora basta obtermos a session, abrirmos uma transação, salvarmos a categoria e depois salvarmos o produto. Só isso! Session session = new HibernateFactory().getSession(); Transaction transaction = session.beginTransaction(); session.save(c); session.save(p); transaction.commit(); session.close(); Exercício 1) Crie uma classe AdicionaProdutoComCategoria: Vá em File -> New -> Class Coloque o pacote como br.com.caelum.hibernate E o nome da classe como AdicionaProdutoComCategoria 2) Crie o método main e implemente o código visto acima: package br.com.caelum.hibernate; import org.hibernate.Session; import org.hibernate.Transaction; public class AdicionaProdutoComCategoria { public static void main(String[] args) { Categoria c = new Categoria(); c.setNome("Uma categoria"); Produto p = new Produto(); p.setNome("Nome do produto"); p.setDescricao("Sua descricao"); p.setPreco(123.00); p.setCategoria(c); Session session = new HibernateFactory().getSession(); Transaction transaction = session.beginTransaction(); session.save(c); session.save(p); transaction.commit(); session.close(); } } 3) Rode a classe e observe o SQL gerado pelo Hibernate: - Clique com o botão direito na classe e vá em: Run as -> Java Application Adicional: lista os produtos e sua categoria Como fazer agora a listagem de produtos listar também o nome da categoria do produto? Joins? Com hibernate, basta acessarmos no produto, o getCategoria() e depois o getNome(): p.getCategoria().getNome() Sem complicação! O Hibernate se vira com o join que precisar fazer. Na nossa classe ListaProdutos bastaria alterar o for para imprimir o nome da categoria também, só isso! Veja o código completo do ListaProdutos após a modificação (note que a única modificação necessária está em amarelo): package br.com.caelum.hibernate; import java.util.List; import org.hibernate.Session; public class ListaProdutos { public static void main(String[] args) { Session session = new HibernateFactory().getSession(); // obtém lista de todos os produtos List<Produto> produtos = session.createCriteria(Produto.class).list(); // percorre os produtos e imprime o nome de cada um for (Produto p : produtos) { System.out.println( p.getNome() + ", categoria: " + p.getCategoria().getNome()); } session.close(); } } Adicional: Exercícios para o preguiçoso 1-) Teste um programa que faz somente o seguinte: busca um produto por id. O código deve somente buscar o produto e não imprimir nada! Qual o resultado? Session session = new HibernateFactory().getSession(); Produto encontrado = (Produto) session.load(Produto.class,new Long(1)); 2-) Tente imprimir o nome do produto do teste anterior, o que acontece? Session session = new HibernateFactory().getSession(); Produto encontrado = (Produto) session.load(Produto.class,new Long(1)); System.out.println(encontrado.getNome()); 3-) Antes de imprimir o nome do produto, tente imprmir uma mensagem qualquer, do tipo: “O select já foi feito”. E agora? Como isso é possível? Session session = new HibernateFactory().getSession(); Produto encontrado = (Produto) session.load(Produto.class,new Long(1)); System.out.println(“O select já foi feito”); System.out.println(encontrado.getNome()); Então, onde está o código do select? Ele deve estar no método getNome(), certo? 4-) Imprima o nome da classe do objeto referenciado pela variável encontrado: Session session = new HibernateFactory().getSession(); Produto encontrado = (Produto) session.load(Produto.class,new Long(1)); System.out.println(“O select já foi feito”); System.out.println(encontrado.getNome()); System.out.println(encontrado.getClass().getName()); O Hibernate retorna um objeto cujo tipo estende Produto: ele não deixa de ser um Produto mas não é somente um Produto. O método getNome() foi sobrescrito nessa classe para fazer a busca na primeira vez que é chamado, economizando tempo de processamento. É claro que para fazer o fine-tuning do Hibernate é interessante conhecer muito mais a fundo o que o Hibernate faz e como ele faz isso. Adicional: Fazer paginação Usando a classe ListaProdutos temos a lista com todos os produtos no banco de dados. Em um sistema com listagens longas, normalmente apresentamos a lista por páginas. Para implementar paginação, precisamos determinar que a listagem deve começar em um determinado ponto e ser de um determinado tamanho. Usando o Criteria, como anteriormente, isso é bastante simples. Podemos fazer o seguinte: List<Produto> produtos = session.createCriteria(Produto.class) .setFirstResult(0).setMaxResults(2).list(); O método setMaxResults determina o tamanho da lista (resultados por página) e o método setFirstResult determina em que ponto a listagem deve ter início. Por fim, basta chamar o método list() e a listagem devolvida será apenas daquela página! No nosso caso estamos listando a partir do primeiro mas apenas 2 resultados. Adicional: Atualiza (UPDATE) Assim como save e delete, podemos fazer o update chamando o método update() na session e passando o produto a ser atualizado. Lembre que, para um update no banco, além dos dados normais, precisamos do id. O código final, com controle de transações fica: package br.com.caelum.hibernate; import org.hibernate.Session; import org.hibernate.Transaction; public class AtualizaProduto { public static void main(String[] args) { // cria um Produto Produto p = new Produto(); p.setId(1L); // lembre que id é um Long! p.setNome("Novo nome"); p.setDescricao("Nova descricao"); p.setPreco(78.50); // obtém sessão Session session = new HibernateFactory().getSession(); // abre transação Transaction transaction = session.beginTransaction(); // atualiza o produto session.update(p); // comita a transação transaction.commit(); session.close(); } }