21 de fevereiro de 2011

JavaEE 6: CRUD em JSF 2.0

Inicialmente no banco de dados crie uma database chamada 4tads, e nela crie uma tabela cliente com o seguinte código:
CREATE TABLE `4tads`.`cliente` ( 
  `codigo` INTEGER(11)  NOT NULL AUTO_INCREMENT, 
  `nome` VARCHAR(255)  NOT NULL, 
  `telefone` VARCHAR(30) , 
  PRIMARY KEY (`codigo`) 
) 
ENGINE = InnoDB; 

Agora crie um projeto JEE 6 (Veja aqui), com o nome de crudJSF, teremos como estrutura final o seguinte projeto:


Antes de iniciar a codificação, vamos colocar no projeto o driver JDBC para acesso ao banco de dados, para isso clique com o botão direito em cima de BibliotecasAdicionar Biblioteca e selecione MySQL JDBC Driver:


Agora no Pacotes de código-fonte crie os seguintes pacotes: model, controller e dao.
Dentro do model crie uma classe java chamada Cliente.java, com os atributos codigo, nome e telefone, além dos getters e setters:
public class Cliente {

    private int codigo;
    private String nome;
    private String telefone;

    public int getCodigo() {
        return codigo;
    }

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

    public String getNome() {
        return nome;
    }

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

    public String getTelefone() {
        return telefone;
    }

    public void setTelefone(String telefone) {
        this.telefone = telefone;
    }
}

Dentro do dao vamos criar uma classe java chamada Conexao.java, esta será responsável por fazer a conexão com o banco de dados:
import java.sql.DriverManager;
import java.sql.SQLException;
import com.mysql.jdbc.Connection;

public class Conexao {

    private String driver = "com.mysql.jdbc.Driver";
    private String URL = "jdbc:mysql://localhost/4tads";
    private String USER = "root";
    private String SENHA = "admin";
    private Connection conn;

    public Conexao() {
        try {
            Class.forName(driver);
            conn = (Connection) DriverManager.getConnection(URL, USER, SENHA);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Connection getConn() {
        return conn;
    }

    public void fecharConexao() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Perceba que o atributo URL termina com 4tads, este está se referindo a nossa database criada no inicio do post que contém nossa tabela cliente. Os atributos USER e SENHA, se referem ao usuário e a senha do banco de dados.
Depois de ter criado nosso arquivo de conexão com o banco, vamos criar nossa classe ClienteDAO.java:
import com.mysql.jdbc.Statement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import model.Cliente;

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

    private Conexao conexao;
    private Statement stmt;
    private boolean sucesso = false;

    public ClienteDAO() {
        conexao = new Conexao();
        try {
            stmt = (Statement) conexao.getConn().createStatement();
        } catch (SQLException ex) {
            ex.printStackTrace();
        }
    }

    public boolean inserir(Cliente cliente) {
        try {
            stmt.execute("INSERT INTO cliente (nome, telefone) VALUES ('" + cliente.getNome() + "','" + cliente.getTelefone() + "')");
            sucesso = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conexao.fecharConexao();
        }

        return sucesso;
    }

    public boolean alterar(Cliente cliente) {
        try {
            stmt.execute("UPDATE cliente SET nome = '" + cliente.getNome() + "', telefone = '" + cliente.getTelefone() + "' WHERE codigo = '" + cliente.getCodigo() + "'");
            sucesso = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conexao.fecharConexao();
        }

        return sucesso;
    }

    public boolean remover(Cliente cliente) {
        try {
            stmt.execute("DELETE FROM cliente WHERE codigo = '" + cliente.getCodigo() + "'");
            sucesso = true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conexao.fecharConexao();
        }

        return sucesso;
    }

    public List<Cliente> listar() {
        List<Cliente> clientes = new ArrayList<Cliente>();
        try {
            ResultSet rs = stmt.executeQuery("SELECT * FROM cliente ORDER BY nome");
            while (rs.next()) {
                Cliente cliente = new Cliente();
                cliente.setCodigo(rs.getInt("codigo"));
                cliente.setNome(rs.getString("nome"));
                cliente.setTelefone(rs.getString("telefone"));

                clientes.add(cliente);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            conexao.fecharConexao();
        }

        return clientes;
    }
}

Agora dentro do controller vamos criar nosso bean, para isso crie uma nova classe java com o nome de ClienteBean.java:
import dao.ClienteDAO;
import java.io.Serializable;
import java.util.List;
import javax.enterprise.context.SessionScoped;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.inject.Named;
import model.Cliente;

/**
 *
 * @author andii
 */
@Named
@SessionScoped
public class ClienteBean implements Serializable{

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

    public void novo(){
        cliente = new Cliente();
    }

    public String inserir(){
        String resultado = "falha";
        clienteDAO = new ClienteDAO();
        boolean retorno = clienteDAO.inserir(cliente);

        if(retorno){
            resultado = "clientes";
        }

        return resultado;
    }

    public void selecionar(){
        cliente = clientes.getRowData();
    }

    public String alterar(){
        String resultado = "falha";
        clienteDAO = new ClienteDAO();
        boolean retorno = clienteDAO.alterar(cliente);

        if(retorno){
            resultado = "clientes";
        }

        return resultado;
    }

    public String remover(){
        String resultado = "falha";
        clienteDAO = new ClienteDAO();
        boolean retorno = clienteDAO.remover(cliente);

        if(retorno){
            resultado = "clientes";
        }

        return resultado;
    }

    public Cliente getCliente() {
        return cliente;
    }

    public void setCliente(Cliente cliente) {
        this.cliente = cliente;
    }

    public DataModel<Cliente> getClientes() {
        clienteDAO = new ClienteDAO();
        List<Cliente> clienteList = clienteDAO.listar();
        clientes = new ListDataModel<Cliente>(clienteList);
        return clientes;
    }

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

}

Para finalizar vamos criar nossas páginas JSF, com o botão direito em Páginas WebNovoOutro... selecione a categoria JavaServer Faces e o tipo de arquivo Página JSF e aí é só colocar o nome e finalizar. Faça esse procedimento para criar as seguintes páginas:
Obs.: apenas substitua o h:body de cada página criada pelos respectivos códigos abaixo.
novo.xhtml:
<h:body> 
        <h:form> 
            <h:panelGrid columns="2"> 
                <h:outputText value="Nome" /> 
                <h:inputText value="#{clienteBean.cliente.nome}" /> 
                <h:outputText value="Telefone" /> 
                <h:inputText value="#{clienteBean.cliente.telefone}" /> 
                <h:commandButton action="#{clienteBean.inserir}" value="Inserir" /> 
                <h:commandButton action="clientes" immediate="true" value="Cancelar" />
            </h:panelGrid> 
        </h:form> 
    </h:body>
Visualização:



alterar.xhtml:
<h:body> 
        <h:form> 
            <h:panelGrid columns="2"> 
                <h:outputText value="Nome" /> 
                <h:inputText value="#{clienteBean.cliente.nome}" /> 
                <h:outputText value="Telefone" /> 
                <h:inputText value="#{clienteBean.cliente.telefone}" /> 
                <h:commandButton action="#{clienteBean.alterar}" value="Alterar" /> 
                <h:commandButton action="clientes" immediate="true" value="Cancelar" />     
            </h:panelGrid> 
        </h:form> 
    </h:body>
Visualização:


remover.xhtml:
<h:body> 
        <h:form> 
            <h:outputText value="Deseja remover o cliente: #{clienteBean.cliente.nome} ?" /> 
            <h:panelGrid columns="2"> 
                <h:commandButton action="#{clienteBean.remover}" value="Remover" /> 
                <h:commandButton action="clientes" immediate="true" value="Cancelar" /> 
            </h:panelGrid> 
        </h:form> 
    </h:body>
Visualização:


clientes.xhtml:
<h:body> 
        <h:form> 
            <h:commandButton action="novo" actionlistener="#{clienteBean.novo}" value="Novo" /> 
            <h:dataTable value="#{clienteBean.clientes}" var="c"> 
                <h:column> 
                    <f:facet name="header"><h:outputText value="Nome" /></f:facet>
                    <h:outputText value="#{c.nome}" /> 
                </h:column> 
                <h:column> 
                    <f:facet name="header"><h:outputText value="Telefone" /></f:facet> 
                    <h:outputText value="#{c.telefone}" /> 
                </h:column> 
                <h:column> 
                    <f:facet name="header"><h:outputText value="Ações" /></f:facet> 
                    <h:commandButton action="alterar" actionListener="#{clienteBean.selecionar}" value="Alterar" /> 
                    <h:commandButton action="remover" actionListener="#{clienteBean.selecionar}" value="Remover" /> 
                </h:column> 
            </h:dataTable> 
        </h:form> 
    </h:body>
Visualização:



index.xhtml:
<h:body> 
        <h:form> 
            <h:commandButton action="clientes" value="Clientes" /> 
        </h:form> 
    </h:body>
Visualização:


erro.xhtml:
<h:body> 
        <h:form> 
            <h:outputText value="Ocorreu um erro, tente novamente:" /> 
            <h:commandButton action="index" value="Tentar novamente" /> 
        </h:form> 
    </h:body>
Visualização:



É isso aí... ;)

52 comentários:

  1. legal post como sugestão se pudesse detalhar mais o que acontece.. como por exemplo como usar o datamodel etc.

    ResponderExcluir
  2. Olá Bruno! Obrigada pela dica, se vc ver as postagens de antes são todas explicadinhas... é que realmente estou postando as pressas, sem tempo mesmo de ficar detalhando, assim que acabar essa correria posso editá-las :)

