quinta-feira, 20 de setembro de 2012

JAVA + Spring + Netbeans 7.2 - Mysql + Swing + CRUD Lesson 06

Dando seqüência nos posts de JAVA + Spring

Requisitos :
- Netbeans 7.2
- spring-3.2.0.M2
- estar com o banco mysql springlessons, e a tabela contato criada. ver lição 4 ou 5

- Ter lido :

Posts Anteriores : 
JSNBTut - Base  que é uma preparação do ambiente para o desenvolvimento

Opcionais : 
JSNBTut - Lesson 01 - introdução ao Spring
JSNBTut - Lesson 02 - introdução ao gerenciamento de dependências no  Spring

JSNBTut - Lesson 03 - acessando banco de dados Mysql com beans e injeção de dependências
JSNBTut - Lesson 04 - outro jeito rápido de utilizar o Spring e Mysql sem utilizar Arquivos XML.

Obrigatório:
JSNBTut - Lesson 05 - comparação entre as implementações e introdução ao Swing. Jtable + DefaultTableModel e implementação de uma TableModel, analizando a aplicação com o Profile
, e apresentação do exemplo que usaremos nesta lição

Em vez de criar outro projeto, renomear , etc vou fornecer o projeto limpo dos testes e pronto p/ acompanhar a lição
- Alterei a classe Main p/ chamar  o frameContato
- Refatorei o projeto p/ adequar ao tutorial
- Removi o que não estava sendo utilizado

Antes que alguém reclame de usar todas as libs do spring,  nesse projeto incluí apenas as libs q estão sendo utilizadas (se estiver sobrando alguma, me avise ;) )

Figura 01- Inicio do projeto

Download do projeto NB
SpringLesson06-Base


Este é o 6º da série , o objetivo deste post é :
- Implementar um CRUD no exemplo
- Criar as telas adicionais

p/ economizar  os screenshots, algumas coisas básicas como citar botão direito, nova classe etc, vou apenas apresentar a tela da criação da classe, os trechos importantes de código e no final da explicação o código inteiro, sem  a parte gerada pelo NB p/ gerenciar o layout, e quando alterarmos uma propriedade de algo no Design mode, procurarei mostrar o q foi gerado no código quando importante para o entendimento  do exemplo, porque no final da lição é fornecido o projeto completo que fazemos durante o tutorial.

O importante é ver o conceito e conseguir aplicar p/ seus exemplos, existem várias maneiras de resolver um problema, estou sugerindo um.


1) Ao abrir o Projeto, corrija as dependencias apontando as libs para a pasta onde vc baixou o spring spring-3.2.0.M2 ou o mais atual, e localize-as na pasta libs conforme explicado no JSNBTut - Base 

Clique no Run >> Project
o resultado deve ser parecido com esse :
Figura 02 - Teste o programa baixado ou renomeado

Se compilou e rodou, vamos adiante



2) abra o frameListaContato em modo Design e adicione 3 botões  e 2 Labels nessa tela arrastando da Pallete

Figura 03 - Arraste os itens do Palette p/ dentro do formulario
A) Vamos acertar a Tela
I) Vamos definir o tamanho minimo da janela:
Clique duas vezes na linha cinza q envolve o formulário, vc deverá ver uma tela assim :
Figura 04 - Definindo as medidas da tela

II) Selecione o Frame e na aba properties faça 3 acertos :
a) Defina a propriedade title como Meus Contatos
Figura 05 - Defina o titulo do Frame

b) Defina a propriedade minimum size p/ ficar quase igual a size, p/ que o usuário não possa diminuir a tela, sumindo a grade e os botões
Figura 06 - Evitando redimensionamentos


B) Vamos acertar os botões :
Opcional : vou adicionar ícones aos botões:
Antes que alguém pergunte dos direitos.

The icons are free for personal use and also free for commercial use, but we require linking to our web site.
http://creativecommons.org/licenses/by-sa/3.0/
os ícones : 
sair 

usuario

adicionar/novo 

remover 

editar 


Se vc optar por colocar os ícones, vamos criar um package p/ guardar as imagens :
Figura 07 - Criar um package p/ as imagens package name=springlessons06.images


Coloque as imagens dentro desse diretório :
Figura 08 - Coloque as imagens dentro da pasta imagens criada no NB

O NB já deve mostrar as imagens no Project Explorer :
Figura 09 - NB verifica alterações externas e mostra as imagens

I) No botão que tem o texto jButton1 , selecione esse botão e vamos acertar ele pela aba properties :
Defina as propriedades :
Text: Novo
tooTipText: Adicionar Contato
Se for adicionar a imagem, clique no icon, e selecione o ícone  :
Figura 10 - Adicionando ícones ao programa, neste caso ao botão


II) Altere o nome da variável do botão para jBtNovo : isso vc pode fazer de 2 jeitos :

