ResultSetTableModel

10 respostas
lucas_guj

Pessoal, já cansei de ver aqui sobre: Não Use DefaultTableModel. É o seguinte, minha dúvida é bem básica, só para ter certeza mesmo. Estou estudando pelo livro Java como programar 8° edição e no cap. sobre banco de dados, onde ele explica a JTable, AbstractTableModel eu não o vi usar esse DefaultTableModel. Ele cria uma classe que estende AbstractTableModel, em momento nenhum vi o DefaultTableModel. A minha pergunta é: o livro já está ensinando da melhor maneira? É correto isso que ele está fazendo? Isso é a minha própria implementação do AbstractTableModel e está no padrão MVC ainda por cima?

Vou até digitar o enunciado aqui para vocês terem noção.

[color=blue]28.8.2 Consultando o banco de dados books[/color]

O próximo exemplo( figuras 28.25 e 28,28 ) permite ao usuário inserir qualquer consulta no programa. O exemplo exibe o resultado de uma consulta em uma JTable, usando um objeto TableModel para fornecer os dados de ResultSet para a JTable. Um JTable é um componente Swing que pode ser vinculado a um banco de dados para exibir os resultados de uma consulta. A classe ResultSetTableModel( Figura 28.25 ) realiza a conexão com o banco de dados via uma TableModel e mantém o ResultSet. A classe DisplayQueryResults( Figura 28.28 ) cria a GUI e especifica uma instância da classe ResultSetTableModel para fornecer dados a JTable.

[color=blue]Classe ResultSetTableModel[/color]

A classe ResultSetTableModel estende a classe AbstractTableModel, que implementa a interface TableModel. ResultSetTableModel sobrescreve os métodos TableModel getColumnClass, getColumnCount, getColumnName, getRowCount e getValueAt. As implementações padrão dos métodos TableModel isCellEditable e setValueAt( fornecidas por AbstractTableModel ) não são sobrescritas, porque esse exemplo não suporta a edição de células JTable. As implemetações padrão dos métodos addTableModelListener e removeTableModelListener( fornecidos por AbstractTableModel ) não são sobrescritas, porque as implementações desses métodos em AbstractTableModel adicionam e removem adequadamente os ouvintes( listeners ) de evento.

[color=red]
Além dessa dúvida, tenho outra, que é, se na interface temos que sobrescrever todos os métodos nas implementações dela, menos naquelas que são Adapter que a gente pode escolher o que implementar, mesmo com a explicação do livro, eu não entendi porque ele não sobrescreveu os métodos: isCellEditable e setValueAt e addTableModelListener e removeTableModelListener. Alguém poderia me explicar com OUTRAS PALAVRAS( Não explique exatamente igual ao livro senão vou continuar a ficar sem entender por favor ). Obrigado[/color]

Aqui segue o código da classe do exemplo do livro:

/* #######################################################################################################################################
 * Nome do Aplicativo:                                                                                                                  ##
 * Nome do Arquivo: ResultSetTableModel.java                                                                                            ##
 * Nome do Projeto:                                                                                                                     ##
 * Plataforma:                                                                                                                          ##
 * Função do Aplicativo: Um Table Model que fornece dados ResultSet a uma JTable. Linhas e colunas do ResultSet sao contadas a partir   ##
 * de 1 linhas e colunas. JTable são contadas a partir de 0. Ao processar linhas ou colunas de ResultSet para utilização em uma JTable, ##
 * é necessário adicionar 1 ao número de linha ou coluna para manipular a coluna apropriada de ResultSet( isto é, coluna 0 de JTable é  ##
 * a coluna de ResultSet 1 e a linha de JTable 0 é a linha de ResultSet 1 ).                                                            ##
 * @author Lucas Santos                                                                                                                 ##
 * Criado em 17/06/2012, 12:37:18                                                                                                       ##
 * #######################################################################################################################################
 */
import java.sql.Connection;
import java.sql.Statement;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import javax.swing.table.AbstractTableModel;

public class ResultSetTableModel extends AbstractTableModel {
    private Connection connection;
    private Statement statement;
    private ResultSet resultSet;
    private ResultSetMetaData metaDados;
    private int numeroDeLinhas;

    /** * Monitora o status da conexão de banco de dados */
    private boolean conectadoAoDB = false;

