本文介紹了如何將JDBC Swing Worker與連接池一起使用(理想情況下,同時將SQL和應用程序邏輯分開)?的處理方法,對大家解決問題具有一定的參考價值,需要的朋友們下面隨著小編來一起學習吧!
問題描述
我有一個帶有Swing圖形用戶界面的Java應用程序,它使用Swing工作器從數據庫(例如,SQLite或MySQL)中提取數據來填充JTable。Swing工作器使用JDBC,一次將多個行塊放入表中。
為此,我使code found here適應了我的目的。該代碼包含一個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
類中,以便編寫更清晰的分離代碼。但是看起來Worker的publish
方法必須嵌套在結果集的try
-with-resource塊中,因為如果該集不在塊中,它就已經關閉了。如何將這兩個任務分離到單獨的類/方法中,而不會使連接永遠處于活動狀態、不會丟失連接跟蹤、不會混淆SQL和圖形用戶界面/表模型代碼?是否有辦法在返回的
ResultSet
對象被銷毀或已到達while
循環結束時自動關閉我在Sql
類中創建的連接?
推薦答案
在看到trashgod的comment后,我提出了一個解決方案,盡可能地將這兩個問題分開,并在隨后關閉連接。盡管知道它并不完美,但我仍將其作為答案發布,希望有人能提出更好的方案。
在我的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輔助進程可以在try
-with-resource塊中使用它,并且在完成后它將自動關閉連接、語句和結果集。Swing工作器按如下方式處理結果:
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;
}
由于AutoCloseable
接口,doInBackground
函數在處理結果集后關閉連接、語句和結果集。與我之前的版本相比,性能似乎沒有明顯的差異,而且分離到Sql
類和圖形用戶界面/模型關注點更嚴格,這使得每個主題的專家都更易于維護。
但是,這仍然有一些缺點:代碼要長得多。如果其他人開始使用getResultSet
方法,他們可能沒有意識到連接仍然需要關閉,可能無法使用帶資源的try
或手動調用close
。而且仍然沒有完美的關注點分離,因為Swing工作者仍然必須關閉連接并處理結果集(即JDBC代碼)。但我看不出如何才能實現更好的分離(歡迎發表評論)。至少在這段代碼中,我將所有的SQL和一些JDBC內容放在Sql
類中,即使有一些開銷。
我猜作為替代方案,我可以將整個doInBackground
函數移到單獨的Sql
類中,并在Swing工作器中引用它來完成工作。但我不知道如何告訴函數,然后從哪里獲取publish
方法。
這篇關于如何將JDBC Swing Worker與連接池一起使用(理想情況下,同時將SQL和應用程序邏輯分開)?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,