II a )- botão direito : Change Variable Name
Figura 11 - Alterando nome de variáveis

II b) - Botão direito, propriedades altere Variable Name
Figura 12 - Alterando nome de variáveis, essa caixa de propriedades pode ser acessada com o botao direito em qualquer objeto

o form deve ficar assim :
Figura 13 - Proposta visual do formulario com os botões
Veja o código gerado para entender a aplicação das propriedades na figura acima

// Variables declaration - do not modify
    private javax.swing.JButton jBtEditar;
    private javax.swing.JButton jBtNovo;
    private javax.swing.JButton jBtRemover;
    private javax.swing.JButton jBtSair;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;

e dentro da função initComponents

jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable();
        jBtSair = new javax.swing.JButton();
        jBtNovo = new javax.swing.JButton();
        jBtEditar = new javax.swing.JButton();
        jBtRemover = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        jLabel2 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Meus Contatos");
        setMinimumSize(new java.awt.Dimension(638, 317));
        setSize(new java.awt.Dimension(638, 320));

        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {

            }
        ));
        jScrollPane1.setViewportView(jTable1);

        jBtSair.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/icon_sair.png"))); // NOI18N
        jBtSair.setText("Sair");

        jBtNovo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/user_add.png"))); // NOI18N
        jBtNovo.setText("Novo");
        jBtNovo.setToolTipText("Adicionar Contato");

        jBtEditar.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/user_edit.png"))); // NOI18N
        jBtEditar.setText("Editar");

        jBtRemover.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/user_delete.png"))); // NOI18N
        jBtRemover.setText("Remover");

        jLabel1.setText("jLabel1:");

        jLabel2.setText("jLabel2");

C) Vamos acertar os Labels, a idéia é informar o total de Registros : o label que contem o valor total iremos atualizar apenas esse texto , por isso 2 Labels;

Labels também podem conter imagens, então nossos Labels ficarão jLabel1=ICONE + Total de usuariosjLabel2=0, que será renomeado p/ jLbTotal

Veja o código gerado :
   
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLbTotal;

    jLabel1 = new javax.swing.JLabel();
    jLbTotal = new javax.swing.JLabel();
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/user.png"))); // NOI18N
        jLabel1.setText("Total de Usuários");

        jLbTotal.setText("0");
        jLbTotal.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));
E abaixo o resultado :
Figura 14 - Edição do formulário em Modo Design
Clicando no botão Preview Design
Figura 15  - Botão Preview Design 

você pode visualizar como a tela ficará em tempo de execução :
Figura 16 - Clique Preview Design

Conseguiu deixar o formulário com essa cara ?
Vamos adiante...

3) Vamos adicionar funcionalidade aos botões, mas antes é necessário saber que os eventos serão tratados utilizando java.awt.event.ActionEvent.
Então criamos funções normalmente chamadas de nomeObjetoActionPerformed .

Como o NB facilita isso ?
- Clique com o botão direito no botão Sair. Escolha Events>>Action>>actionPerformed , que significa quando o usuário clicar nesse botão.

Figura 17 - Adicionando eventos

Figura 18 - actionPerformed , usaremos muito esse evento.
2 - Clique duas vezes nesse botão que o NB já criará o código necessário e colocamos o que precisamos dentro dessa função :

    private void jBtSairActionPerformed(java.awt.event.ActionEvent evt) {
        // TODO add your handling code here:
    }

que encerraremos o programa
    private void jBtSairActionPerformed(java.awt.event.ActionEvent evt) {
        System.out.println("Encerrando programa ");
        System.exit(0);
    }

veja o que o NB gerou p/ esse evento :


       jBtSair.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/icon_sair.png"))); // NOI18N
        jBtSair.setText("Sair");
        jBtSair.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jBtSairActionPerformed(evt);
            }
        });


3) Teste o programa (isso quer dizer , aperte o F6 do teclado ou menu Run>> Project) e clique no botão Sair.
Figura 19 - Executando o Projeto 
o log do NB deve mostrar algo assim :

Sep 21, 2012 1:37:33 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@5815338: startup date [Fri Sep 21 13:37:33 BRT 2012]; root of context hierarchy
Sep 21, 2012 1:37:33 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [beans-definitions.xml]
Sep 21, 2012 1:37:34 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@36c8570c: defining beans [dataSource,jdbcTemplate,ContatoDao]; root of factory hierarchy
Sep 21, 2012 1:37:34 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Sep 21, 2012 1:37:36 PM org.springframework.jdbc.datasource.SingleConnectionDataSource initConnection
INFO: Established shared JDBC Connection: com.mysql.jdbc.JDBC4Connection@316d3536
Encerrando programa 
BUILD SUCCESSFUL (total time: 33 seconds)

4) Vamos acertar o label  - na função carregaTabela que está em nossa Classe Main vamos adicionar a linha q atualiza o campo jlbTotal ficando assim :

    private void carregaTabela(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-definitions.xml");
        ContatoDAO dao = (ContatoDAO) ctx.getBean("contatoDao");
        ContatoTableModel tableModel = new ContatoTableModel(dao.listar());
        jTable1.setModel(tableModel);
        jLbTotal.setText(tableModel.getRowCount()+"");
    }

5) Teste o programa.
o resultado deve ser esse :
Figura 21 - Execução do programa, repare o total de usuários : 2

6) Para editar e criar um novo Contato , vamos criar outro Frame de nome frameEditaContato no package springlesson06.view.contato:

I) Crie o JFrame
Figura 22 - Adicionando outro JFrame

Figura 23 - nomeie o JFrame frameEditaContato, usaremos p/ Insert e Update

II) Adicionando os campos
e nesse formulário precisamos criar campos que atendam os campos da tabela no banco:
o que precisamos adicionar nesse form ?
- Adicionar 

- 1 Panel 
- Painel com borda preta p/ nao deixar os elementos soltos na tela - jPanel1

-4 Labels
-- ícone usuario (opcional) jLbIcon
-- id  jLbId
-- nome jLbNome
-- telefone  jLbTelefone
- 3 campos texto

-- id - apenas leitura jTFId
-- nome  jTFNome
-- telefone jTFTelefone

- 2 botões
-- salvar  jBtSalvar
-- cancelar jBtCancelar


Figura 24 - Após arrastar os elementos p/ o Form

Após customizado :

Figura 25 - Após editar as propriedades dos elementos do Form e o modo Preview

Vamos ver o que o NB fez com o código lá dentro da função initComponents:

private javax.swing.JButton jBtCancelar;
    private javax.swing.JButton jBtSalvar;
    private javax.swing.JLabel jLbIcon;
    private javax.swing.JLabel jLbId;
    private javax.swing.JLabel jLbNome;
    private javax.swing.JLabel jLbTelefone;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JTextField jTFId;
    private javax.swing.JTextField jTFNome;
    private javax.swing.JTextField jTFTelefone;

        jPanel1 = new javax.swing.JPanel();
        jTFTelefone = new javax.swing.JTextField();
        jLbTelefone = new javax.swing.JLabel();
        jLbNome = new javax.swing.JLabel();
        jTFNome = new javax.swing.JTextField();
        jTFId = new javax.swing.JTextField();
        jLbId = new javax.swing.JLabel();
        jBtSalvar = new javax.swing.JButton();
        jBtCancelar = new javax.swing.JButton();
        jLbIcon = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Contato");
        setResizable(false);

        jPanel1.setBorder(new javax.swing.border.LineBorder(new java.awt.Color(0, 0, 0), 1, true));

        jLbTelefone.setText("TELEFONE");

        jLbNome.setText("NOME");
        jTFId.setEnabled(false);
        jLbId.setText("ID");

        jBtSalvar.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/icon_save.png"))); // NOI18N
        jBtSalvar.setText("Salvar");

        jBtCancelar.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/icon_cancel_menor.png"))); // NOI18N
        jBtCancelar.setText("Cancelar");

        jLbIcon.setIcon(new javax.swing.ImageIcon(getClass().getResource("/springlesson06/images/user.png"))); // NOI18N

e as novas imagens :
cancelar 

salvar 

III) Vamos adicionar a função que carrega o contato para o formulário logo abaixo  do contrutor frameEditaContato:

  
public frameEditaContato() {
        initComponents();
}
public void carregaContato(Contato C){
        jTFId.setText(C.getId()+"");
        jTFNome.setText(C.getNome());
        jTFTelefone.setText(C.getTelefone());
    }



7) Agora precisaremos alterar nosso ContatoTableModel p/ retornar um object Contato para passar p/ o carregaContato que acabamos de fazer então em nossa classe springlesson06.model.ContatoTableModel vamos adicionar a seguinte função :


  
  public Contato getObjectAtRow(int row){
        return lista.get(row); 
    }
    


8) Vamos fazer o botão editar da lista de contatos abrir o frame frameEditaContato passando o objeto Contato :
Clique duas vezes no botão jBtEditar


 
private void jBtEditarActionPerformed(java.awt.event.ActionEvent evt) {
        Contato C = tableModel //???????
    }


e agora ?? não consigo acessar o objeto criado na função carregaTabela. Como resolve isso ?

R1 : Criamos um objeto ContatoTableModel privado nessa classe p/ conter o tableModel , encapsulamos e utilizamos Get/Set p/ acessá-lo;


R2 : Criamos um objeto Contato  privado nessa classe p/ conter o Contato Selecionado , encapsulamos e utilizamos Get/Set p/ acessá-lo;

R3: etc... 

