java - 如何将 JDBC Swing Worker 与连接池一起使用(最好是在分离 SQL 和应用程序逻辑时)?
问题描述
我有一个带有 Swing GUI 的 Java 应用程序,它使用 Swing 工作者从数据库(例如 SQLite 或 MySQL)中提取数据以填充 JTable。swing worker 使用 JDBC 并一次将大块的行放入表中。
为此,我根据我的目的调整了此处找到的代码。该代码包含一个JDBCModel
类,该类扩展了一个AbstractTableModel
以存储 JTable 的数据。该代码还包含一个JDBCWorker
扩展类,SwingWorker
用于访问数据库并将行添加到表模型中。
的构造函数JDBCModel
首先建立一个连接,执行一个查询,然后创建一个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);
}
然后JDBCWorker
简单地遍历结果集并为表创建行。被JDBCWorker
定义为 中的私有类JDBCModel
。这是JDBCWorker
遍历结果集的方式:
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;
}
在我自己的代码中,我使用连接池而不是保持同一个连接处于活动状态。我已将代码修改如下,以便能够从我在单独的Sql
类中定义的数据源请求新连接。我还把私人JDBCWorker
班级移出了JDBCModel
班级。每次需要重新填充表时,都会创建一个新的工作人员。这就是连接池中的worker现在的样子;它使用try
-with-resources 在使用后自动关闭连接、语句和结果集:
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;
}
它似乎工作正常,但我现在担心正确分离我的代码。我有以下三个相互关联的问题:
- 这是否相当有效,或者是否强烈建议像在原始代码中所做的那样,只为工作人员在后台保持连接数小时?
- 理想情况下,我想将所有与 SQL 和 JDBC 相关的代码移到我的
Sql
班级中,以便编写更清晰分离的代码。但看起来publish
工作人员的方法必须嵌套在try
结果集的 -with-resources 块内,因为如果它不在块内,该集将已经关闭。如何将这两个任务分成单独的类/方法,而不会使连接永远保持活动状态、丢失连接跟踪或混合 SQL 和 GUI/表模型代码? - 有没有办法在返回的对象被销毁或到达循环末尾时自动关闭我在
Sql
类中创建的连接?ResultSet
while
解决方案
看到垃圾神评论后,我想出了一个解决方案,尽可能将这两个问题分开,并根据有用的评论,稍后关闭连接。尽管知道它并不完美,但我仍将其发布为答案,希望有人能想出更好的东西。
在我的Sql
课程中,我创建了一个方法来执行查询并将结果集与语句和连接一起保存并返回所有内容:
public SqlStuff getSqlStuff() {
String query = "SELECT * FROM MyTable;";
ResultSet rs = null;
Connection conn = null;
PreparedStatement statement = null;
try {
conn = getDataSource().getConnection();
statement = conn.prepareStatement(query);
rs = statement.executeQuery();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
// nothing to close here because the result set is still needed
}
SqlStuff s = new SqlStuff(rs, tableStatement, conn);
return s;
}
一旦数据被提取,连接、语句和结果集需要由 Swing Worker 关闭,因此它们还不能关闭,并且都需要存储在一个SqlStuff
对象中并为此目的返回。这个容器对象定义如下:
public class SqlStuff implements AutoCloseable {
ResultSet rs;
PreparedStatement ps;
Connection c;
public SqlStuff(ResultSet rs, PreparedStatement ps, Connection c) {
this.rs = rs;
this.ps = ps;
this.c = c;
}
public void close() {
rs.close();
ps.close();
c.close();
}
public ResultSet getResultSet() {
return rs;
}
}
类SqlStuff
实现AutoCloseable
和close
方法。因此,swing worker 可以在try
-with-resources 块中使用它,完成后它会自动关闭连接、语句和结果集。swing worker 处理结果如下:
protected List<TableDocument> doInBackground() {
try (SqlStuff s = sql.getSqlStuff();
ResultSet rs = s.getResultSet()) {
while (rs.next()) {
Row r = new Row(rs.getString(1));
// omitting additional getString and getInt calls for brevity
publish(r);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
由于接口,该doInBackground
函数在结果集处理完毕后关闭连接、语句和结果集AutoCloseable
。与我之前的版本相比,似乎没有明显的性能差异,并且对Sql
类和 GUI/模型关注点的分离更加严格,这使得每个主题的专家都更易于维护。
但是,这仍然有一些缺点:代码要长得多。如果其他人开始使用该getResultSet
方法,他们可能没有意识到连接仍然需要关闭,并且可能无法使用try
-with-resources 或close
手动调用。并且仍然没有完美的关注点分离,因为 Swing Worker 仍然必须关闭连接并通过结果集工作,这是 JDBC 代码。但我看不到如何实现更好的分离(欢迎评论)。至少有了这段代码,我在Sql
课堂上拥有了所有的 SQL 和一些 JDBC 的东西,即使有一些开销。
我想作为替代方案,我可以将整个doInBackground
函数移到单独的Sql
类中,并在 swing worker 中引用它来完成工作。但我不知道如何告诉函数然后publish
从哪里获取方法。
推荐阅读
- python - 用布雷森汉姆线算法python知道一条线穿过灰度图像的像素值是多少
- java - 我可以直接调用 Dropwizard/Jersey 异常映射吗?
- python - AWS lambda 函数实现中的 Concurrent.futures
- python - 如何使用 tensorflow.contrib 重用 BERT 模型
- reactjs - 找不到 iPhone X 模拟器
- python - 从列中的条目创建列表
- macos - OS X 是否真的支持 ASLR
- ionic3 - Ionic 3 ion-option 监听取消选中/取消选择事件?
- mysql - 现在将日期与 MySQL [Angular] 中的日期进行比较的问题
- laravel - 卡在多对多数据呼叫单分配