    ResponderExcluir
  3. Boa noite, Andii.

    Ótimo post, absorvi bastante coisas, porém, não consegui rodar esse exemplo.
    Estou estudando JSF e para comunicar uma pagina do JSF (zhtml ou jsp) preciso configurar um arquivo xml chamado faces-config (Configuração do JSF Faces...).

    Tem alguma dica para que eu possa fazer esse Crud funcionar?

    Valeu.

    ResponderExcluir
  4. Olá Átila, preciso que vc me diga exatamente o que está acontecendo pra dar errado, que mensagem de erro ou o que acontece. Os erros podem ser inúmeros! Quanto ao faces-config.xml no JSF 2.0 não é mais obrigatório o uso do mesmo, por isso veja para o que exatamente você precisa dele... e o que vc está usando pra fazer esses exemplo: NetBeans e GlassFish!? Seguiu exatamente como criar um projeto JSF no NetBeans? tem o link para isso na postagem... Aguardo retorno.

    ResponderExcluir
  5. Bom dia, Andii.

    Fiz tudo exatamente como está no exemplo acima, inicialmente criei o projeto com Tomcat 7.0, agora criei outro com GlassFish 3.1, estou usando o NetBeans 7.0, e segui exatamente como criar um projeto JSF no NetBeans do seu outro post..
    Vamos lá, o erro é o seguinte:

    Quando inicializo o sistema, ele me traz a tela apenas com o botão 'Clientes' onde eu clico e ele me leva pra pagina 'Clientes' que mostra todos os cadastrados e me da as opções de Cadastrar um novo, alterar ou excluir um existe, porém, quando ele me leva para a pagina 'Clientes', não vem nenhum registro do banco de dados, como se nao tivesse nenhum cadastrado (Cadastrei alguns manualmente no MySql); Daí quando clico em 'Novo' para cadastrar um registro pela pagina, não da em nada, eu clico no botão 'Novo' mas continua ali, na pagina 'Clientes' sem registro algum. Por isso pensei que fosse problema no 'faces-config'...

    Agradeço desde já.

    ResponderExcluir
  6. Dá uma conferida na sua classe Conexao, seja se os dados estão de acordo com o seu banco, tipo: database, username e senha. Quanto ao botão Novo, tome cuidado que o JSF é case sensitive, então na action que chama a pagina e no nome da página a ser chamadas precisam ser idênticos: por exemplo, veja se colocou tudo minúsculo. No NetBeans na tab do GlassFish, veja se dá algum erro, caso sim, poste alguma parte dele que tenha um "Cause by"

    ResponderExcluir
  7. estou acostumado a usar o JSF 1.2. Sem o faces-config, como dizer qual pagina irá para qual, como controlar o fluxo??

    ResponderExcluir
  8. resolvi minhas dúvidas, aprendi que os ANNOTATIONS substituem o faces-config.xml para efeito de bean gerenciador e que no JSF 2.0, o proprio return já direciona para a pagina escolhida...tks

    ResponderExcluir
  9. Olá! é bem isso mesmo, desculpa por não ter respondido antes!

    ResponderExcluir
  10. Olá Andii, estou começando com JSF e estou acompanhando e aproveitando muito suas postagens, obrigado mesmo!

    Agora sobre o post o meu esta funcionando até na hora de inserir, que ele retorna o seguinte erro: "Não foi possível encontrar um caso de navegação correspondente na ID de exibição '/novo.xhtml' para a ação '#{clienteBean.inserir}' com o resultado 'falha'"

    Pensei que poderia ser o caminho da String do BD, pois no ex seu esta faltando a porta do MySQL entretanto a coloquei e o erro persiste.
    Caso possa me ajudar estarei muito grato,

    Obrigado!

    Fábio Cordeiro
    www.taichous.com
    fabioled.blog.com

    ResponderExcluir
  11. Olá Fábio! Bom, aconselho vc a ver o erro que está aparecendo no console do GlassFish, lá tem a exceção que está ocorrendo, procure por "cause by" e poste ela aqui para eu poder ver o que está acontecendo.

    ResponderExcluir
  12. Olá Anddi, primeiramente obrigado pelo resposta.
    Verifiquei no GlassFish onde estava dando erro e corrigi, ele deu esse erro anterior por que coloquei como retorno de uma String com nome falha e pagina de erro criada foi a pagina com nome erro.xhtml. Pois bem na verdade isso não corrigiu o problema propriamente dito.

    Mesmo antes disso eu inseri dados manualmente no BD e consegui listá-los, agora quando vou inserir ou alterar ou excluir ele esta gerando uma exceção relacionado ao método Selecionar gerando a seguinte exceção " SEVERE: 'java.lang.NullPointerException' recebido ao invocar escuta de ação '#{clienteBean.selecionar}' para o componente 'j_idt17'
    SEVERE: java.lang.NullPointerException
    at controller.ClienteBean.selecionar(ClienteBean.java:44).

    No código esta exceção remete para a seguinte linha do método selecionar:
    cliente = clientes.getRowData();



    Esse erro ocorre quando vou tentar alterar ou excluir algum registro. Quando peço para listar não ocorre nenhuma exceção porem ele lista os valores cadastrados manualmente normal, e os valores nos quais eu cadastrei pelo formulário ele lista como NULL.
    Estou fazendo testes e System.out.println no código todo para entender onde estou errando.

    Desde já muito agradecido por tua atenção!

    ResponderExcluir
  13. Qual o escopo que vc está usando?

    ResponderExcluir
  14. Estranho, deveria funcionar sem problemas. Realmente não sei te dizer o que está acontecendo, quando é assim, tem que fazer testes e mais testes, System.out.print aqui e ali e ver onde está a falha.

    ResponderExcluir
  15. Olá. Tentei rodar o exemplo mas estou tomando o seguinte erro:javax.servlet.ServletException: /novo.xhtml @13,68 value="#{ClienteBean.cliente.nome}": Target Unreachable, identifier 'ClienteBean' resolved to null javax.faces.webapp.FacesServlet.service(FacesServlet.java:606).

    ResponderExcluir
  16. Olá, não consegui rodar, pois o seguinte erro aparece no browser: Não foi possível encontrar um caso de navegação correspondente na ID de exibição '/index.xhtml' para a ação 'ufs' com o resultado 'ufs'

    ResponderExcluir
  17. Muito bom !!! Funcionou certinho aqui.
    Ajudou bastante, principalmente porque iniciei meus estudos a pouco tempo do desenvolvimento web Java, e precisava de uma fonte para tirar duvidas. Obrigado e continue assim

    ResponderExcluir
  18. Olá Allan! é que ele não está encontrando o ClienteBean com C maiúsculo... é que quando vc não define a nomenclatura do bean, o padrão é que vc deva acessar ele com a primeira letra minuscula, então troque seu ClienteBean por clienteBean