    /** * Construtor inicializa resultSet e obtém seu objeto de metaDados; Determina o número de linhas */
    public ResultSetTableModel( String url, String username, String password, String query ) throws SQLException {
        /** * Conecta-se ao banco de dados */
        connection = DriverManager.getConnection( url, username, password );

        /** * Cria Statement para enviar instruções de SQL ao DB */
        statement = connection.createStatement( ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY );

        /** * Atualiza status de conexão de banco de dados */
        conectadoAoDB = true;

        /** * Configura consulta e a executa */
        setQuery( query );
    } // fim do Construtor

    /** * Obtém a classe que representa o tipo de coluna */
    public Class getColumnClass( int coluna ) throws IllegalStateException {
        /** * Assegura que o banco de dados conexão está disponível */
        if( !conectadoAoDB ) {
            throw new IllegalStateException( "Não está conectado ao Banco de Dados" );
        }

        /** * Determina a classe Java de coluna */
        try {
            String nomeClasse = metaDados.getColumnClassName( coluna + 1 );

            /** * Retorna objeto Class que representa a nomeClasse */
            return Class.forName( nomeClasse );
        }
        catch( Exception e ) {
            e.printStackTrace();
        }

        /** * Se ocorrerem os problemas acima, assume tipo Object */
        return Object.class;
    } // fim do método getColumnClass

    /** * Obtém o número de colunas em ResultSet */
    public int getColumnCount() throws IllegalStateException {
        /** * Assegura que o banco de dados conexão está disponível */
        if( !conectadoAoDB ) {
            throw new IllegalStateException( "Não está conectado ao Banco de Dados" );
        }

        try {
            return metaDados.getColumnCount();
        }
        catch( SQLException sqlE ) {
            sqlE.printStackTrace();
        }

        /** * Se ocorrerem os problemas acima, retorna 0 para o número de colunas */
        return 0;
    } // fim do método getColumnCount

    /** * Obtém o nome de uma coluna particular em ResultSet */
    public String getColumnName( int column ) throws IllegalStateException {
        /** * Assegura que o banco de dados conexão está disponível */
        if( !conectadoAoDB ) {
            throw new IllegalStateException( "Não está conectado ao Banco de Dados" );
        }

        try {
            return metaDados.getColumnName( column + 1 );
        }
        catch( SQLException sqlE ) {
            sqlE.printStackTrace();
        }

        /** * Se ocorrerem os problemas acima, retorna string vazia para nome de coluna */
        return "";
    } // fim do método getColumnName

    /** * Retorna o número de linhas em ResultSet */
    public int getRowCount() throws IllegalStateException {
        /** * Assegura que o banco de dados conexão está disponível */
        if( !conectadoAoDB ) {
            throw new IllegalStateException( "Não está conectado ao Banco de Dados" );
        }

        return numeroDeLinhas;
    } // fim do método getRowCount

    /** * Obtém o valor na linha e coluna especificadas */
    public Object getValueAt( int row, int column ) throws IllegalStateException {
        /** * Assegura que o banco de dados conexão está disponível */
        if( !conectadoAoDB ) {
            throw new IllegalStateException( "Não está conectado ao Banco de Dados" );
        }

        /** * Obtém o valor na linha e na coluna especificadas do ResultSet */
        try {
            resultSet.absolute( row + 1);
            return resultSet.getObject( column + 1 );
        }
        catch( SQLException sqlE ) {
            sqlE.printStackTrace();
        }

        /** * Se ocorrerem os problemas acima, retorna objeto string vazio */
        return "";
    } // fim do método getValueAt

    /** * Configura a nova string de consulta de banco de dados */
    public void setQuery( String query ) throws SQLException, IllegalStateException {
        /** * Assegura que o banco de dados conexão está disponível */
        if( !conectadoAoDB ) {
            throw new IllegalStateException( "Não está conectado ao Banco de Dados" );
        }

        /** * Especifica a consulta e a executa */
        resultSet = statement.executeQuery( query );

        /** * Obtém os metadados para o ResultSet */
        metaDados = resultSet.getMetaData();

        /** * Determina o número de linhas em ResultSet */
        resultSet.last(); // move para a última linha
        numeroDeLinhas = resultSet.getRow(); // obtém o número de linha

        /** * Notifica a JTable de que modelo foi alterado */
        fireTableStructureChanged();
    } // fim do método setQuery