Pensanndo.. o que é melhor ? o que precisaremos acessar ?
Vamos pensar na Resposta 1. alguns trechos do código modificado :
Resposta 1 do passo 08
//declaracao
public class frameListaContatos extends javax.swing.JFrame {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-definitions.xml");
        ContatoDAO dao = (ContatoDAO) ctx.getBean("contatoDao");
        ContatoTableModel tableModel = new ContatoTableModel(dao.listar());
//construtor
public frameListaContatos() {
        initComponents();
        carregaTabela();
    }
    private void carregaTabela(){
        jTable1.setModel(this.tableModel);
        jLbTotal.setText(this.tableModel.getRowCount()+"");
    }
//botao sair
 private void jBtSairActionPerformed(java.awt.event.ActionEvent evt) {
        System.out.println("Encerrando programa ");
        System.exit(0);
    }
//botao editar
    private void jBtEditarActionPerformed(java.awt.event.ActionEvent evt) {
        Contato C = this.tableModel.getObjectAtRow(jTable1.getSelectedRow());
        frameEditaContato fec = new frameEditaContato();
        fec.carregaContato(C);
        fec.setVisible(true);
    }

8) ainda : R1 Teste o programa.
Ao carregar o form com o titulo Meus Contatos , Selecione algum contato e clique no botao jBtEditar.
Funcionou ?? Boa , então : 
- Verifique que o programa permite abrir várias janelas do mesmo registro. Será que isso é bom ?? 
- Essa implementação não foi nada elegante. Cadê o encapsulamento

Figura 26 - Resposta 1 do passo 08

8) ainda : do jeito q está vamos implementar a Resposta 2 :
Resposta 2 do passo 08
I) Vamos adicionar a jTable um evento
Figura 27 -Adicionando um evento de clique a nossa jTable
e o o código modificado

public class frameListaContatos extends javax.swing.JFrame {

    ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-definitions.xml");
    ContatoDAO dao = (ContatoDAO) ctx.getBean("contatoDao");
    ContatoTableModel tableModel = new ContatoTableModel(dao.listar());
    Contato contatoAtivo;

    /**
     * Creates new form frameListaContatos
     */
    public frameListaContatos() {
        initComponents();
        carregaTabela();
    }

    private void carregaTabela() {
        jTable1.setModel(this.tableModel);
        jLbTotal.setText(this.tableModel.getRowCount() + "");
    }

    private void carregaContato() {
        Contato C = this.tableModel.getObjectAtRow(jTable1.getSelectedRow());
        this.contatoAtivo = C;
        System.out.println("Contato Ativo na memória: " + C.getNome());
    }

    private void abreFrameEdit(Contato C) {
        frameEditaContato fec = new frameEditaContato();
        fec.carregaContato(C);
        fec.setVisible(true);
    }

    private void sair() {
        System.out.println("Encerrando programa ");
        System.exit(0);
    }

/// NB CODE
 private void jBtSairActionPerformed(java.awt.event.ActionEvent evt) {
        sair();
    }

    private void jBtEditarActionPerformed(java.awt.event.ActionEvent evt) {
        abreFrameEdit(this.contatoAtivo);
    }

    private void jTable1MouseClicked(java.awt.event.MouseEvent evt) {
        carregaContato();
    }
    public static void main(String args[]) {...}
 // Variables declaration - do not modify
    private javax.swing.JButton jBtEditar;
    private javax.swing.JButton jBtNovo;
    private javax.swing.JButton jBtRemover;
    private javax.swing.JButton jBtSair;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLbTotal;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;
    // End of variables declaration

E o que fizemos aqui ? Teste o programa.
Figura 28 - Teste do Programa , veja o Log do seu NB

Quando o usuário selecionar alguma linha , será executado a função  jTable1MouseClicked tratado peloListener criado em

   jTable1.addMouseListener(new java.awt.event.MouseAdapter() {
            public void mouseClicked(java.awt.event.MouseEvent evt) {
                jTable1MouseClicked(evt);
            }
        });

E com isso colocamos o objeto Contato na memória e podemos utilizá-lo para mais coisa se for preciso e  o botão edit lê o contatoAtivo.

Opa...  e se clicar direto no botao editar, sem selecionar uma linha ??
Figura 29 - Erro causado por clicar no botao editar sem selecionar um contato, perceba o java.lang.NullPointerException

Yes... Erro
Em vez de colocar isso dentro de um Try/catch , nesse caso prefiro evitar que o erro aconteça da seguinte maneira :

II) Vamos modificar a função abreFrameEdit que precisa de um contato válido


      private void abreFrameEdit(Contato C) {
        if (C==null){
            JOptionPane.showMessageDialog(null,"Selecione um Contato");
        }else {
        frameEditaContato fec = new frameEditaContato();
        fec.carregaContato(C);
        fec.setVisible(true);
        }
    }

e.. Teste o programa.
Figura 30 - Prevenindo o erro da função abreFrameEdit


