17 de fevereiro de 2011

JPA 2.0: Criando tabelas - Parte 1

Para trabalhar com o JPA, vamos usar a implementação padrão para a versão 2.0, o EclipseLink com o banco de dados MySQL. Antes de começar vamos preparar nosso banco de dados para receber nossas tabelas que serão geradas pelo JPA, então no banco de dados crie uma database com o nome de 5tads.
Depois de criar um projeto Java EE6 (Veja aqui), vamos começar a utilizar o JPA.
Inicialmente criei um projeto com o nome de TesteJPA, segue a estrutura final do projeto:


1. Habilitando o JPA
Para habilitar o JPA no projeto, basta clicar no projeto com o botão direito e ir em Novo - Outro... Escolha o tipo de arquivo da categoria Persistence do tipo Unidade de Persistência e vá para o próximo passo...



No próximo passo, é a hora de definir o provedor e o banco de dados:
O nome da unidade de persistência é gerada por padrão com o nome do projeto + PU (Persistence Unit)
O provedor de persistência é o EclipseLink(JPA 2.0)
E no combo de Fonte de dados, selecione a opção, Nova fonte de dados...


Ao selecionar a opção de criar uma nova fonte de dados, abrirá a seguinte janela, basta seguir a configuração das imagens:


Ao selecionar uma Nova conexão com o banco de dados... irá abrir a próxima janela, basta colocar os dados referentes aos seu banco de dados, lembre-se de selecionar o driver para o MySQL:


Ao clicar em OK ele fechará a janela acima, e na janela anterior a esta, aparecerá setado no combo a conexão que foi acabada de criar (imagem abaixo).


Basta dar um OK na janela acima, e voltamos a janela principal, onde estará setada a nossa nova fonte de dados, e clique em Finalizar.



2. Criando uma classe de entidade
Para organizar meu projeto, criei um pacote model dentro do pacote de códigos fonte. Para criar uma entidade, pode-se criar uma classe normal e fazer as anotações necessárias, porém o NetBeans, já dá a opção de criar uma entidade pré-anotada, para isso, clique com o botão direito no pacote model - Novo - Outro... Escolha a categoria Persistence e o tipo de arquivo Classe da entidade, como na imagem abaixo:


Ao clicar em Próximo defina um nome para a classe, nesse caso será Cliente, e deixe os valores padrões e finalize.


Temos uma entidade pré-anotada criada, ela já vem com o atributo id anotado porém, altere o GenerationType.AUTO para GenerationType.IDENTITY... Quando este valor está como AUTO, o JPA irá identificar qual será o banco para qual as tabelas serão geradas e a partir disso decide se auto-incremento será SEQUENCE ou IDENTITY, no caso do MySQL é o IDENTITY, mas deixando AUTO, o JPA acaba criando uma tabela SEQUENCE e tals, o que não é o padrão do MySQL, por isso é necessário alterar.
Para minha entidade Cliente adicionei um atributo nome, deixando minha classe na seguinte forma:
@Entity
public class Cliente implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(length=100)
    private String nome;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    @Override
    public int hashCode() {
        int hash = 0;
        hash += (id != null ? id.hashCode() : 0);
        return hash;
    }

    @Override
    public boolean equals(Object object) {
        // TODO: Warning - this method won't work in the case the id fields are not set
        if (!(object instanceof Cliente)) {
            return false;
        }
        Cliente other = (Cliente) object;
        if ((this.id == null && other.id != null) || (this.id != null && !this.id.equals(other.id))) {
            return false;
        }
        return true;
    }

    @Override
    public String toString() {
        return "model.Cliente[id=" + id + "]";
    }

}
IMPORTANTE: É necessário adicionar todas as entidades criadas no persistence.xml, senão elas não serão gerenciadas pelo JPA.
Meu persistence.xml fica do seguinte formato:


em xml:
<persistence version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/persistence" xsi:schemalocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
    <persistence-unit name="TesteJPAPU" transaction-type="JTA">
        <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
        <jta-data-source>5tads</jta-data-source>
        <class>model.Cliente</class>
        <exclude-unlisted-classes>true</exclude-unlisted-classes>
        <properties>
            <property name="eclipselink.ddl-generation" value="create-tables" />
        </properties>
    </persistence-unit>
</persistence>

Feito isso nosso JPA já estará funcionando, mas ainda não criará nossa tabela Cliente de acordo com a nossa entidade, o que falta? É necessário fazer alguma operação no banco de dados via JPA, aí sim ele criará nossas tabelas!

3. Criando nosso DAO de acesso ao banco de dados
Inicialmente precisamos criar um pacote chamado dao, depois disso vamos criar uma nova classe java com o nome de ClienteDAO.java:
Nessa classe teremos um EntityManagerFactory e um EntityManager que é responsável por conectar ao JPA, veja que o EntityManagerFactory é criado pelo nossa unidade de persistência: TesteJPAPU. E nosso EntityManager é criado no construtor da classe pelo EntityManagerFactory: para entender direito esse parágrafo é necessária a leitura desse artigo do DevMedia: Veja aqui .
E a partir disso, criamos um método para listar os dados da tabela cliente (não irei entrar em detalhes sobre o método agora), no final nosso DAO ficará assim:
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import model.Cliente;