    ResponderExcluir
  19. Olá Antonio Marcos! O problema está sendo que seu último método chamado está retornando uma String "ufs" e vc não tem uma página com esse nome. Ou seja, quando vc retorna alguma coisa em um método utilizando o action do JSF, ele vai te redirecionar para a página que tenha o mesmo nome do retorno.

    ResponderExcluir
  20. Andii,
    Consigo inserir numa boa,mas não consigo exibir os dados. Sempre retorna vazio, apesar dos dados persistirem no banco. Rola uns pequenos bugs chatos de resolver que me impedem de avançar.

    ResponderExcluir
  21. Procurei no site posts relativos a DataModel mas não achei. Você poderia dar um bizu de como funciona essa classe?

    ResponderExcluir
  22. Veja que o DataModel é do pacote faces, então ele é uma lista assim como o List praticamente, mas ele tem a diferença que ao selecionar uma linha da dataTable ele já te retorna o objeto daquela linha, veja isso no metodo selecionar do Bean.

    ResponderExcluir
  23. Quanto não trazer os dados: veja que no getClientes() este método é editado, onde tem uma conversão de um List para um DataModel

    ResponderExcluir
  24. Andii primeiramente parabéns pelo post...há vários dias venho lendo posts sobre jsf e tals mas o seu é mto bom....

    Bom seguindo o projeto meu dá o seguinte erro:"Não foi possível encontrar um caso de navegação correspondente na ID de exibição '/novo.xhtml' para a ação '#{clienteBean.inserir}' com o resultado 'falha'"

    estou usando o postgre como banco...

    ResponderExcluir
  25. bom esse erro consegui arrumar -.- colokndo pra chama a página de erro que na vdd se chama falha porém qndo tento inserir nao funfa...vai pra pag de erro...msmo se insiro manualmente pelo banco nao lista....provavelmente é a conexao com o banco que está errada...vc sab se além do driver e configuração da conexao muda mas alguma coisa por eu usar o postgre?

    vlw

    ResponderExcluir
  26. Bom andii qria apenas dizer que após alguns post consegui resolver o problema realmente era a conexao...

    vlw...mto o post

    ResponderExcluir
  27. Tami! desculpa a demora, cheguei de viagem apenas hoje, estava sem internet nessa 1 semana.

    ResponderExcluir
  28. Andii...td bem de qualquer forma em outros posts achei o erro...bom to aqui pq tenho outro probleminha do qual nao imagino como resolver....

    Bom na classe usamos o o objeto Statement.execute, para executar comandos do banco, porém quando tento criar qualquer outra classe .DAO dá o seguinte erro no comando execute ==" metodo execute in class java.beans.Statement cannot applied to given types required:no arguments"...tentei entao usar o preparedStatement porem nao funfa....

    Vc imagina ou sabe qual o problema?

    ResponderExcluir
  29. Olá tami**kinderOvo, o que eu posso te dizer é que para usar o PreparedStatement o Conexao.java será um pouco diferente deste do exemplo.

    ResponderExcluir
  30. Andii vc sab o que causa esse erro javax.faces.model.NoRowAvailableException sei q eh por causa do DataModel...mas fiz td conforme o exemplo...a única coisa é q estou usando o prepared statement...mas na inclusão funfa normal...

    ResponderExcluir
  31. Olá Tami.
    Verifique se os dados estão sendo trazidos do banco de dados. E em que momento dá esse erro?

    ResponderExcluir
  32. Posso afirmar...
    Modelo é meu POJO;
    Manager Bean é meu controle responsável em renderizar os valores do meu view para minha classe modelo (Pojo), passa os valores da classe POJO para minha instancia DAO para executar a logica do negocio;
    DAO é responsável pela lógica do negocio.

    ResponderExcluir
  33. Olá Ayres! Não irei discutir isso, mas foi dessa forma que eu aprendi, e em minhas pesquisas, o DAO sempre foi o padrão para persistência de dados.

