在 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,尽可能短。一旦你在内存中获得数据,你就可以随意操作。
我正在尝试创建一个框架来显示我数据库中的所有电影名称,并且它们 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,尽可能短。一旦你在内存中获得数据,你就可以随意操作。