Cadastro utilizando JSF2 + JPA2 + EJB3 + CDI + PrimeFaces 2.2 Criando o Banco de Dados A figura abaixo mostra as tabelas que vão fazer parte de nossa demonstração. O exemplo mostra a criação de um simples controle de vendas, onde temos apenas duas entidades: Clientes e Compras.
Criando o Projeto Vamos agora criar o nosso projeto web no NetBeans. Para isso vamos clicar no menu Arquivo -> Novo Projeto. Na janela que se abre vamos escolher a categoria Java Web do lado esquerdo e Aplicação Web do lado direito. Conforme imagem abaixo:
Na próxima tela devemos informar o nome do nosso projeto e o diretório onde será salvo. Escolha o nome que desejar para o projeto. Na tela seguinte é onde devemos informar qual servidor gostaríamos de usar e qual versão da JEE será usada no nosso projeto. Para o nosso exemplo vamos escolher Apache e JEE6. Na tela seguinte, o assistente pergunta quais frameworks gostaríamos de utilizar para desenvolver nosso projeto. Vamos marcar a opção Java Server Faces e em Complementos selecionar o PrimeFaces. Pronto, nosso projeto está criado. Precisamos agora fazer algumas configurações no nosso projeto: configurar um tema para a aplicação. Escolha um tema, baixe e adicione às bibliotecas. Para que o tema funcione é necessário que adicione as linhas abaixo no arquivo web.xml:
1.
primefaces.THEME 2. cupertino 3. 4.
Pronto, nosso projeto está criado e pronto para receber os componentes do Primefaces. Vamos ao próximo passo. Criando o arquivo persistence.xml Vamos agora navegar entre as especificações que foram citadas no titulo do post. A primeira delas é a JPA (Java Persistence API). Em geral, essa especificação define regras para o mapeamento objeto relacional. A implementação de referência é o eclipseLink,mas iremos usar o Hibernate. A especificação fala que devemos ter um arquivo de configuração, que seja independente da implementação usada. Esse arquivo contém todas as informações necessárias para persistir objetos, conexão com o banco de dados, detalhes de caches, dentre outros. Esse arquivo chama-se persistence.xml. Vamos adicionar agora esse arquivo ao nosso projeto. Para isso, clique com o botão direito do mouse sobre o nome do projeto e em novo, escolha a opção outro. Na janela que abrir procure pela categoriaPersistência do lado esquerdo e do lado direito procure por Unidade de Persistência. Ao selecionar essas opções vai aparecer pra você a janela da imagem abaixo:
Gerando as Entidades a partir do Banco de Dados Com nossa unidade de persistência criada podemos criar nossas classes de entidades a partir de nossas tabelas. Desta forma nossas classes de modelo vão ser geradas pelo NetBeans e vão está perfeitamente anotadas com as anotações da JPA. Para gerar as tabelas basta clicar com o botão direito do mouse a pasta pacotes de códigos fontes e escolher a opção novo e depois outro… Na janela que vai abrir procure pela
opção Persistência do lado direito e do lado esquerdo procure por Classe de Entidade do Banco de Dados (Entity Classes from Database) a próxima janela é mostrada na figura abaixo.
Nessa primeira janela apenas selecionamos o nosso data source (jdbc/sample) criado durante a criação de nossa unidade de persistência. Ao selecionar nossas tabelas vão aparecer, em seguida temos apenas que selecionar de quais tabelas queremos gerar as classes de entidade. Vamos selecionar a tabela tab_compras e jogar para o lado direito. Observe que ao jogarmos a tabela tab_compras, automaticamente a tabela tab_clientes também é jogada. Isso porque tab_compras possui um relacionamento de dependência com a tabela tab_clientes. Em outras palavras, não existem compras sem clientes. A próxima janela é mostrada na figura abaixo:
Na janela acima definimos o nome da classe para cada tabela e para qual pacote as classes geradas vão adicionadas. Observe que criamos o pacote qparadigmas.modelo. Pronto podemos finalizar e ver as classes geradas no pacote. Cliente.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58.
/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package qparadigmas.modelo; import import import import import import import import import import import import import import import import
java.io.Serializable; java.util.Date; java.util.List; javax.persistence.Basic; javax.persistence.CascadeType; javax.persistence.Column; javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; javax.persistence.NamedQueries; javax.persistence.NamedQuery; javax.persistence.OneToMany; javax.persistence.Table; javax.persistence.Temporal; javax.persistence.TemporalType;
/** * * @author Wagner */ @Entity @Table(name = "TAB_CLIENTES" ) @NamedQueries ({ @NamedQuery(name = "Cliente.findAll", query = "SELECT c FROM Cliente c" ), @NamedQuery(name = "Cliente.findById", query = "SELECT c FROM Cliente c WHERE c.id = : id"), @NamedQuery(name = "Cliente.findByNome", query = "SELECT c FROM Cliente c WHERE c.nome = :nome"), @NamedQuery(name = "Cliente.findByDataNascimento", query = "SELECT c FROM Cliente c WH ERE c.dataNascimento = :dataNascimento" ), @NamedQuery(name = "Cliente.findByCpf", query = "SELECT c FROM Cliente c WHERE c.cpf = :cpf")}) public class Cliente implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue @Column(name = "ID") private Integer id; @Basic(optional = false) @Column(name = "NOME") private String nome; @Column(name = "DATA_NASCIMENTO") @Temporal(TemporalType.DATE) private Date dataNascimento; @Basic(optional = false) @Column(name = "CPF") private String cpf; @OneToMany(cascade = CascadeType.ALL, mappedBy = "cliente") private List compraList; public Cliente() {
} public Cliente(Integer id) {
this.id = id; 59. 60. } 61. public Cliente(String nome, Date dataNascimento, String cpf) { 62. this.nome = nome; 63. this.dataNascimento = dataNascimento; 64. this.cpf = cpf; 65. 66. } 67. public Integer getId() { 68. return id; 69. 70. } 71. public void setId(Integer id) { 72. this.id = id; 73. 74. } 75. public String getNome() { 76. return nome; 77. 78. } 79. public void setNome(String nome) { 80. this.nome = nome; 81. 82. } 83. public Date getDataNascimento() { 84. return dataNascimento; 85. 86. } 87. public void setDataNascimento(Date dataNascimento) { 88. this.dataNascimento = dataNascimento; 89. 90. } 91. public String getCpf() { 92. return cpf; 93. 94. } 95. public void setCpf(String cpf) { 96. this.cpf = cpf; 97. 98. } 99. public List getCompraList() { 100. return compraList; 101. 102. } 103. public void setCompraList(List compraList) { 104. this.compraList = compraList; 105. 106. } 107. 108. @Override public int hashCode() { 109. int hash = 0; 110. 111. hash += (id != null ? id.hashCode() : 0); return hash; 112. 113. } 114. 115. @Override public boolean equals(Object object) { 116. 117. // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Cliente)) { 118. return false; 119. 120. } 121. Cliente other = (Cliente) object; if ((this.id == null && other.id != null) || ( this.id != null && !this.id.equals(o 122. ther.id))) { return false; 123. 124. } return true; 125. 126. }
127. 128. 129. 130. 131. 132. 133.}
@Override public String toString() { return "qparadigmas.modelo.Cliente[id=" + id + "]"; }
Compra.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56.
package qparadigmas.modelo; import import import import import import import import import import import import import import
java.io.Serializable; java.util.Date; javax.persistence.Column; javax.persistence.Entity; javax.persistence.GeneratedValue; javax.persistence.GenerationType; javax.persistence.Id; javax.persistence.JoinColumn; javax.persistence.ManyToOne; javax.persistence.NamedQueries; javax.persistence.NamedQuery; javax.persistence.Table; javax.persistence.Temporal; javax.persistence.TemporalType;
/** * * @author Wagner */ @Entity @Table(name = "TAB_COMPRAS") @NamedQueries ({ @NamedQuery(name = "Compra.findAll", query = "SELECT c FROM Compra c"), @NamedQuery(name = "Compra.findById", query = "SELECT c FROM Compra c WHERE c.id = :id "), @NamedQuery(name = "Compra.findByDescricao" , query = "SELECT c FROM Compra c WHERE c.d escricao = :descricao"), @NamedQuery(name = "Compra.findByValor", query = "SELECT c FROM Compra c WHERE c.valor = :valor"), @NamedQuery(name = "Compra.findByDataCompra", query = "SELECT c FROM Compra c WHERE c. dataCompra = :dataCompra" )}) public class Compra implements Serializable { private static final long serialVersionUID = 1L; @Id @GeneratedValue @Column(name = "ID") private Integer id; @Column(name = "DESCRICAO") private String descricao; @Column(name = "VALOR") private Float valor; @Column(name = "DATA_COMPRA") @Temporal(TemporalType.DATE) private Date dataCompra; @JoinColumn(name = "CLIENTE_ID", referencedColumnName = "ID") @ManyToOne(optional = false) private Cliente cliente; public Compra() {
} public Compra(Integer id) { this.id = id;
} public Integer getId() { return id;
}
57. public void setId(Integer id) { 58. this.id = id; 59. 60. } 61. public String getDescricao() { 62. return descricao; 63. 64. } 65. public void setDescricao(String descricao) { 66. this.descricao = descricao; 67. 68. } 69. public Float getValor() { 70. return valor; 71. 72. } 73. public void setValor(Float valor) { 74. this.valor = valor; 75. 76. } 77. public Date getDataCompra() { 78. return dataCompra; 79. 80. } 81. public void setDataCompra(Date dataCompra) { 82. this.dataCompra = dataCompra; 83. 84. } 85. public Cliente getCliente() { 86. return cliente; 87. 88. } 89. public void setCliente(Cliente cliente) { 90. this.cliente = cliente; 91. 92. } 93. 94. @Override public int hashCode() { 95. int hash = 0; 96. 97. hash += (id != null ? id.hashCode() : 0); return hash; 98. 99. } 100. 101. @Override public boolean equals(Object object) { 102. 103. // TODO: Warning - this method won't work in the case the id fields are not set if (!(object instanceof Compra)) { 104. return false; 105. 106. } 107. Compra other = (Compra) object; if ((this.id == null && other.id != null) || ( this.id != null && !this.id.equals(o 108. ther.id))) { return false; 109. 110. } return true; 111. 112. } 113. 114. @Override public String toString() { 115. return "qparadigmas.modelo.Compra[id=" + id + "]"; 116. 117. } 118. 119.}
Criando os Sessions Beans
Vamos criar agora as classes que são responsáveis por prover os serviços de acesso ao dado. Essa classe comumente chamada de DAO (Data Acces Object). Aqui neste post vamos utilizar EJB para nossas classes de serviços. A versão 3.0 dos EJBs trouxeram grandes facilidades no uso de Session Beans ao adotar anotações, injeção de dependências e inversão de controle. Para esse exemplo vamos criar Stateless Session Beans. Seráo classes cujos objetos vão ser injetados por Injeção de Dependencia utilizando CDI (Context Dependency Injection). Um Stateless Session Beans não mantei estado de conversação com o servidor, isso significa que o tempo de vida de um objeto dura até o execução de uma solicitação. Para criar nosso Session Bean, basta anotarmos a classe com a anotação @Stateless. Abaixo segue os código das classesClienteService e ComprasService. 1. @Stateless 2. public class ClienteService { 3. 4. @PersistenceContext(name = "qparadigmasPU") 5. private EntityManager entityManager; 6. public List findAll() { 7. 8. Query query = entityManager.createNamedQuery( "Cliente.findAll"); return query.getResultList(); 9. 10. } 11. public void save(Cliente cliente) { 12. 13. entityManager.persist(cliente); 14. } 15. public void delete(Cliente cliente) { 16. 17. entityManager.remove(cliente); 18. } 19. public void update(Cliente cliente) { 20. 21. entityManager.merge(cliente); 22. } 23. } 24. @Stateless 25. public class ComprasService { 26. @PersistenceContext (name = "qparadigmasPU" ) private EntityManager entityManager; 27. 28. public Collection findAll() { 29. 30. Query query = entityManager.createNamedQuery( "Compra.findAll"); return query.getResultList(); 31. 32. } 33. public void save(Compra compra) { 34. 35. entityManager.persist(compra); 36. } 37. public void delete(Compra compra) { 38. 39. entityManager.remove(compra); 40. } 41. public void update(Compra compra) { 42. 43. entityManager.merge(compra); 44. } 45. }
Criando os Controllers Controladores são classes que normalmente representam uma página JSF, são comumente chamadas deBackBeans ou ManagedBeans. A quem diga que são coisas diferentes, mas não existe uma explicação 100% aceita. Aqui vamos chamar de Managed Beans. Essas classes fornecem serviços que visíveis em nossas páginas, esses serviços podem
ser métodos, atributos ou objetos. Segundo os especialistas, o ideal é que se tenha uma classe ManagedBean para cada página. Em nosso exemplo vamos ter apenas uma página, então vamos ter um único controlador. Vamos chamar nossa classe de VendasController. Uma observação importante é que ao invés de usarmos a anotação @ManagedBean, vamos utilizar a anotação @Named, já que desejamos implementar o exemplo usando os recursos da CDI, para criar nosso controller. Quando usamos CDI é necessário criamos o arquivo beans.xml dentro da pasta WEB-INF. Isso pode ser feito facilmente clicando a pasta com o botão direito e escolhendo Novo à Outro… e na janela que vai abrir procure pela categoria Context Depedency Injection (CDI), em seguida do lado direito procure por beans.xml (CDI Configuration File). Quando avançar para a próxima tela, apenas digite beans para o nome do arquivo. Abaixo segue o código da classe VendasController. 1. @Named(value = "vendasController" ) 2. public class VendasController { 3. 4. @Inject private ClienteService clienteService; 5. private Cliente cliente = new Cliente(); 6. 7. public VendasController() { 8. 9. } 10. public List getListaClientes() { 11. 12. return clienteService.findAll(); 13. } 14. 15. public Cliente getCliente() { 16. return cliente; 17. } 18. public void setCliente(Cliente cliente) { 19. this.cliente = cliente; 20. 21. } 22. }
Criando a pagina JSF Para terminar essa primeira parte do post, vamos editar nossa página index.xhtml e vamos deixa-lo pronto para os posts seguintes. Nesse primeiro momento nossa página apenas vai possuir a funcionalidade de listar os clientes já cadastrados. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19.
20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40.
A imagem abaixo mostra uma prévida página:
A segunda funcionalidade que vamos adicionar será o cadastro de clientes. Vamos utilizar o mesmo controller do post anterior: VendasController. Apenas vamos fazer duas alterações, acrescentar o método adicionarCliente e colocar nosso controller no escopo de sessão. As alterações são mostradas no código abaixo: Controller no escopo de sessão: @Named @SessionScoped public class VendasController implements Serializable Método adicionarCliente() public void adicionarCliente() { clienteService.save(cliente); if (cliente.getId() > 0) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Novo Cliente Adicionado com Sucesso!")); } else { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Ocorreu um erro na tentativa de salvar um novo Cliente!"));
} } O código acima pode ser colocado em qualquer lugar de seu Controller. Como você pode observar, logo depois da chamada para o método save, é feita uma verificação para sabermos se o novo objeto foi salvo com sucesso ou se ocorreu algum erro durante a gravação. Se a gravação ocorreu com sucesso, o atributo id é atualizado com o valor que foi gerado no banco pela sequencia. Caso contrário o id continua com o valor zero. O que nós fazemos é enviar uma mensagem para o usuário notificado o que aconteceu. Essa mensagem vai ser exibída em um componente p:growl que foi inserido página index.xhtml no post anterior. Criando a Página Adicionar Cliente Vamos agora criar a página que vai representar o cadastro de um novo cliente em nosso sistema. O código da página que vamos fazer agora, poderia ficar na página index.xhtml. Mas por questão de organização e de melhor legibilidade do código vamos coloca-lo em uma página separada. Em seguida através da diretiva ui:include vamos adiciona-la à nossa pagina index.xhtml. Abaixo vamos mostrar três códigos: O código da página adicionar_cliente.xhtml, o código de inclusão da página de cadastro na página index.xhtml e o código do botão Adicionar Cliente para o dialogo de cadastro. Código da Página adicionar_cliente.xhtml
Fazendo a inclusão na Página index.xhtml A inclusão pode ser feita em qualquer parte de sua página. Mas o melhor lugar para se fazer é logo depois que iniciar a tag
.
Código do botão Adicionar Cliente Deixamos um botão rotulado de Adicionar Cliente na página index.xhtml. Vamos agora fazer que ele abra nosso dialog de cadastro de cliente quando clicado. Abaixo segue o código do botão:
A figura abaixo mostra nosso dialog de Cadastrado incluindo um novo cliente e atualizando nossa tabela de listagem:
A imagem abaixo mostra a tabela depois de alguns registros adicionados:
Vamos agora implementar a funcionalidade de exclusão. O nosso botão de excluir já aparece na listagem porem ainda não faz nada. Vamos fazer com que ele, quando clicado, exclua um cliente de nossa base de dados. Para isso, vamos fazer duas simples alterações: Uma na classe VendasController para adicionar o método excluirCliente e adicionar um atributo, clienteSelecionado, que represente o objeto que deverá ser excluído. a Segunda alteração será na página index.xhtml, para adicionarmos a funcionalidade ao botão de excluir. Atributo private Cliente clienteSelecionado; //gerar getters e setters Método excluirCliente() public void excluirCliente() { try { clienteService.delete(clienteSelecionado); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Ocorreu um erro na tentativa de excluir o Cliente!")); } catch (Exception e) { FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Ocorreu um erro na tentativa de excluir o Cliente!")); } } Alteração da Página index.xhtml Esse código vai para o botão da lixeira, que fica dentro da listagem de clientes.