/**
 *
 * @author andii
 */
public class ClienteDAO {

    private EntityManagerFactory factory = Persistence.createEntityManagerFactory("TesteJPAPU");
    private EntityManager em;

    public ClienteDAO() {
        em = factory.createEntityManager();
    }


    public List< Cliente > listar() {
        List< Cliente > clientes = null;
        try {
            em.getTransaction().begin();
            Query query = em.createQuery("Select c from Cliente c");
            clientes = query.getResultList();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            em.close();
        }
        return clientes;
    }
}


4. Bean que acessará o ClienteDAO.java
Quase finalizando nosso projeto, vamos criar um pacote chamado controle para guardarmos os beans, e nele vamos criar uma classe java com o nome de ClienteBean.java, que terá os seguintes dados:
import dao.ClienteDAO;
import java.util.List;
import javax.inject.Named;
import javax.faces.bean.RequestScoped;
import model.Cliente;

/**
 *
 * @author andii
 */
@Named(value="clienteBean")
@RequestScoped
public class ClienteBean {

    private ClienteDAO clienteDAO = new ClienteDAO();
    private List< Cliente > clientes;

    public List< Cliente > getClientes() {
        clientes = clienteDAO.listar();
        return clientes;
    }

    public void setClientes(List< Cliente > clientes) {
        this.clientes = clientes;
    }

Nesse bean apenas iremos retornar uma lista de clientes para o JSF...

5. Página index.xhtml, acessando o bean
Falando em JSF, lembra que quando criamos o projeto o NetBeans já cria um arquivo index.xhtml? Pois bem, agora vamos alterar ele mesmo para mostrar os clientes trazidos pelo bean, que foi trazido pelo dao, que enfim foi trazido do banco de dados pelo JPA. Ficando assim nosso index.xhtml, o código abaixo é apenas a parte do h:body da página, o restante continua normal:
<h:body>
        <h:datatable value="#{clienteBean.clientes}" var="cliente">
            <h:column>
                <f:facet name="header"><h:outputtext value="ID" /></f:facet>
                <h:outputtext value="#{cliente.id}" />
            </h:column>
            <h:column>
                <f:facet name="header"><h:outputtext value="NOME" /></f:facet>
                <h:outputtext value="#{cliente.nome}" />
            </h:column>
        </h:datatable>
    </h:body>

Obs: inseri alguns clientes diretamente no banco para melhorar a visualização do resultado do nosso projeto, finalizando, o resultado no navegador é o seguinte:


Por hoje ficamos por aqui! ;)

8 comentários:

  1. Oi,
    eu estou fazendo um projeto para cadastrar produtos usando JPA e JSF, eu tentei fazer mais deu um erro (não aparece os dados do banco na dataTable).
    Dai eu segui o seu tutorial para ver se eu conseguia,mas não. Tentei usar List e DataModel, mas também não apareceu nada.
    acho que ele está retornando uma lista vazia.
    A conexão está ok, pois todas as outras funcões estão funcionando (adicionar,atualizar e remover).
    tentei varias formas de sql:
    List produto = em.createQuery("produtos").getResultList();
    "produtos","from produtos","select * from produtos". e nada, as vezes ele dá um erro: Caused by: java.lang.IllegalArgumentException: An exception occurred while creating a query in EntityManager:
    Exception Description: Syntax error parsing the query [produtos], line 1, column 0: unexpected token [produtos].
    o que poderia estar acontecendo?

    ResponderExcluir
  2. Olá Lucky! Vi que vc está tentando usar SQL normal para fazer as consultas, mas na JPA, vc precisa fazer as consultas no objeto e não nas tabelas do bd. Suponhamos que sua classe seja Produto e sua tabela produtos, para fazer um select nela seria assim: "Select p from Produto p"

    ResponderExcluir
  3. Parabéns pelo tutorial.

    ResponderExcluir
  4. Olá!

    Não seria possível obter uma lista com todos os objetos armazenados no BD sem fazer nenhum tipo de consulta SQL, por exemplo, neste caso vc está utilizando
    "Query query = em.createQuery("Select c from Cliente c");"
    não teria como fazer sem utilizar o Select c from Cliente c ???

    já que esta seria uma das vantagens de utilizar um Framework ORM...
    tipo, pra salvar temos o obj.save();
    Não teria como fazer desta forma, tipo:

    class.list();

    ou algo semelhante...

    té+!

    ResponderExcluir
    Respostas
    1. Olá Miltex, com o entityManager eu não vi nada parecido com isso, mas com critéria é possível usar assim, dá uma olhada nesse link http://www.universidadejava.com.br/docs/jpa20-criarconsultascomaapicriteria

      Excluir
  5. Muito bom, obrigado por compartilhar seu conhecimento conosco!

    Abraço!

    ResponderExcluir
  6. Este comentário foi removido pelo autor.

    ResponderExcluir
  7. o meu tbm deu erro ai usei

    Query query = em.createNativeQuery("Select e from equipamento e");

    ResponderExcluir

Deixe seu comentário... ;)