I have a Java application with a Swing GUI that uses a swing worker to pull data out of a database (e.g., SQLite or MySQL) to fill a JTable. The swing worker uses JDBC and puts chunks of rows at a time into the table.
To do this, I adapted code found here to my purposes. The code contains a JDBCModel
class, which extends an AbstractTableModel
to store the data for the JTable. The code also contains a JDBCWorker
class, which extends SwingWorker
, to access the database and add the rows to the table model.
The constructor of the JDBCModel
starts out by establishing a connection, executing a query, and creating a ResultSet
:
try {
Statement s = conn.createStatement();
rs = s.executeQuery(query);
meta = rs.getMetaData();
JDBCWorker worker = new JDBCWorker();
jpb.setIndeterminate(true);
worker.execute();
} catch (SQLException e) {
e.printStackTrace(System.err);
}
The JDBCWorker
then simply iterates through the result set and creates rows for the table. The JDBCWorker
is defined as a private class within the JDBCModel
. This is how the JDBCWorker
iterates through the result set:
protected List<Row> doInBackground() {
try {
while (rs.next()) {
Row r = new Row();
// omitting some additional computations for brevity...
publish(r);
}
} catch (SQLException e) {
e.printStackTrace(System.err);
}
return data;
}
In my own code, I use connection pooling instead of keeping the same connection alive. I have modified the code as follows in order to be able to request a new connection from the data source I defined in a separate Sql
class. I also moved the private JDBCWorker
class out of the JDBCModel
class. Each time the table needs to be re-populated, a new worker is created. This is what the worker now looks like with the connection pooling; it uses try
-with-resources to close the connection, statement, and result set automatically after use:
protected List<Row> doInBackground() {
try (Connection conn = sql.getDataSource().getConnection();
PreparedStatement statement = conn.prepareStatement(query)) {
ResultSet rs = statement.executeQuery();
while (rs.next()) {
Row r = new Row();
// omitting some additional computations for brevity...
publish(r);
}
} catch (SQLException e) {
e.printStackTrace(System.err);
}
return null;
}
It seems to work fine, but I am now concerned about properly separating my code. I have the following three interrelated questions:
- Is this reasonably efficient, or is there a strong recommendation to keep a connection alive for hours in the background just for the worker, as is done in the original code?
- Ideally I would like to move all the SQL- and JDBC-related code into my
Sql
class for writing more cleanly separated code. But it looks like thepublish
method of the worker must be nested inside thetry
-with-resources block of the result set because the set would already be closed if it wasn't inside the block. How do I separate the two tasks into separate classes/methods without keeping the connection alive forever, losing track of the connection, or mixing SQL and GUI/table model code? - Is there a way to auto-close the connection I create in the
Sql
class as soon as the returnedResultSet
object is destroyed or has reached the end of thewhile
loop?