    ResponderExcluir
  34. Olá. Tenho a seguinte dúvida: Iniciei um projeto CRUD com JSP e Apache Tomcat como servidor no Eclipse Indigo seguindo os passos que estão em outra página de voces e está tudo certo, a parte Model, DAO, Control, as páginas JSP e tal, mas e se eu quiser agora usar JSF ? Ou Primefaces, como eu faço? Nos tutoriais é sempre: "INICIE UM NOVO PROJETO CLICANDO EM ..." e por aí vai. Mas e se eu tiver o projeto iniciado eu não consigo altera-lo? Eu vi que com JSF as páginas ficam mais bonitas e os controle mais funcionais com validação e etc., mas não sei ainda como fazer, poderiam dar outra mão? Seu site foi o que mais me ajudou até agora. Show de bola. Estão de parabéns....
    Atenciosamente

    Fabio

    ResponderExcluir
    Respostas
    1. Fábio, depende muito também de que versão do Java EE vc está utilizando, se for o 5 e quer partir para o 6, eu já tentei "converter" e não deu muito certo, pra mim a solução foi começar do zero. Quanto ao eclipse não conseguirei te ajudar, faz muitoooo tempo que não mexo mais com ele. Mas se vc for começar um novo projeto, te aconselho usar o Netbeans, é bem mais tranquilo.

      Excluir
  35. muito bom , estou pegando ele como base para aprimorar o meu tcc

    ResponderExcluir
  36. Parabéns exemplo muito bom e objetivo.

    ResponderExcluir
  37. Qual a classe está mapeada para ser o ManagedBean? Cliente ou ClienteBean? Pois as duas não estão com a anotação @ManagedBean.

    ResponderExcluir
  38. Amigo notei algo estranho, quando aperto o botão Novo no clientes.xhtml ele me leva a tela de novo.xhtml, então eu cadastro um Cliente, mas notei uma coisa, se eu apertar em editar um cliente, e depois ir em Novo ele está indo para a tela do novo.xhtml e trazendo os dados do Cliente que eu tinha editado.

    O que eu fiz de errado?

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

    ResponderExcluir
  40. Amigo eu fiz aqui e ta certo eu verifiquei mas no conexao ele fica querendo remover o printStackTrace e não esta abrindo a page index, vc pode me ajudar tem algo que pode substituir o printStackTrace.

    ResponderExcluir
  41. Amigo gostei muito do seu crud , agora gostaria de saber se só com isso estaria apto a fazer qualquer sistema, visto que basicamente todo software apresenta as 4 opções CRUD, fico no aguardo da resposta sou iniciante em JSF e pelo tutorial que vi o seu foi de mais fácil entendimento!
    abraço

    ResponderExcluir
    Respostas
    1. Bom dia Anderson, sinceramente falando, a maioria dos sistema envolvem operações bem mais complexas do que apenas um CRUD. Cada sistema tem sua particularidade, agora: se vc quiser fazer uma agenda telefônica ou algo nesse nível, vc pode se arriscar de fazer com essa base. Mas falando em fazer 'qualquer sistema", não é bem simples assim, pois vc precisa ter uma lógica bem apurada, tem que conhecer de segurança, interface e relatórios também. Mas se é vc mesmo (sozinho) que irá fazer o sistema, aí entram outras disciplinas, como: análise/projeto e interface homem-maquina, e assim por diante.

      Excluir
  42. Olá Andi boa tarde!
    Você poderia criar uma aplicação básica !
    simples mesmo mais usando o bootstrap como design!
    Poderia ser até um crud Básico Mesmo!
    esse Projeto usando Maven , Jboss!
    Obrigado!
    e Parabéns pelo Blog!

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

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

    ResponderExcluir
  45. Olá boa noite/bom dia,

    só não entendi qual jar é responsável por gerar a importação javax.enterprise.context.SessionScoped, que consegue tratar o @Named.




    Grato

    ResponderExcluir
  46. Muito bom o seu material.
    Gostaria de saber se posso utilizá-lo para demonstração em uma aula introdutória de JSF no curso de Sistemas para Internet.
    Prof. Paulo Cezar professorpaulocezar@gmail.com

    ResponderExcluir
  47. Parabéns pelo post. Eu queria aproveitar e compartilhar 12 projetos em Java Web com o pessoal que está passando aqui pelo site. Segue link pra download → https://goo.gl/7mnVyL

    ResponderExcluir

Deixe seu comentário... ;)