    /** * Fecha Statemente e Connection */
    public void disconectaDoDB() {
        if( conectadoAoDB ) {
            /** * Fecha Statement e Connection */
            try {
                resultSet.close();
                statement.close();
                connection.close();
            }
            catch( SQLException sqlE ) {
                sqlE.printStackTrace();
            }
            finally { // atualiza status de conexão de banco de dados
                conectadoAoDB = false;
            }
        }
    }
}
/* ####################################################
 * Nome do Aplicativo: Tabela
 * Nome do Arquivo: ExibeResultadoConsultas.java
 * Nome do Projeto: Consutlando Banco de Dados Books
 * Plataforma: Windows, Linux, Mac
 * Função do Aplicativo: Exibe o conteúdo da tabela Authors no banco de dados de livros
 * @author Lucas Santos
 * Criado em 17/06/2012, 23:06:02
 * ####################################################
 */
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.sql.SQLException;
import java.util.regex.PatternSyntaxException;
import javax.swing.*;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;

public class ExibeResultadoConsultas extends JFrame {
    /** * URL de banco de dados, nome de usuário e senha */
    static final String DATABASE_URL = "jdbc:mysql://localhost/books";
    static final String USERNAME     = "root";
    static final String PASSWORD     = "";

    /** * A consulta padrão recupera todos os dados da tabela Authors */
    static final String DEFAULT_QUERY = "SELECT * FROM Authors";

    private ResultSetTableModel tableModel;
    private JTextArea queryArea;

    /** * Cria o ResultSetTableModel e GUI */
    public ExibeResultadoConsultas() {
        super( "Exibindo Resultado de Consultas" );

        /** * Cria o ResultSetTableModel e exibe a tabela de banco de dados */
        try {
            /** * Cria o TableModel para resultados da consulta SELECT * FROM Authors */
            tableModel = new ResultSetTableModel( DATABASE_URL, USERNAME, PASSWORD, DEFAULT_QUERY );

            /** * Configura JTextArea em que o usuário digita consultas */
            queryArea = new JTextArea( DEFAULT_QUERY, 3, 100 );
            queryArea.setWrapStyleWord( true );
            queryArea.setLineWrap( true );

            JScrollPane scrollPane = new JScrollPane( queryArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER );

            /** * Configura o JButton para enviar consulta */
            JButton botaoEnviar = new JButton( "Enviar Consulta" );

            /** * Cria o Box para gerenciar o posicionamento da queryArea e do botaoEnviar na GUI */
            Box boxNorte = Box.createHorizontalBox();
            boxNorte.add( scrollPane );
            boxNorte.add( botaoEnviar );

            /** * Cria JTAble com base no tableModel */
            JTable resultTable = new JTable( tableModel );

            JLabel lblFiltro = new JLabel( "Filtros:" );
            final JTextField textoFiltro = new JTextField();
            JButton botaoFiltro = new JButton( "Aplicar Filtro" );
            Box boxSul = Box.createHorizontalBox();

            boxSul.add( lblFiltro );
            boxSul.add( textoFiltro );
            boxSul.add( botaoFiltro );

            /** * Posiciona os componentes GUI no painel de conteúdo */
            add( boxNorte, BorderLayout.NORTH );
            add( new JScrollPane( resultTable ), BorderLayout.CENTER );
            add( boxSul, BorderLayout.SOUTH );

            /** * Cria evento ouvinte para botaoEnviar */
            botaoEnviar.addActionListener( new ActionListener() {
                @Override
                public void actionPerformed( ActionEvent e ) {
                    /** * Passa consulta para modelo de tabela */
                    try {
                        /** * Realiza uma nova consulta */
                        tableModel.setQuery( queryArea.getText() );
                    }
                    catch( SQLException sqlE ) {
                        JOptionPane.showMessageDialog( null, sqlE.getMessage() , "Erro no Banco de Dados", JOptionPane.ERROR_MESSAGE );

                        /** * Tenta recuperar a partir da consulta de usuário inválida executando consulta padrão */
                        try {
                            tableModel.setQuery( DEFAULT_QUERY );
                            queryArea.setText( DEFAULT_QUERY );
                        }
                        catch( SQLException sqlE2 ) {
                            JOptionPane.showMessageDialog( null, sqlE2.getMessage() , "Erro no Banco de Dados", JOptionPane.ERROR_MESSAGE );

                            /** * Assegura que a conexão d ebanco de dados está fechada */
                            tableModel.disconectaDoDB();

                            System.exit( 1 ); // termina o aplicativo
                        } // fim do catch interno
                    } // fim do catch externo
                } // fim do método actionPerformed
            } ); // fim do método addActionListener e da classe interna

            final TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>( tableModel );
            resultTable.setRowSorter( sorter );
            setSize( 500, 250 );
            setVisible( true );

            /** * Cria ouvinte para botaoFiltro */
            botaoFiltro.addActionListener( new ActionListener() {
                @Override
                public void actionPerformed( ActionEvent e ) {
                    /** * Passa o texto do filtro para o ouvinte */
                    String texto = textoFiltro.getText();

                    if( texto.length() == 0 )
                        sorter.setRowFilter( null );
                    else {
                        try {
                            sorter.setRowFilter( RowFilter.regexFilter( texto ) );
                        }
                        catch( PatternSyntaxException psE ) {
                            JOptionPane.showMessageDialog( null, "Regex Pattern Ruim" , "Regex Pattern Ruim", JOptionPane.ERROR_MESSAGE );
                        }
                    } // fim do else
                } // fim do método actionPerformed
            } ); // fim do método addActionListener e da classe interna
        }
        catch( SQLException sqlE ) {
            JOptionPane.showMessageDialog( null, sqlE.getMessage() , "Erro no Banco de Dados", JOptionPane.ERROR_MESSAGE );

            /** * Assegura que a conexão d ebanco de dados está fechada */
            tableModel.disconectaDoDB();

            System.exit( 1 ); // termina o aplicativo
        }

        /** * Dispõe da janela quando o usuário fecha o apicativo( isso sobresvreve o padrão de HIDE_ON_CLOSE */
        setDefaultCloseOperation( DISPOSE_ON_CLOSE );

        /** * Assegura que a conexão de banco de dados é fechada quando usuário fecha o apicativo */
        addWindowListener( new WindowAdapter() {
            /** * Desconecta-se do banco de dados e sai quando a janela for fechada */
            public void windowClosed( WindowEvent event ) {
                tableModel.disconectaDoDB();
                System.exit( 0 );
            } // fim do método windowClosed
        } ); // fim do método addWindowListener e da classe interna
    } // fim do construtor

    public static void main( String[] args ) {
        new ExibeResultadoConsultas();
    }
}

10 Respostas

drsmachado

Sim, ensina de uma maneira melhor que usando DefaultTableModel.

E

Quer saber uma coisa?
É um segredo que as pessoas têm dificuldade de aceitar.
Muitas vezes, quando você aprende uma coisa, deve estar preparado para esquecer o modo que aprendeu, e reaprender sobre ela continuamente - por exemplo, não é porque você aprendeu a fazer divisões de um jeito, na primeira série do primeiro grau, que você tem de sempre usar o mesmo método a vida inteira.
Portanto, o Deitel ensina você a criar um TableModel a partir de um ResultSet (o que é muito bom e muito prático) em vez de fazê-lo de um array bidimensional de objetos (argh).
Isso está OK, e aliás é um bom jeito de aprender - é uma boa prática.
Mas não quer dizer que você tenha de fazer isso igualzinho desse jeitinho que está no livro a vida inteira, sem aprender mais nada diferente - afinal de contas, você muda e a tecnologia muda, e o que o Deitel disse não é a palavra de Deus; você pode aprender um modo melhor ou mais eficiente depois.

drsmachado

entanglement:
Quer saber uma coisa?
É um segredo que as pessoas têm dificuldade de aceitar.
Muitas vezes, quando você aprende uma coisa, deve estar preparado para esquecer o modo que aprendeu, e reaprender sobre ela continuamente - por exemplo, não é porque você aprendeu a fazer divisões de um jeito, na primeira série do primeiro grau, que você tem de sempre usar o mesmo método a vida inteira.
Portanto, o Deitel ensina você a criar um TableModel a partir de um ResultSet em vez de fazê-lo de um array bidimensional de objetos (argh).
Isso está OK, e aliás é um bom jeito de aprender - é uma boa prática.
Mas não quer dizer que você tenha de fazer isso igualzinho desse jeitinho que está no livro a vida inteira, sem aprender mais nada diferente - afinal de contas, você muda e a tecnologia muda, e o que o Deitel disse não é a palavra de Deus; você pode aprender um modo melhor ou mais eficiente depois.

Ontem eu lia uns artigos sobre Diógenes de Sinope (não é Sinop, no MT), filósofo grego que ficou conhecido por andar com uma lanterna acesa, durante o dia e acabei lendo mais sobre o seu mestre, Sócrates (não o jogador).
O fundamento do princípo filosófico de Sócrates, a maiêutica, baseia o que o entanglement falou. Basicamente, precisamos aprender a abandonar as certezas e questionar nossas verdades absolutas, para nos permitirmos aprender coisas novas.
Isso resume toda a construção de qualquer processo científico.
Se Ptolomeu não questionasse os modelos estabelecidos, não teria formulado a teoria do geocentrismo. Se Galileu não o questionasse, não teria sugerido o heliocentrismo.
Se os caras do google se contentassem em buscar pelo altavista, não teríamos android.
Se o Steve não questionasse o design e a criatividade, não teríamos PCs.
E assim por diante.
Claro que temos de ter bom senso. Questionar ordens expressas dos superiores pdoe ser tolice, se você não tem uma boa reserva de grana ou quer manter o emprego.
Agora, nem receita de bolo se segue à risca, sempre. Quanto mais programação.

lucas_guj

Obrigado pela resposta.

Eu entendi o que você quis dizer. O livro te ensina, mas não quer dizer que você tem que programar exatamente como quem fez os códigos do livro a vida toda, posso ter meu próprio jeito, desde que seja certo, bom etc. Agora, você tem alguma dica a dar sobre isso? Algum jeito que você argumente ser melhor para eu estar testando? Igual ao outro tópico que você falou do MIG Layout. Eu via as pessoas falando do GridBagLayout que era melhor que os outros, e sempre falavam para usar ele ou entao esse tal MIG Layout que eram os mais flexiveis. Então o que eu vou fazer, vou estudar esse MIG também. Gostaria de ver a opinião do ViniGodoy também.

lucas_guj

Também concordo com o que disse. Ótimo post dos dois. Agora, poderiam me ajudar na dúvida da implementação dos métodos que eu coloquei em vermelho no primeiro post, se estiverem afim é claro.

F

lucas_guj:
[b][color=red]
Além dessa dúvida, tenho outra, que é, se na interface temos que sobrescrever todos os métodos nas implementações dela, menos naquelas que são Adapter que a gente pode escolher o que implementar, mesmo com a explicação do livro, eu não entendi porque ele não sobrescreveu os métodos: isCellEditable e setValueAt e addTableModelListener e removeTableModelListener. Alguém poderia me explicar com OUTRAS PALAVRAS( Não explique exatamente igual ao livro senão vou continuar a ficar sem entender por favor ). Obrigado[/color]

Antes que a galera esqueça as tuas perguntas ( :stuck_out_tongue: ), vou tentar respondê-las.

O método isCellEditable(int rowIndex, int columnIndex) define quais células são editáveis (ou seja, quais você pode editar direto na tabela, como no EXCEL).
O método setValueAt(Object aValue, int rowIndex, int columnIndex) define como você “edita as células editáveis”.
Os métodos addTableModelListener(TableModelListener l) e removeTableModelListener(TableModelListener l) definem um “ouvinte”, que vai alertar de atualizações (inserir, alterar, remover) nos dados do modelo, e o removem, respectivamente.

Ele não sobrescreveu os dois primeiros métodos porque no exemplo não será possível editar diretamente na tabela.
Não entendi muito bem o motivo dele não ter sobrescrito os dois últimos métodos, mas acredito que ele tenha criado um método que faça a mesma coisa fora do TableModel.

Pra mais informações:
http://docs.oracle.com/javase/1.4.2/docs/api/javax/swing/table/AbstractTableModel.html

E um tutorial:
http://docs.oracle.com/javase/tutorial/uiswing/components/table.html#data

lucas_guj

Obrigado pela resposta fasts. Mas olha só, se eu fico sem implementar esses métodos não era para dar um erro de compilação? Coloquei as classes de exemplo no primeiro post.

E

Uma classe concreta , se ela declara implementar uma interface, deve implementar os métodos “que faltam”.

Uma classe abstrata pode deixar de implementar alguns métodos de uma interface. A classe concreta que herdar (direta ou indiretamente) dessa classe abstrata deve implementar os métodos que faltam.

Pelo que imagino, sua classe ResultSetTableModel é uma classe concreta, não?

rmendes08

Não, porque a classe AbstractTableModel já fornece uma implementação padrão desses métodos.

lucas_guj

Não, porque a classe AbstractTableModel já fornece uma implementação padrão desses métodos.

AAAAAAAAAAAAAAAAAAAAAAAAAAAAGORA entendi…AHDUAOADHADHIAHIDHAIHDIah. Obrigado.

Criado 22 de junho de 2012
Ultima resposta 22 de jun. de 2012
Respostas 10
Participantes 5