9) Vamos modificar um pouco como isso está escrito: , Refactor>>Encapsulate Fields
Figura 31- Refatorando e Encapsulando : Após selecionar e aplicar , selecione as 5 que  utilizei

public final class frameListaContatos extends javax.swing.JFrame {

    private ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-definitions.xml");
    private ContatoDAO dao = (ContatoDAO) ctx.getBean("contatoDao");
    private ContatoTableModel tableModel = new ContatoTableModel(dao.listar());
    private Contato contatoAtivo;

     public frameListaContatos() {
        initComponents();
        carregaTabela();
    }
 private void jBtSairActionPerformed(java.awt.event.ActionEvent evt) {
        sair();
    }

    private void jBtEditarActionPerformed(java.awt.event.ActionEvent evt) {
        abreFrameEdit(this.getContatoAtivo());
    }

    private void jTable1MouseClicked(java.awt.event.MouseEvent evt) {
        carregaContato();
    }
//NB CODE
// End of variables declaration

    public ApplicationContext getCtx() {
        return ctx;
    }

    public ContatoDAO getDao() {
        return dao;
    }

    public ContatoTableModel getTableModel() {
        return tableModel;
    }

    public Contato getContatoAtivo() {
        return contatoAtivo;
    }

    public void setContatoAtivo(Contato contatoAtivo) {
        this.contatoAtivo = contatoAtivo;
    }

    private void carregaTabela() {
        jTable1.setModel(this.getTableModel());
        jLbTotal.setText(this.getTableModel().getRowCount() + "");
    }

    private void carregaContato() {
        Contato C = this.getTableModel().getObjectAtRow(jTable1.getSelectedRow());
        this.setContatoAtivo(C);
        System.out.println("Contato Ativo na memória: " + C.getNome());
    }

    private void abreFrameEdit(Contato C) {
        if (C == null) {
            JOptionPane.showMessageDialog(null, "Selecione um Contato");
        } else {
            frameEditaContato fec = new frameEditaContato();
            fec.carregaContato(C);
            fec.setVisible(true);
        }
    }

    private void sair() {
        System.out.println("Encerrando programa ");
        System.exit(0);
    }


e.. Teste o programa.


10) O lance de abrir várias janelas:
Não sou muito fã disso, pois sujeito o usuário abrir várias janelas sem perceber e fazer uma bagunça com os dados.

I) mais uma modificação : se fechar a janela de edição fecha o programa, e não apenas a janela.
Para resolver isso abra o frameEditaContato , selecione o frame e nas propriedades altere a propriedade defaultCloseOperation para DISPOSE . 
Figura 32 - Fechando apenas a janela filha


II) Permitindo apenas uma janela de edição de Contato.
Vamos modificar a classe frameEditaContato p/ receber como construtor o frame pai.

public class frameEditaContato extends javax.swing.JFrame {

    /**
     * Creates new form frameEditaContato
     */
    frameListaContatos flC;
   
//    public frameEditaContato() {
//        initComponents();
//    }
    
    //novo construtor
    public frameEditaContato(frameListaContatos flC) {
        initComponents();
        this.flC = flC;
    }
    // métoodo obrigatório
    private frameEditaContato() {
//        throw new UnsupportedOperationException("Not yet implemented");
    }
    
    public void carregaContato(Contato C){
        jTFId.setText(C.getId()+"");
        jTFNome.setText(C.getNome());
        jTFTelefone.setText(C.getTelefone());
    }
//... etc

E para chamar esse frame em nossa Lista criamos uma variável de classse privada do tipo frameEditaContato chamada janelaEdicao e passamos um novo objeto passando o construtor sem parâmetros()  na função abreFrameEdit q recebe o contato Ativo, se estiver carregado.
modificação :

public final class frameListaContatos extends javax.swing.JFrame {

    private ApplicationContext ctx = new ClassPathXmlApplicationContext("beans-definitions.xml");
    private ContatoDAO dao = (ContatoDAO) ctx.getBean("contatoDao");
    private ContatoTableModel tableModel = new ContatoTableModel(dao.listar());
    private Contato contatoAtivo;
    private frameEditaContato janelaEdicao;
  
     public frameEditaContato getJanelaEdicao() {
        return janelaEdicao;
    }

    public void setJanelaEdicao(frameEditaContato janelaEdicao) {
        this.janelaEdicao = janelaEdicao;
    }

