ResultSetMetaData NullPointerException

Olá pessoal do GUJ,

estou treinando um pouco de conexão com banco de dados e interface gráfica. Porém me deparei com um obstáculo: tenho uma interface gráfica com 1 text field (tfQuery) onde insiro o comando SQL, 1 button (btExec) que executa o comando SQL do text field, e uma table (table) onde quero que apareça o resultado de um SELECT.

Quero que a table seja dinâmica. Exemplo:

SELECT * FROM Aluno <- 3 colunas devem aparecer
SELECT * FROM Livro <- 7 colunas devem aparecer

Mas, quando mando executar o código que já tenho pronto (código que eu fiz pegando exemplo do livro “JAVA how to program” do Deitel), ele dá NullPointerException aqui:

public int getColumnCount() {
        try {
            return(mdt.getColumnCount()); //exatamente aqui a exceção!
        }catch(SQLException e) {
            JOptionPane.showMessageDialog(null, "Erro ao retornar o número de colunas!!", "Erro!", JOptionPane.PLAIN_MESSAGE);
        }
        return(0);
}

Não tenho a menor noção do que pode estar faltando, mas gostaria de uma ajudinha. Se precisarem de mais informações, é só pedir!
Obrigado.

mdt está null.
De onde ele vem?

Bom, vamos lá.

private void btExecActionPerformed(java.awt.event.ActionEvent evt) {                                       
        Table table = new Table();
        table.setQuery(taQuery.getText()); //seta o comando
}
public void setQuery(String Query) {
        Conexao con = new Conexao();
        con.getConexao();
        modelo.executeQuery(Query); //executa o comando
        con.closeConexao();
}

public void executeQuery(String Query) {
        try {    
            con.rowSet.setCommand(Query); //seta o comando no rowset
            con.rowSet.execute();
            mdt = con.rowSet.getMetaData(); //aqui ele pega o metadata
            con.rowSet.last();
            nor = con.rowSet.getRow();
            fireTableStructureChanged();
        }catch(SQLException e) {
            JOptionPane.showMessageDialog(null, "Erro ao executar comando!!", "Erro", JOptionPane.PLAIN_MESSAGE);
        }
}

Só lembrando que o código é uma tentativa de adaptação (ERRO!).

Mas isso só acontece quando eu tento colocar na table, quando fazia aparecer no System.out ele funcionava.

O problema desse código postado é que o Jack Estripador passou antes e só estou vendo partes.
1 - Qual a relação entre o método public int getColumnCount() { try { return(mdt.getColumnCount()); //exatamente aqui a exceção! }catch(SQLException e) { JOptionPane.showMessageDialog(null, "Erro ao retornar o número de colunas!!", "Erro!", JOptionPane.PLAIN_MESSAGE); } return(0); }
e o método

public void executeQuery(String Query) { try { con.rowSet.setCommand(Query); //seta o comando no rowset con.rowSet.execute(); mdt = con.rowSet.getMetaData(); //aqui ele pega o metadata con.rowSet.last(); nor = con.rowSet.getRow(); fireTableStructureChanged(); }catch(SQLException e) { JOptionPane.showMessageDialog(null, "Erro ao executar comando!!", "Erro", JOptionPane.PLAIN_MESSAGE); } } ?
Não vejo uma ligação entre eles, nem sei em que ordem são executados, nem sei se estão na mesma classe…

Exatamente esse o ponto. Também não vejo ligação entre eles. Porém, julgo que quando eu executo o código e ele tenta colocar os dados na table ele passa por getColumnCount e gera a exceção.
Perdoe-me a falta de código, mas coloquei a parte que imaginei ser importante.

Pacote de conexão - Conecta e desconecta com o banco.

package Conexao;

import com.sun.rowset.JdbcRowSetImpl;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.rowset.JdbcRowSet;
import javax.swing.JOptionPane;

public class Conexao {
    public JdbcRowSet rowSet = new JdbcRowSetImpl();

    Properties properties = new Properties();
    FileInputStream arquivoDePropriedades = null;
    
    public Conexao() {
        
    }
    
    public void getConexao() {
        try {
            arquivoDePropriedades = new FileInputStream("conexao.properties");
            properties.load(arquivoDePropriedades);
            try {
                rowSet.setUrl(properties.getProperty("jdbc.url"));
                rowSet.setUsername(properties.getProperty("jdbc.usuario"));
                rowSet.setPassword(properties.getProperty("jdbc.senha"));
            }catch(SQLException e) {
                JOptionPane.showMessageDialog(null,"Erro de execução em comandos SQL!");
            }
        }catch(FileNotFoundException e) {
            JOptionPane.showMessageDialog(null,"O arquivo de properiedades não foi encontrado!!");
        }catch(IOException e) {
            JOptionPane.showMessageDialog(null,"Erro ao carregar arquivo!!");
        }
    }

    public void closeConexao() {
        try {
            rowSet.close();
        }catch(Exception e) {
            JOptionPane.showMessageDialog(null,"Erro ao fechar objetos de controle de SQL!");
        }
    }
}

Pacote de Visualização - Cria a table e seta o comando SQL.

package Layout;

import Conexao.Conexao;
import javax.swing.JTable;

public class Table {
    ResultSetTableModel modelo = new ResultSetTableModel();
    JTable table;
    
    public Table() {
        table = new JTable(modelo);
    }
    
    public void setQuery(String Query) {
        Conexao con = new Conexao();
        con.getConexao();
        modelo.executeQuery(Query);
        con.closeConexao();
    }
}

Pacote de visualização - cria o tableModel da table.

package Layout;

import Conexao.Conexao;
import java.sql.*;
import javax.swing.JOptionPane;
import javax.swing.table.AbstractTableModel;

public class ResultSetTableModel extends AbstractTableModel {
    ResultSetMetaData mdt;
    Conexao con = new Conexao();
    private int nor;
    
    @Override
    public int getRowCount() {
        return(nor);
    }

    @Override
    public int getColumnCount() {
        try {
            return(mdt.getColumnCount());
        }catch(SQLException e) {
            JOptionPane.showMessageDialog(null, "Erro ao retornar o número de colunas!!", "Erro!", JOptionPane.PLAIN_MESSAGE);
        }
        return(0);
    }

    @Override
    public Object getValueAt(int row, int column) {
        try {
            con.rowSet.absolute(row+1);
            return(con.rowSet.getObject(column+1));
        }catch(SQLException e) {
            JOptionPane.showMessageDialog(null, "Erro ao receber valor nas linha específica!!", "Erro!", JOptionPane.PLAIN_MESSAGE);
        }
        return("");
    }
    
    public void executeQuery(String Query) {
        try {    
            con.rowSet.setCommand(Query);
            con.rowSet.execute();
            mdt = con.rowSet.getMetaData();
            con.rowSet.last();
            nor = con.rowSet.getRow();
            fireTableStructureChanged();
        }catch(SQLException e) {
            JOptionPane.showMessageDialog(null, "Erro ao executar comando!!", "Erro", JOptionPane.PLAIN_MESSAGE);
        }
    }
    
}

Espero que entenda o código, mas acho que está com muitos erros!

Pra que o resultado do método @Override public int getColumnCount() { try { return(mdt.getColumnCount()); }catch(SQLException e) { JOptionPane.showMessageDialog(null, "Erro ao retornar o número de colunas!!", "Erro!", JOptionPane.PLAIN_MESSAGE); } return(0); } seja válido (ou seja, mdt != null, você precisa ter executado o método public void executeQuery(String Query) { try { con.rowSet.setCommand(Query); con.rowSet.execute(); mdt = con.rowSet.getMetaData(); con.rowSet.last(); nor = con.rowSet.getRow(); fireTableStructureChanged(); }catch(SQLException e) { JOptionPane.showMessageDialog(null, "Erro ao executar comando!!", "Erro", JOptionPane.PLAIN_MESSAGE); } }
Qualquer outra maneira irá apresentar a exceção que originou este tópico.

Outra coisa, a abordagem de colocar consulta SQL na TableModel, no meu entender, é mais que tosca, é ruim, porca e desaconselhável.
Pesquise pelo pattern DAO.

Sim. Realmente, depois de alguns System.out, descobri que o getColumnCount está sendo executado antes do executeQuery.
Agora vou descobrir em que ordem está sendo executado cada método e tentar resolver.

Muito Obrigado!
:roll: