在 AbstractTableModel 中使用 ResultSet 后关闭 ResultSet 后不允许操作

Operation not allowed after ResultSet closed after using ResultSet in AbstractTableModel

我正在尝试创建一个框架来显示我数据库中的所有电影名称,并且它们 select 在框架中占一行以执行另一个查询。

我有一个结果集 (rs),它接收对 select 电影名称的查询执行结果。之后,我在 SQLTableModel 中使用 rs 来创建 JTable 并显示在我的框架中。 table 显示我数据库中所有电影的名称。一切都很好。

但是,当我 select 一行时,我使用相同的 rs 接收另一个查询,该查询在我的数据库中 select 我拥有的电影的所有信息 select在我的 JTable 中编辑,我得到了一些错误。

首先,我在 JTable 中 select 编辑的行丢失了值(电影的名称)。但是在其他行中没有任何反应。 其次,我的控制台在函数 "getValueAt" 中向我显示了一个来自 SQLTableModel 的错误,表示 "Erro: #0 Operation not allowed after ResultSet closed".

我在 Whosebug 中搜索了一些解决方案,但没有成功。

我做了一些测试。如果我的 ResultSet (rs) 在我在 SQLTableModel 中使用它之前收到多个查询,则一切正常。但是在 SQLTableModel 中使用它之后,如果我再次尝试使用它,就会出现我提到的错误。

FrameCliente.java

public class FrameCliente {

    JFrame frameCliente;
    JTable table;

    BancoDeDados bd;
    ResultSet rs;

    public FrameCliente() {
        bd = new BancoDeDados();
        frameCliente = new JFrame();
        table = new JTable();
        rs = bd.listar("SELECT fil_nome Nome FROM filme");
        table.setModel(new SQLTableModel(rs));

        table.addMouseListener(new MouseAdapter() {
            public void mousePressed(MouseEvent e) {
                if(table.getSelectedRowCount() > 0) {
                    rs = bd.listar(" SELECT * FROM filme WHERE fil_nome = '" + table.getValueAt(table.getSelectedRow(), 0) + "'");  
                }
            }
        });

        frameCliente.add(new JScrollPane(table));
        frameCliente.setTitle("Filmes");
        frameCliente.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frameCliente.setSize(800, 300);
        frameCliente.setLocationRelativeTo(null);
        frameCliente.setVisible(true);
    }

}

SQLTableModel.java

public class SQLTableModel extends AbstractTableModel {

    private ResultSet rs;
    private ResultSetMetaData rsmd;
    private int numberOfRows=0;

    public SQLTableModel(ResultSet rs){
        this.rs = rs;
        try { //Todos os métodos são privado e isso gera exceção.
            rsmd = this.rs.getMetaData();
            //Navegar para encontrar a ultima linha, para saber quantas linhas a tabela possui.
            this.rs.last();     // Pega a ultima linha;
            numberOfRows = rs.getRow(); //Devolve a linha, no caso, a ultima.
            this.rs.beforeFirst(); //Retorna para a primeira linha.
        }catch(SQLException sqle){
            System.out.printf("Erro: #%d [%s]\n", 
                    sqle.getErrorCode(), sqle.getMessage());
        }

    }


    @Override
    public int getRowCount() {
        // TODO Auto-generated method stub
        return numberOfRows;
    }

    @Override
    public int getColumnCount() {
        // TODO Auto-generated method stub
        try { 
            return rsmd.getColumnCount();
        }catch(SQLException sqle){
            System.out.printf("Erro: #%d [%s]\n", 
                    sqle.getErrorCode(), sqle.getMessage());
            return 0; // Se der exceção não retorna nenhuma coluna;
        }
    }

    @Override
    public String getColumnName(int column) {
        String ColumnName = "";
        try { 
            ColumnName = rsmd.getColumnLabel(column+1);
        }catch(SQLException sqle){
            System.out.printf("Erro: #%d [%s]\n", 
                    sqle.getErrorCode(), sqle.getMessage());
        }
        return ColumnName;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        // TODO Auto-generated method stub
        Object dado = null;
        try { 
            rs.absolute(rowIndex+1);
            dado = rs.getObject(columnIndex+1);
        }catch(SQLException sqle){
            System.out.printf("Erro: #%d [%s]\n", 
                    sqle.getErrorCode(), sqle.getMessage());
        }
        return dado;
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        String className;
        try { 
            className = rsmd.getColumnClassName(columnIndex+1);
            return Class.forName(className);
        }catch(SQLException sqle){
            System.out.printf("Erro: #%d [%s]\n", 
                    sqle.getErrorCode(), sqle.getMessage());
        } catch (ClassNotFoundException cnfe) {
            System.out.printf("Erro: %s\n", cnfe.getMessage());
        }
        return null;
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return false;
    }

    public void atualizarTabela() {
        fireTableDataChanged();
    }


}

BancoDeDados

public class BancoDeDados {
    private Connection con = null;
    private Statement sta = null;    //faz consultas
    private ResultSet rset = null;  //armazenas as info pra trabalhar com elas depois - resultado do select

    public BancoDeDados() {
        this.conectar();
    }


    public void conectar() {
        String servidor = "jdbc:mysql://localhost:3306/cinema?useSSL=true&useTimezone=true&serverTimezone=UTC";
        String usuario = "root";
        String senha = "coreduo2";
        String driver = "com.mysql.cj.jdbc.Driver";

        try {

            //Class.forName(driver);
            this.con = DriverManager.getConnection(servidor, usuario, senha);
            this.sta = this.con.createStatement();
        }
        catch(Exception e)
        {
            System.out.println("Error " + e.getMessage());
        }
    }

    public boolean estaConectado() {
        if (con != null) {
            return true;
        }
        else {
            return false;
        }
    }

    public void desconecte() {
        try {
            con.close();
            System.out.println("Conexao com banco encerrado");
        }
        catch(SQLException e) {
            System.out.println("Erro: " +  e.getMessage());
        }
    }

    public ResultSet listar(String query) {
        try {
            return sta.executeQuery(query);
            //this.sta = this.con.createStatement();
            /*while (this.rset.next()) {
                System.out.println("ID: " + rset.getString("fil_codigo") + " Nome: " + rset.getString("fil_nome") + " Duração: " + rset.getString("fil_duracao"));
            }*/

        }
        catch(SQLException e) {
            System.out.println("Erro "+ e.getMessage());
            return null;
        }   
    }

    public void inserir(String query) {
        try {
            this.sta.executeUpdate(query);
        }
        catch(SQLException e) {
            System.out.println("Erro"+ e.getMessage());
        }

    }

    public void excluir(String query) {
        try {
            this.sta.executeUpdate(query);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public void editar(String query) {
        try {
            this.sta.executeUpdate(query);
        }
        catch (SQLException e) {
            e.printStackTrace();
        }
    }

}

始终使用ArrayList或Map等内存结构来缓存从DB获取的结果。由于与数据库的连接很昂贵,因此保持此连接也很昂贵。所以尽可能快地调用 DB,尽可能短。一旦你在内存中获得数据,你就可以随意操作。