    /**
     * Creates new form frameListaContatos
     */
    public frameListaContatos() {
        initComponents();
        carregaTabela();
        setJanelaEdicao(new frameEditaContato(this));
    }

// e na função usa o objeto janelaEdicao pelo método getJanelaEdicao();
  private void abreFrameEdit(Contato C) {
        if (C == null) {
            JOptionPane.showMessageDialog(null, "Selecione um Contato");
        } else {
            frameEditaContato fec = getJanelaEdicao();
            fec.carregaContato(C);
            fec.setVisible(true);
        }
    }
}

e.. Teste o programa.

OBS, Como estamos utilizando um objeto Contato, observe em seu Mysql , que podemos abrir várias janelas sem aumentar as conexões :
Figura 33 - Usando o MysqlWorkBench para monitorar as conexões 
OBS : Pelo MysqlWORKBench é possível realizar diversas tarefas administrativas, aproveite a conexão criada e a utilize na seção Server Administration

Figura 34 - Opção Server Administration do MWB, vale a pena conferir


11) Salvando os registros (UPDATE) : CRUD

I) Vamos adicionar uma função salvar em nosso contatoDAO springlesson06.dao.ContatoDAO

public void salvar(Contato C){
        String sql=null;
        List<Object> params = new ArrayList<Object>();
        
        if ( C.getId()==0 ){
        // novo contato  
            sql="INSERT INTO `springlessons`.`contato`"
            + "(`id`,`nome`,`telefone`) VALUES (?,?,?)";
            params.add(0);
            params.add(C.getNome());
            params.add(C.getTelefone());
      
        }else {
        //atualiza existente
            sql="UPDATE `springlessons`.`contato` "
            + " SET `nome` = ?,"
            + " `telefone` = ?"
            + " WHERE `id` = ? ";
            params.add(C.getNome());
            params.add(C.getTelefone());
            params.add(C.getId());
        }
// se der erros descomente abaixo e veja se o contato chegou aqui, mais p/ frente veremos q isso é uma pegadinha 
//System.out.println("SQL:" + sql);
        t.update(sql, params.toArray());
    }


II) Vamos modificar o construtor do objeto Contato inicializando ele com id=0, devido a verificação no método salvar, abra a classe Contato springlesson06.model.Contato
dica : com a classe aberta aperte ctrl + espaço e escolha a 1ª opção generate

Figura 35 - Classe Contato, utilizando os atalhos do NB p/ gerar as funções

ficando assim :

package springlesson06.model;

public class Contato {

    private int id;
    private String nome;
    private String telefone;

    public Contato() {
        this.id = 0;
    }


   public Contato(int id, String nome, String telefone) {
        this.id = id;
        this.nome = nome;
        this.telefone = telefone;
    }
    public int getId() {
        return id;
    }

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

    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;
    }
}

III) no frameEditaContato que não conhece o DAO, precisamos informar onde serão salvos os dados.
Para isso , vamos adicionar um objeto dao do tipo ContatoDAO `a classe, e adicionar um objeto desse tipo no construtor do Frame springlesson06.view.contato.frameEditaContato:


package springlesson06.view.contato;

import springlesson06.dao.ContatoDAO;
import springlesson06.model.Contato;

public class frameEditaContato extends javax.swing.JFrame {

    frameListaContatos flC;
    ContatoDAO dao ;
    
    //novo construtor
    public frameEditaContato(frameListaContatos flC,ContatoDAO daoInformed) {
        initComponents();
        this.flC = flC;
        this.dao = daoInformed;
    }
    // métoodo obrigatório usado em Run
    private frameEditaContato() { }
    
    public void carregaContato(Contato C){
        jTFId.setText(C.getId()+"");
        jTFNome.setText(C.getNome());
        jTFTelefone.setText(C.getTelefone());
    }
//NB CODE
 public static void main(String args[]) {
        
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new frameEditaContato().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JButton jBtCancelar;
    private javax.swing.JButton jBtSalvar;
    private javax.swing.JLabel jLbIcon;
    private javax.swing.JLabel jLbId;
    private javax.swing.JLabel jLbNome;
    private javax.swing.JLabel jLbTelefone;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JTextField jTFId;
    private javax.swing.JTextField jTFNome;
    private javax.swing.JTextField jTFTelefone;
    // End of variables declaration

  
}

Tirando a parte do NB se o seu codigo estiver como o acima, com dao criado , vamos fazer salvar os dados

IV) Agora em modo Design clique duas vezes no botão salvar : vamos editar a função jBtSalvarActionPerformed

Dica : dentro da função escreva salvarContato() (que ainda não existe) e veja a sugestão do NB, aceite;
Figura 36 - Mais uma vez utilize o NB p/ gerar a função. Pessoalmente acho q usar esses recursos diminuem a margem de erros e nomes que não sejam evidentes

Antes de mostrar a função salvarContato, algumas explicações :
Acho mais fácil criar + uma variavel do tipo Contato e encapsular as que ja temos, e criar um novo Contato no construtor do frame, que já terá um id=0 e não precisamos ficar convertendo o valor do id p/ String a todo momento:, resumindo modifiquei a classe , segue ela abaixo

package springlesson06.view.contato;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import springlesson06.dao.ContatoDAO;
import springlesson06.model.Contato;

public class frameEditaContato extends javax.swing.JFrame {

    private frameListaContatos flC;
    private ContatoDAO dao;
    private Contato contato;

    //novo construtor
    public frameEditaContato(frameListaContatos flC, ContatoDAO daoInformed) {
        initComponents();
        this.flC = flC;
        this.dao = daoInformed;
        this.contato = new Contato(); // que já possui um id=0
    }
    // método obrigatório

    private frameEditaContato() {
    }

    public void carregaContato(Contato C) {
        this.setContato(C);
        jTFId.setText(C.getId() + "");
        jTFNome.setText(C.getNome());
        jTFTelefone.setText(C.getTelefone());
    }

    private void salvarContato() {

        Contato C = new Contato();
        //Solução 1 : validação manual 
        String preMessage = "Verifique os seguintes erros:\n";
        String message = "";

        if ("".equals(jTFNome.getText())) {
            message += "- Nome deve ser preenchido \n";
        }
       
        if (message.length() == 0) {
            if ("".equals(jTFId.getText())) {
                C.setId(0);
            } else {

                C.setId(Integer.parseInt(jTFId.getText()));
                C.setNome(jTFNome.getText());
                C.setTelefone(jTFTelefone.getText());
            }
            this.contato = C;
            try {
                this.getDao().salvar(this.getContato());
                System.out.println("Salvando Contato:" + C.getNome());
                this.getFlC().atualizaGrade();
                this.dispose();
            } catch (java.lang.NullPointerException npe) {
                System.out.println("Erro ao Salvar o  Contato.");
                Logger.getLogger(frameEditaContato.class.getName()).log(Level.SEVERE, null, npe);
            }
        } else {
            JOptionPane.showMessageDialog(null, preMessage + message);
        }
    }
//NB CODE
 public static void main(String args[]) {
        
        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new frameEditaContato().setVisible(true);
            }
        });
    }

    private void jBtSalvarActionPerformed(java.awt.event.ActionEvent evt) {
        salvarContato();
    }
    public static void main(String args[]) {
       

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new frameEditaContato().setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify
    private javax.swing.JButton jBtCancelar;
    private javax.swing.JButton jBtSalvar;
    private javax.swing.JLabel jLbIcon;
    private javax.swing.JLabel jLbId;
    private javax.swing.JLabel jLbNome;
    private javax.swing.JLabel jLbTelefone;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JTextField jTFId;
    private javax.swing.JTextField jTFNome;
    private javax.swing.JTextField jTFTelefone;
    // End of variables declaration
public ContatoDAO getDao() {
        return dao;
    }

    public Contato getContato() {
        return contato;
    }

    public void setContato(Contato contato) {
        this.contato = contato;
    }
}
Após as alterações ,

12) Teste o programa.
Selecione algum registro , clique em editar , altere o nome e clique em salvar,  o resultado deve ser esse
Figura 37 - Observe o Log, mas o registro foi salvo. 
hummm , o que aconteceu com o sql ?? será que atualizou ?
R: Sim.
O sql é esse mesmo, lembre-se que no dao usamos t.update(sql, params.toArray()); e os valores estão no List<Object> params.
Feche o programa e abra ele de novo que mostrará o registro atualizado.

13) Fechando a tela de edição e atualizando a Grade
I) Na classe ContatoTableModel vamos implementar uma função que atualize essa grade :

  
public void atualizaLista(List<Contato> lista) {
    this.lista = lista;
    }


II) Na classe frameListaContatos vamos criar uma função que chama essa q atualiza a lista p/ poder chamar da janela de edição após salvar o Registro

   public void atualizaGrade(){
        this.getTableModel().atualizaLista(this.getDao().listar());
        jTable1.updateUI();
        
    }

III) Na classe frameEditaContato após salvar o Registro, chame a função da janela pai atualizaGrade e em seguida fechar , e gere a funcao getFlC() para acessar frame Pai
 
     public frameListaContatos getFlC() {
        return flC;
    }

 E a função salvar ficou assim 


 
private void salvarContato() {

        Contato C = new Contato();
        //Solução 1 : validação manual 
        String preMessage = "Verifique os seguintes erros:\n";
        String message = "";

        if (!"".equals(jTFNome.getText())) {
            message += "- Nome deve ser preenchido \n";
        }

        if (message.length() > 0) {
            if ("".equals(jTFId.getText())) {
                C.setId(0);
            } else {

                C.setId(Integer.parseInt(jTFId.getText()));
                C.setNome(jTFNome.getText());
                C.setTelefone(jTFTelefone.getText());
            }
            this.contato = C;
            try {
                this.getDao().salvar(this.getContato());
                System.out.println("Salvando Contato:" + C.getNome());
                this.getFlC().atualizaGrade();
                this.dispose();
            } catch (java.lang.NullPointerException npe) {
                System.out.println("Erro ao Salvar o  Contato.");
                Logger.getLogger(frameEditaContato.class.getName()).log(Level.SEVERE, null, npe);
            }
        } else {
            JOptionPane.showMessageDialog(null, preMessage + message);
        }



13 )Teste o programa.

I) Edite um contato e salve veja se está tudo funcionando, após salvar a tela de edição deve fechar e a Grade de baixo atualizar. :
Meu Output :

run:
Sep 21, 2012 8:37:06 PM org.springframework.context.support.AbstractApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@48ff2413: startup date [Fri Sep 21 20:37:06 BRT 2012]; root of context hierarchy
Sep 21, 2012 8:37:06 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [beans-definitions.xml]
Sep 21, 2012 8:37:07 PM org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
INFO: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@469a9b65: defining beans [dataSource,jdbcTemplate,ContatoDao]; root of factory hierarchy
Sep 21, 2012 8:37:07 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Sep 21, 2012 8:37:08 PM org.springframework.jdbc.datasource.SingleConnectionDataSource initConnection
INFO: Established shared JDBC Connection: com.mysql.jdbc.JDBC4Connection@52b57e9a
Contato Ativo na memória: Cliente 2 renomeado
SQL:UPDATE `springlessons`.`contato`  SET `nome` = ?, `telefone` = ? WHERE `id` = ? 
Salvando Contato:Cliente 2 renomeado de novo
Encerrando programa 
BUILD SUCCESSFUL (total time: 1 minute 59 seconds)
14) Salvando novos registros : esse ficou fácil

I) Na classe frameListaContatos vamos criar uma função que chama o cadastro
Clique duas vezes no botao novo :


    
private void jBtNovoActionPerformed(java.awt.event.ActionEvent evt) {
        abreFrameEdit(new Contato());
    }

II) E mande atualizar o label Total apos as inserções / atualizações:
public void atualizaGrade(){
        this.getTableModel().atualizaLista(this.getDao().listar());
        jTable1.updateUI();
        jLbTotal.setText(this.getTableModel().getRowCount() + "");
        
    }


Teste o programa. Veja se o label total esta atualizando após inserir registros.

15) Removendo registros :(DELETE) : CRUD

I) Vamos fazer função delete no contatoDAO e a ação do botão Remover
springlesson06.dao.ContatoDAO , adicione a função abaixo

    
public void apagar(Contato C) {
        JdbcTemplate t = getJdbcTemplate();
        String sql = null;
        List<Object> params = new ArrayList<Object>();
        sql = "DELETE FROM `springlessons`.`contato` "
                + " WHERE `id` = ? ";
        params.add(C.getId());
//        System.out.println("SQL:" + sql);
        t.update(sql, params.toArray());
    }



II) Na classe frameListaContatos vamos criar uma função que chama o cadastro
Clique duas vezes no botao remover :

     private void jBtRemoverActionPerformed(java.awt.event.ActionEvent evt) {
        removeContato();
    }

e escreva a função removeContato, observe que estou utilizando o objeto Logger daqui p/ frente : (p/ mostrar os erros, o NB usa ele por default)


  
  private void removeContato(Contato C) {
        if (C != null) {
            int apaga = JOptionPane.showConfirmDialog(null, "Deseja Apagar o contato  " + C.getNome(),
                    "Sistema informa:", JOptionPane.YES_NO_OPTION);
            if (apaga == JOptionPane.YES_OPTION) {

                try {
                    System.out.println("Removendo contato do banco: " + C.getNome());
                    this.getDao().apagar(C);
                    System.out.println("Removendo contato Ativo da memória: " + C.getNome());
                    setContatoAtivo(null);
                    atualizaGrade();
                } catch (Exception ex) {
                    Logger.getLogger(frameListaContatos.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        } else {
            JOptionPane.showMessageDialog(null, "Selecione um Contato");
        }
    }

16) Teste o programa. 
o resultado agora ao clicar no remover deve ser uma confirmação, escolha sim e remova o registro.
Figura 38 - Confirmação da execução através de uma Caixa de Diálogo

Figura 39 - Agora o contato é removido se escolhido a opção Sim, e a grade atualizada.



Download do projeto NB
SpringLesson06-v1.zip

Corrigido a função de salvar Contato q tinha um bug :
SpringLesson06-V2.zip


Experimente executar o Profile e compare os resultados.

E assim concluímos nossa lição 06.

Resumo : o que foi feito ?
- Implementamos um CRUD em Java   utilizando Swing e  Spring
- Vimos como controlar uma Janela filha e chamar metodos  e passar objetos entre elas
- Utilizamos nosso ContatoTableModel p/ controlar os dados da Grade
- Vimos um pouco de como o NB gera os códigos (que são protegidos no ambiente de código, mas todas as suas propriedades podem ser alterados pelo GUI)
- Vimos mais faciliddes do NB



Dúvidas, Contribuições, Sugestões? Poste..
Grato,
Até a próxima.

Nenhum comentário: